之前看到一篇單向鏈表的博文,代碼也看着很舒服,於是乎記錄下來,留給自己~,循序漸進,慢慢
延伸到真正的內核鏈表~(敢問路在何方?路在腳下~)
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. 后記
行歌
在草長鶯飛的季節里喃喃低唱
到處人潮洶涌還會孤獨
怎么
在燈火闌珊處竟然會覺得荒蕪
從前輕狂繞過時光