Linux C 單向連結串列1
順序儲存的優點:
順序儲存的缺點:
連結串列就是典型的鏈式儲存,將線性表L = (a0,a1,a2,........an-1)中個元素分佈在儲存器的不同儲存塊,成為結點(Node),通過地址或指標建立他們之間的練習,所得到的儲存結構為連結串列結構。表中元素ai的結點形式如下:
其中,結點的data域存放資料元素ai,而next域是一個指標,指向ai的直接後繼a(i+1)所在的結點。於是,線性表L=(a0,a1,......an-1)的結構如圖:
一、節點型別描述:
[cpp] view plain copy- typedef struct node_t
- {
- data_t data; //節點的資料域
- struct node_t *next;//節點的後繼指標域
- }linknode_t,*linklist_t;
也可這樣表示:
[cpp] view plain copy- struct node_t
- {
- data_t data;
- struct node_t *next;
- }
- typedef struct node_t linknode_t;
- typedef struct node_t *linklist_t;
若說明
linknode_t A;
linklist_t p = &A;
則結構變數A為所描述的節點,而指標變數P為指向此型別節點的指標(p的值為節點的地址);
這樣看來 linknode_t linklist_t 的作用是一樣的,那為什麼我們要定義兩個資料型別(同一種)呢?主要為了程式碼的可讀性,我們要求識別符號要望文識義,便於理解;
1、linknode_t *pnode 指向一個節點;
2、linklist_t list 指向一個整體
二、頭結點 head
我們在前篇提到的順序儲存線性表,如何表達一個空表{ },是通過list->last = -1來表現的,所謂的空表就是資料域為NULL,而我們的連結串列有資料域和指標域,我們如何表現空連結串列呢?這時,就引入了頭結點的概念,頭結點和其他節點資料型別一樣,只是資料域為NULL,head->next = NULL,下面我們看一個建立空連結串列的函式,如何利用頭結點來建立一個空連結串列:
[cpp] view plain copy- linklist_t CreateEmptyLinklist()
- {
- linklist_t list;
- list = (linklist_t)malloc(sizeof(linknode_t));
- if (NULL != list) {
- list->next = NULL;
- }
- return list;
- }
只要頭結點,連結串列就還在!
三、連結串列基本運算的相關演算法
連結串列的運算除了上面的建立空連結串列,還有資料的插入,刪除,查詢等函式,連結串列的運算有各種實現方法,如何寫出一個高效的,封裝性較好的函式是我們要考慮的,比如資料插入函式,我們就要儘可能考慮所有能出現的結果,比如:1)如果需插入資料的連結串列是個空表;2)所插入的位置超過了連結串列的長度;如果我們的函式能包含所有能出現的情況,不僅能大大提高我們的開發效率,也會減少程式碼的錯誤率。下面,我們來看看下面的這個連結串列的插入函式的實現:
[cpp] view plain copy- int InsertLinklist(linklist_t list, int at, data_t x)
- {
- linknode_t *node_prev, *node_at, *node_new;
- int pos_at;
- int found = 0;
- if (NULL == list) return -1;
- /* at must >= 0 */
- if (at < 0) return -1;
- /*第一步、分配空間*/
- node_new = malloc(sizeof(linknode_t));
- if (NULL == node_new)
- {
- return -1;
- }
- node_new->data = x; /* assigned value */
- node_new->next = NULL; /*節點如果插入超過連結串列長度的位置,會接到尾節點後面,這樣,node_new成了尾節點,node_new->next = NULL */
- /*第二步、定位*/
- node_prev = list;//跟隨指標,幫助我們更好的定位
- node_at = list->next; //遍歷指標
- pos_at = 0;
- while (NULL != node_at)
- {
- if (pos_at == at)
- {
- found = 1; //找到正確的位置,跳出迴圈
- break;
- }
- /* move to the next pos_at */
- node_prev = node_at; //跟隨指標先跳到遍歷指標的位置
- node_at = node_at->next;//遍歷指標跳到下一個節點的位置
- pos_at++;
- }
- /*第三步、插入*/
- if (found)
- {
- /* found = 1,找到正確的位置,插入 */
- node_new->next = node_at;//插入的節點next指向node_at
- node_prev->next = node_new;//插入節點的前一個節點
- }
- else
- {
- /*若是沒找到正確的位置,即所插入位置超越了連結串列的長度,則接到尾節點的後面,同樣,這樣適用於{ }即空連結串列,這樣我們可以建立一個空連結串列,利用這個函式,實現連結串列的初始化*/
- node_prev->next = node_new;
- }
這個插入函式可利用性就非常高。
下面講一個完整連結串列程式碼貼出:
listlink.h
[cpp] view plain copy- #ifndef _LNK_LIST_H_
- #define _LNK_LIST_H_
- typedef int data_t;
- typedef struct node_t {
- data_t data;
- struct node_t *next;
- } linknode_t, *linklist_t;
- linklist_t CreateEmptyLinklist();
- void DestroyLinklist(linklist_t list);
- void ClearLinklist(linklist_t list);
- int EmptyLinklist(linklist_t list);
- int LengthLinklist(linklist_t list);
- int GetLinklist(linklist_t list, int at, data_t *x);
- int SetLinklist(linklist_t list, int at, data_t x);
- int InsertLinklist(linklist_t list, int at, data_t x);
- int DeleteLinklist(linklist_t list, int at);
- linklist_t ReverseLinklist(linklist_t list);
- #endif /* _LNK_LIST_H_ */
linklist.c
[cpp] view plain copy- #include <stdio.h>
- #include <stdlib.h>
- #include "linklist.h"
- linklist_t CreateEmptyLinklist()
- {
- linklist_t list;
- list = (linklist_t)malloc(sizeof(linknode_t));
- if (NULL != list) {
- list->next = NULL;
- }
- return list;
- }
- void DestroyLinklist(linklist_t list)
- {
- if (NULL != list) {
- ClearLinklist(list);
- free(list);
- }
- }
- void ClearLinklist(linklist_t list)
- {
- linknode_t *node; /* pointer to the node to be removed */
- if (NULL == list) return;
- while (NULL != list->next) {
- node = list->next;
- list->next = node->next;
- free(node);
- }
- return;
- }
- int LengthLinklist(linklist_t list)
- {
- int len = 0;
- linknode_t *node; //iterate pointer
- if (NULL == list) return -1;
- node = list->next; // node points to the first data node
- while (NULL != node) {
- len++;
- node = node->next;
- }
- return len;
- }
- int EmptyLinklist(linklist_t list)
- {
- if (NULL != list) {
- if (NULL == list->next) {
- return 1;
- } else {
- return 0;
- }
- } else {
- return -1;
- }
- }
- int GetLinklist(linklist_t list, int at, data_t *x)
- {
- linknode_t *node; /* used for iteration */
- int pos; /* used for iteration and compare with */
- if (NULL == list) return -1;
- /* at must >= 0 */
- if (at < 0) return -1;
- /* start from the first element */
- node = list->next;
- pos = 0;
- while (NULL != node) {
- if (at == pos) {
- if (NULL != x) {
- *x = node->data;
- }
- return 0;
- }
- /* move to the next */
- node = node->next;
- pos++;
- }
- return -1;
- }
- int SetLinklist(linklist_t list, int at, data_t x)
- {
- linknode_t *node; /* used for iteration */
- int pos;
- int found = 0;
- if (!list) return -1;
- /* at must >= 0 */
- if (at < 0) return -1;
- /* start from the first element */
- node = list->next;
- pos = 0;
- while (NULL != node) {
- if (at == pos) {
- found = 1; /* found the position */
- node->data = x;
- break;
- }
- /* move to the next */
- node = node->next;
- pos++;
- }
- if (1 == found) {
- return 0;
- } else {
- return -1;
- }
- }
- int InsertLinklist(linklist_t list, int at, data_t x)
- {
- /*
- * node_at and pos_at are used to locate the position of node_at.
- * node_prev follows the node_at and always points to previous node
- * of node_at.
- * node_new is used to point to the new node to be inserted.
- */
- linknode_t *node_prev, *node_at, *node_new;
- int pos_at;
- int found = 0;
- if (NULL == list) return -1;
- /* at must >= 0 */
- if (at < 0) return -1;
- node_new = malloc(sizeof(linknode_t));
- if (NULL == node_new) {
- return -1;
- }
- node_new->data = x; /* assigned value */
- node_new->next = NULL;
- node_prev = list;
- node_at = list->next;
- pos_at = 0;
- while (NULL != node_at) {
- if (pos_at == at) {
- /*
- * found the node 'at'
- */
- found = 1;
- break;
- }
- /* move to the next pos_at */
- node_prev = node_at;
- node_at = node_at->next;
- pos_at++;
- }
- if (found) {
- /* insert */
- node_new->next = node_at;
- node_prev->next = node_new;
- } else {
- /*
- * If not found, means the provided "at"
- * exceeds the upper limit of the list, just
- * append the new node to the end of the list.
- */
- node_prev->next = node_new;
- }
- return 0;
- }
- int DeleteLinklist(linklist_t list, int at)
- {
- /*
- * node_at and pos_at are used to locate the position of node_at.
- * node_prev follows the node_at and always points to previous node
- * of node_at.
- */
- linknode_t *node_prev, *node_at;
- int pos_at;
- int found = 0;
- if (!list) return -1;
- /* at must >= 0 */
- if (at < 0) return -1;
- node_prev = list;
- node_at = list->next;
- pos_at = 0;
- while (NULL != node_at) {
- if (pos_at == at) {
- /*
- * found the node 'at'
- */
- found = 1;
- break;
- }
- /* move to the next pos_at */
- node_prev = node_at;
- node_at = node_at->next;
- pos_at++;
- }
- if (found) {
- /* remove */
- node_prev->next = node_at->next;
- free(node_at);
- return 0;
- } else {
- return -1;
- }
- }
- linklist_t ReverseLinklist(linklist_t list)
- {
- linknode_t *node; /* iterator */
- linknode_t *node_prev; /* previous node of iterator */
- linknode_t *node_next; /* next node of iterator,
- * used to backup next of iterator
- */
- if (NULL == list) return NULL;
- node_prev = NULL;
- node = list->next;
- while (NULL != node) {
- /*
- * step1: backup node->next
- * due to the next of iterator will be
- * modified in step2
- */
- node_next = node->next;
- /*
- * when iterator reaches the last node
- * of original list, make the list head
- * point to the last node, so the original
- * last one becomes the first one.
- */
- if (NULL == node_next) {
- list->next = node;
- }
- /*
- * step2: reverse the linkage between nodes
- * make the node pointer to the previous node,
- * not the next node
- */
- node->next = node_prev;
- /*
- * step3: move forward
- */
- node_prev = node;
- node = node_next;
- }
- return list;
- }
main.c
[cpp] view plain copy- #include <stdio.h>
- #include <stdlib.h>
- #include "linklist.h"
- int main()
- {
- int i;
- data_t x;
- linklist_t p;
- p = CreateEmptyLinklist();
- data_t a[10] = {1,3,5,7,9,11,13,15,17,19};
- for(i = 0;i < 10;i++)
- {
- InsertLinklist(p,i,a[i]);
- }
- ReverseLinklist(p);
- printf("The length of the list is:%d\n",LengthLinklist(p));
- GetLinklist(p,4,&x);
- printf("The NO.4 of this list is:%d\n",x);
- SetLinklist(p,4,100);
- GetLinklist(p,4,&x);
- printf("After updating!The No.4 0f this list is:%d\n",x);
- DeleteLinklist(p,4);
- printf("After updating!The length of the list is:%d\n",LengthLinklist(p));
- GetLinklist(p,4,&x);
- printf("After updating!The No.4 0f this list is:%d\n",x);
- ReverseLinklist(p);
- ClearLinklist(p);
- if(EmptyLinklist(p))
- printf("This list is empty!\n");
- DestroyLinklist(p);
- printf("This list is destroyed!\n");
- return 0;
- }
執行結果如下:
[cpp] view plain copy- [email protected]:~/qiang/list/list2$ ./Test
- The length of the list is:10
- The NO.4 of this list is:11
- After updating!The No.4 0f this list is:100
- After updating!The length of the list is:9
- After updating!The No.4 0f this list is:9
- This list is empty!
- This list is destroyed!