紙上談兵: 表 (list)


作者:Vamei 出處:http://www.cnblogs.com/vamei 歡迎轉載,也請保留這段聲明。謝謝!

 

表(list)是常見的數據結構。從數學上來說,表是一個有序的元素集合。在C語言的內存中,表儲存為分散的節點(node)。每個節點包含有一個元素,以及一個指向下一個(或者上一個)元素的指針。如下圖所示:

表: 橙色儲存數據,藍色儲存指針

圖中的表中有四個節點。第一個節點是頭節點(head node),這個節點不用於儲存元素,只用於標明表的起始。頭節點可以讓我們方便的插入或者刪除表的第一個元素。整個表中包含有三個元素(5, 2, 15)。每個節點都有一個指針,指向下一個節點。最后一個節點的指針為NULL,我們用“接地”來圖示該指針。

表的功能與數組(array)很類似,數組也是有序的元素集合,但數組在內存中為一段連續內存,而表的每個節點占據的內存可以是離散的。在數組中,我們通過跳過固定的內存長度來尋找某個編號的元素。但在表中,我們必須沿着指針聯系起的長鏈,遍歷查詢元素。此外,數組有固定的大小,表可以根據運行情況插入或者刪除節點,動態的更改大小。表插入節點時需要從進程空間的堆中開辟內存空間,用以儲存節點。刪除節點可以將節點占據的內存歸還給進程空間。

刪除節點, free釋放內存

 

插入節點,malloc開辟內存

 

表有多種變種。上面的表中,指針指向是從前向后的,稱為單向鏈表(linked list)。還有雙向鏈表(double-linked list),即每個節點增加一個指向前面一個元素的指針。以及循環鏈表(tabular list),最后一個元素的指針並不為NULL,而是指向頭節點。不同類型的鏈表有不同的應用場景。

雙向鏈表

 

循環鏈表

 

雙向循環鏈表

 

單向鏈表的C實現

一個數據結構的實現有兩方面: 1. 數據結構的內存表達方式; 2. 定義在該數據結構上的操作。我們這里實現最簡單的單向鏈表。表所支持的操作很靈活多樣,我們這里定義一些最常見的操作。每個操作都寫成一個函數。

/* By Vamei */
#include <stdio.h> #include <stdlib.h>
typedef struct node *LIST;
typedef
struct node *position;


/* node,節點 */ struct node { int element; position next; }; /*
* operations (stereotype)
* 操作
*/
LIST init_list(
void); void delete_list(LIST);
int is_null(LIST);
void insert_node(position, int); void delete_node(LIST, position);
position find_last(LIST); position find_value(LIST,
int); position find_previous(LIST, position);
void print_list(LIST);
/* for testing purpose */
void main() { LIST L; position np; int i; /* elements to be put into the list */ int a[] = {1, 3, 5, 7, 9}; /* initiate a list */ L = init_list(); print_list(L); /* insert nodes. Insert just after head node */ for (i=4; i>=0; i--) { insert_node(L, a[i]); } print_list(L); /* delete first node with value 5 */ np = find_value(L, 5); delete_node(L, np); print_list(L); /* delete list */ delete_list(L); /* initiate a list */ L = init_list(); print_list(L); /* insert nodes. Insert just after head node */ for (i=4; i>=0; i--) { insert_node(L, a[i]); } print_list(L); /* delete list */ delete_list(L); } /* * Traverse the list and print each element
* 打印表
*/ void print_list(LIST L) { position np; if(is_null(L)) { printf("Empty List\n\n"); return; } np = L; while(np->next != NULL) { np = np->next; printf("%p: %d \n", np, np->element); } printf("\n"); } /* * Initialize a linked list. This list has a head node * head node doesn't store valid element value
* 創建表
*/ LIST init_list(void) { LIST L; L = (position) malloc(sizeof(struct node)); L->next = NULL; return L; } /* * Delete all nodes in a list
* 刪除表
*/ void delete_list(LIST L) { position np, next; np = L; do { next = np->next; free(np); np = next; } while(next != NULL); } /* * if a list only has head node, then the list is null.
* 判斷表是否為空
*/ int is_null(LIST L) { return ((L->next)==NULL); } /* * insert a node after position np
* 在np節點之后,插入節點
*/ void insert_node(position np, int value) { position nodeAddr; nodeAddr = (position) malloc(sizeof(struct node)); nodeAddr->element = value; nodeAddr->next = np->next; np->next = nodeAddr; } /* * delete node at position np
* 刪除np節點
*/ void delete_node(LIST L, position np) { position previous, next; next = np->next; previous = find_previous(L, np); if(previous != NULL) { previous->next = next; free(np); } else { printf("Error: np not in the list"); } } /*
 * find the last node of the list
* 尋找表的最后一個節點

 */ position find_last(LIST L) { position np; np
= L; while(np->next != NULL) { np = np->next; } return np; } /* * This function serves for 2 purposes: * 1. find previous node * 2. return NULL if the position isn't in the list
* 尋找npTarget節點前面的節點
*/ position find_previous(LIST L, position npTarget) { position np; np = L; while (np->next != NULL) { if (np->next == npTarget) return np; np = np->next; } return NULL; } /* * find the first node with specific value
* 查詢
*/ position find_value(LIST L, int value) { position np; np = L; while (np->next != NULL) { np = np->next; if (np->element == value) return np; } return NULL; }

 

在main()函數中,我們初始化表,然后插入(1, 3, 5, 7, 9)。又刪除元素5。可以看到,節點零散的分布在內存中。刪除節點操作不會影響其他節點的存儲位置。

我們隨后刪除表,又重新創建表。可以看到,這次表占據內存的位置與第一次不同。

 

下面是main()函數的運行結果。

Empty List

0x154d0b0: 1
0x154d090: 3
0x154d070: 5
0x154d050: 7
0x154d030: 9

0x154d0b0: 1
0x154d090: 3
0x154d050: 7
0x154d030: 9

Empty List

0x154d070: 1
0x154d010: 3
0x154d0b0: 5
0x154d090: 7
0x154d050: 9

 

總結

表: 內存中離散分布的有序節點

插入,刪除節點

 

歡迎繼續閱讀“紙上談兵: 算法與數據結構”系列。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM