Linux C 數據結構 ->單向鏈表<-(~千金散盡還復來~)


 

之前看到一篇單向鏈表的博文,代碼也看着很舒服,於是乎記錄下來,留給自己~,循序漸進,慢慢

延伸到真正的內核鏈表~(敢問路在何方?路在腳下~

 

1. 簡介

鏈表是Linux 內核中最簡單,最普通的數據結構。鏈表是一種存放和操作可變數量元素(常稱為節點)

的數據結構,鏈表和靜態數組的不同之處在於,它所包含的元素都是動態創建並插入鏈表的,在編譯

時不必知道具體需要創建多少個元素,另外也因為鏈表中每個元素的創建時間各不相同,所以它們在

內存中無須占用連續內存區。正是因為元素不連續的存放,所以各個元素需要通過某種方式被鏈接在

一起,於是每個元素都包含一個指向下一個元素的指針,當有元素加入鏈表或從鏈表中刪除元素時,

簡單調整一下節點的指針就可以了。

 

根據它的特性,鏈表可分為:單鏈表雙鏈表單向循環鏈表和雙向循環鏈表,今天總結記錄的就是

最簡單的單鏈表,

 

1.1 節點類型描述

1 typedef struct node_t {
2     data_t data;            /* 節點數據域 */
3     struct node_t *next;    /* 節點的后繼指針域 */
4 }linknode_t, *linklist_t;

另一種寫法

1 struct node_t {
2     data_t data;
3     struct node_t *next;
4 }
5 typedef struct node_t linknode_t;
6 typedef struct node_t* linklist_t;

細看說明:

* linknode_t A;
* linklist_t p = &A;
*
* 結構變量A為所描述的節點,而指針變量p為指向此類型節點的指針(p值為節點的地址)
* 這樣看來 linknode_t 和 linklist_t 的作用是一樣的,那么為什么我們要定義兩個數據類
* 型(同一種)呢?  答曰:主要為了代碼的可讀性,我們要求標識符要望文識義,便於理解
*
* linknode_t *pnode  指向一個節點
* linklist_t list  指向一個整體

 

1.2 頭節點 head (~黃河之水天上來~)

在順序存儲線性表,如何表達一個空表{ },是通過list->last = -1來表現的,所謂的空表就是

數據域為NULL,而鏈表有數據域和指針域,我們如何表現空鏈表呢?這時,就引入了頭結點

的概念,頭結點和其他節點數據類型一樣,只是數據域為NULL,head->next = NULL,下面

我們看一個創建空鏈表的函數,如何利用頭結點來創建一個空鏈表,只要頭節點在,鏈表就還在~

 1 // 創建一個空鏈表
 2 linklist_t 
 3 CreateEmptyLinklist()
 4 {
 5     linklist_t list;
 6     list = (linklist_t)malloc(sizeof(linknode_t));
 7     
 8     if(NULL != list) {
 9         list->next = NULL;
10     }
11     
12     return list;
13 }

 

2. 鏈表基本運算的相關"算法"操作 or 操刀(~烹羊宰牛且為樂,會須一飲三百杯~)

 鏈表的運算除了上面的創建空鏈表,還有數據的插入,刪除,查找等函數,鏈表的運算有各種實現方

法,如何寫出一個高效的,封裝性較好的函數是我們要考慮的,比如數據插入函數,我們就要盡可能

考慮所有能出現的結果,比如:1)如果需插入數據的鏈表是個空表;2)所插入的位置超過了鏈表的

長度;如果我們的函數能包含所有能出現的情況,不僅能大大提高我們的開發效率,也會減少代碼的

錯誤率。下面看一個可用性較強的鏈表插入操作的函數實現~

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 = (linklist_t)malloc(sizeof(linknode_t));
    if (NULL == node_new) {
        return -1;
    }
    node_new->data = x; /* assigned value */
    /*
     *節點如果插入超過鏈表長度的位置,會接到尾節點后面,
     *這樣,node_new成了尾節點,node_new->next = NULL 
    */
    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;
    }
    
    return 0;
}

 

3. 正文開始 Demo(~與君歌一曲,請君為我傾耳聽~)

listlink.h

 1 #ifndef _LIST_LINK_H_
 2 #define _LIST_LINK_H_
 3 
 4 typedef int data_t;
 5 
 6 typedef struct node_t {
 7     data_t data;            /* 節點數據域 */
 8     struct node_t * next;    /* 節點的后繼指針域 */
 9 }linknode_t, * linklist_t;
10 
11 
12 
13 /* 鏈表操作函數*/
14 
15 // 創建一個空鏈表
16 linklist_t CreateEmptyLinklist();
17 
18 // 銷毀鏈表
19 void DestroyLinklist(linklist_t list);
20 
21 // 清空鏈表
22 void ClearLinklist(linklist_t list);
23 
24 // 是否為空鏈表
25 int IsEmptyLinklist(linklist_t list);
26 
27 // 鏈表長度
28 int LengthLinklist(linklist_t list);
29 
30 // 獲去鏈表節點數據
31 int GetLinklist(linklist_t list, int at, data_t * x);
32 
33 // 設置鏈表節點數據
34 int SetLinklist(linklist_t list, int at, data_t x);
35 
36 // 插入節點
37 int InsertLinklist(linklist_t list, int at, data_t x);
38 
39 // 刪除節點
40 int DeleteLinklist(linklist_t list, int at);
41 
42 // 鏈表轉置
43 linklist_t ReverseLinklist(linklist_t list);
44 
45 // 打印鏈表
46 int Display(linklist_t list);
47 
48 
49 #endif // _LIST_LINK_H_

listlink.c

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include "listlink.h"
  4 
  5 // 創建一個空鏈表
  6 linklist_t 
  7 CreateEmptyLinklist()
  8 {
  9     linklist_t list;
 10     list = (linklist_t)malloc(sizeof(linknode_t));
 11     
 12     if(NULL != list) {
 13         list->next = NULL;
 14     }
 15     
 16     return list;
 17 }
 18 
 19 // 銷毀鏈表
 20 void 
 21 DestroyLinklist(linklist_t list)
 22 {
 23     if(NULL != list) {
 24         ClearLinklist(list);
 25         free(list);
 26     }   
 27 }
 28 
 29 // 清空鏈表
 30 void 
 31 ClearLinklist(linklist_t list)
 32 {
 33     linknode_t * node; /* pointer to the node to be remove */
 34     if(NULL == list) return;
 35     
 36     while(NULL != list->next) {
 37         node = list->next;
 38         list->next = node->next; //此時node->next是第二node節點元素依次往后
 39         free(node);
 40     }
 41     return;
 42 }
 43 
 44 // 是否為空鏈表
 45 int 
 46 IsEmptyLinklist(linklist_t list)
 47 {
 48     if(NULL != list) {
 49         if(NULL == list->next)  // 只有頭節點
 50             return 1; // 返回1,是個空鏈表
 51         else 
 52             return 0; // 返回0,鏈表非空
 53         
 54     } else 
 55         return -1;    //  返回-1, 錯誤的類型
 56 }
 57 
 58 // 鏈表長度
 59 int 
 60 LengthLinklist(linklist_t list)
 61 {
 62     int len = 0;
 63     linknode_t * node; // 遍歷指針
 64     
 65     if(NULL == list) return -1;
 66     
 67     node = list->next; // node指針指向第一個節點
 68     while(NULL != node) {
 69         len++;
 70         node = node->next;
 71     }
 72     
 73     return len;
 74 }
 75 
 76 // 獲去一個鏈表指定節點數據域的數據值
 77 int 
 78 GetLinklist(linklist_t list, int at, data_t * x)
 79 {
 80     linknode_t *node;   // 遍歷節點的指針
 81     int pos;            // 用於遍歷比較
 82     
 83     if(NULL == list) return -1;
 84     /*at 必須要 >= 0*/
 85     if(at < 0) return -1;
 86     
 87     /* 從第一個元素開始 */
 88     node = list->next; // node指針指向一個元素
 89     pos = 0;
 90     while(NULL != node) {
 91         if(at == pos) {
 92             if(NULL != x) 
 93                 *x = node->data;
 94             return 0;
 95         }
 96         // 下一個元素
 97         node = node->next;
 98         pos++;
 99     }
100     return -1;
101 }
102 
103 // 設置一個指定鏈表節點的數據域值
104 int 
105 SetLinklist(linklist_t list, int at, data_t x)
106 {
107     linknode_t * node; // 遍歷鏈表
108     int pos;
109     int found = 0;
110     
111     if(!list) return -1;
112     /*at 必須 >= 0*/
113     if(at < 0) return -1;
114     
115     /* node指針指向第一個元素 */
116     node = list->next;
117     pos = 0;
118     while(NULL != node) {
119         if(at == pos) {
120             found = 1; // 找到了位置
121             node->data = x;
122             break;
123         }
124         /*往后移動元素*/
125         node = node->next;
126         pos++;
127     }
128     if(1 == found)
129         return 0;
130     else
131         return -1;
132 }
133 
134 // 插入節點
135 int 
136 InsertLinklist(linklist_t list, int at, data_t x)
137 {
138     /* 
139      * node_at and pos_at are used to locate the position of node_at.
140      * node_prev follows the node_at and always points to previous node 
141      *    of node_at.
142      * node_new is used to point to the new node to be inserted.
143      */
144     linknode_t      * node_prev, * node_at, * node_new;
145     int             pos_at;
146     int             found = 0;
147     
148     if(NULL == list) return -1;
149     
150     /* at 必須 >= 0 */
151     if(at < 0) return -1;
152     
153     node_new = malloc(sizeof(linknode_t));
154     if(NULL == node_new)
155         return -1;
156     node_new->data = x; // assigned value
157     node_new->next = NULL;
158     
159     node_prev = list; // head
160     node_at = list->next; //node_at指針指向第一元素
161     pos_at = 0;
162     while(NULL != node_at) {
163         if(pos_at == at) {
164             found = 1; // found the node ‘at'
165             break;
166         }
167         /* move to the next pos_at */
168         node_prev = node_at;
169         node_at = node_at->next;
170         pos_at++;    
171     }
172     
173     if(found) {
174         /* insert */
175         node_new->next = node_at;
176         node_prev->next = node_new;
177     } else{
178         /*
179          * If not found,means the provided 'at'
180          * exceeds the upper limit of the list, just
181          * append the new node to the end of the list
182          */
183         node_prev->next = node_new;   
184     }
185     
186     return 0;
187 }
188 
189 // 刪除節點
190 int 
191 DeleteLinklist(linklist_t list, int at)
192 {
193    /* 
194     * node_at and pos_at are used to locate the position of node_at.
195     * node_prev follows the node_at and always points to previous node 
196     *    of node_at.
197     */ 
198     linknode_t      * node_prev, * node_at;
199     int             pos_at;
200     int             found = 0;
201     
202     if(!list) return -1;
203     if(at < 0) return -1;
204     
205     node_prev = list; // node_prev指針指向鏈表頭
206     node_at = list->next; // node_at指針指向第一元素
207     pos_at = 0;
208     
209     while(NULL != node_at) {
210         if(pos_at == at) {
211             // found the node 'at'
212             found = 1;
213             break;
214         }
215         // move to the next pos_at
216         node_prev = node_at;
217         node_at = node_at->next;
218         pos_at++;    
219     }
220     if(found) {
221         // remove 
222         node_prev->next = node_at->next;
223         free(node_at); 
224         return 0;
225     }else
226         return -1;   
227 }
228 
229 // 鏈表轉置
230 linklist_t 
231 ReverseLinklist(linklist_t list)
232 {
233     linknode_t * node;       // iterator
234     linknode_t * node_prev;  // previous node of iterator
235     linknode_t * node_next;  /* next node of iterator
236                              * used to backup next of iterator
237                              */
238     if(NULL == list) return NULL;
239     node_prev = NULL;
240     node = list->next; // node指針指向第一個元素
241     while(NULL != node) {
242         /*
243          * step1: backup node->next
244          * due to the next of iterator will be
245          * modified in step2
246          */
247         node_next = node->next;
248         /* 
249          * when iterator reaches the last node 
250          * of original list, make the list head
251          * point to the last node, so the original
252          * last one becomes the first one.
253          */
254         if(NULL == node_next)
255             list->next = node;
256         /* 
257          * step2: reverse the linkage between nodes
258          * make the node pointer to the previous node,
259          * not the next node
260          */        
261         node->next = node_prev;
262         /* 
263          * step3: move forward 
264          */
265         node_prev = node;
266         node = node_next;
267     }
268     
269     return list;
270 }
271 
272 // 打印鏈表
273 int
274 Display(linklist_t list)
275 {
276     linknode_t * node;
277     
278     if(NULL == list) return -1;
279     
280     node = list->next;
281     while(node != NULL) {
282         printf(" %d ", node->data);
283         node = node->next;
284     } 
285     printf("\n");
286     
287     return 0;
288 }
289 
290 
291 int main(int argc, char * argv[])
292 {
293     int i;
294     data_t x;
295     linklist_t p;
296     
297     /*創建鏈表*/
298     p = CreateEmptyLinklist();
299     Display(p);
300     data_t a[10] = {1,3,5,7,9,11,13,15,17,19};
301     
302     for(i = 0; i < 10; i++) {
303         /*插入鏈表*/
304         InsertLinklist(p, i, a[i]);
305     }
306     Display(p);
307     
308     /*鏈表轉置*/
309     ReverseLinklist(p);
310     /*鏈表長度*/
311     printf("The length of the list is [%d]\n", LengthLinklist(p));
312     Display(p);
313     
314     /*獲取特定節點值*/
315     GetLinklist(p, 4, &x);
316     printf("The No.4 of this list is [%d]\n", x);
317     
318     /*設置特定節點的值*/
319     SetLinklist(p, 4, 100);
320     GetLinklist(p, 4, &x);
321     printf("After updating! The No.4 of this list is [%d]\n", x);
322     Display(p);
323     
324     /*刪除節點*/
325     DeleteLinklist(p,5);
326     printf("After delete!The length of list is [%d]\n", LengthLinklist(p));
327     Display(p);
328     
329     /*清空鏈表*/
330     ClearLinklist(p);
331     if(IsEmptyLinklist(p)) 
332         printf("This list is empty!\n");
333     /*銷毀鏈表*/
334     DestroyLinklist(p);
335     printf("This list is destroyed!\n");
336     
337    
338     return 0;
339     
340 }

運行

 

4. 鳴謝你動了誰的奶酪? ^_^)

感謝下面博主的共享,本文的基石,謝謝!!

感謝:https://blog.csdn.net/zqixiao_09/article/details/50402523

 

 

5. 后記

 

行歌

 

在草長鶯飛的季節里喃喃低唱 

到處人潮洶涌還會孤獨

怎么

在燈火闌珊處竟然會覺得荒蕪

從前輕狂繞過時光        


免責聲明!

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



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