1.單鏈表:
在順序表中,用一組地址連續的存儲單元來一次存放線性表的結點,因此結點的邏輯順序與物理順序是一致的。但鏈表卻不同,鏈表是用一組任意的存儲單元來存放 線性表的結點,這組存儲單元可以是連續的,也可以是非連續的,甚至是零散分布在內存的任何位置上。因此,鏈表中結點的邏輯順序與物理順序不一定相同。為了正確表示節點間的邏輯關系,必須在存儲線性表的每個數據元素的同時,存儲指示其后繼結點的地址信息,這兩部分信息共同構成了單鏈表結點的結構,如下圖:
結點包括兩個域,數據域用來存放結點的值,指針域用來存儲數據元素的直接后繼的地址(或位置)。線性表正是通過每個結點的指針域將線性表的n個結點按其邏輯順序連接在一起的。由於此線性表的每個節點只有一個next指針域,故將這種鏈表叫做單鏈表。
由於單鏈表中的每一個結點除了第一個節點外,它們的存儲地址存放在其前驅結點的指針域中,由於第一個節點無前驅,所以應該設一個頭指針指向第一個節點,本文中設置了first指針指向第一個結點。單鏈表中的最后一個節點無直接后繼,所以指定單鏈表的最后一個結點的指針域為"空"(NULL)。
一般情況下,使用鏈表,只關心鏈表中結點的邏輯順序,並不關心每個結點的實際存儲位置,因此通常用箭頭來表示鏈域中的指針,於是鏈表就可以更直觀地畫成用箭頭鏈接起來的結點序列,如圖:
有時候,為了操作的統一方便,可以在單鏈表的第一個結點前附設一個頭結點,頭結點的數據域可以存儲一些關於線性表的長度等附加信息,也可以不存儲任何信息,對頭結點的數據域無特別規定,而頭結點的指針域用來存儲指向第一個結點的指針(即第一個結點的存儲位置)。如果線性表為空,則頭結點的指針域為"空"。如圖所示:
2.單鏈表操作:
頭插過程:
頭插的方式如上圖所示。采用頭插法得到的單鏈表的邏輯順序與輸入元素順序相反,亦稱頭插法為逆序建表法。在這只介紹頭插法的示意圖,對於尾插、頭刪、尾刪、可以參考《數據結構----用C語言描述》(耿國華主編)這本書。下面的代碼中,實現了頭插、尾插、頭刪、尾刪,讀者可以仔細研究研究。(本文所編寫的代碼是在VS2013編譯環境下運行的)
3.運行代碼:
Linklist.h文件包括各個操作函數的聲明以及包含的頭文件,test.c包含了測試代碼,Linklist.c文件里主要是各個操作函數的具體實現。
1 //Linklist.h
2 #pragma once
3 #include<stdio.h>
4 #include<assert.h>
5 #include<stdlib.h>
6
7 typedef int LDataType; 8 typedef struct Linklist 9 { 10 LDataType data; 11 struct Linklist *next; 12 }Linklist, *pLinklist; 13
14 //接口函數
15 pLinklist BuyNewNode(LDataType data);//動態生成新節點
16 void InitLinklist(pLinklist *pL);//初始化單鏈表
17 void PushBackLinklist(pLinklist *pL, LDataType data);//尾插
18 void PushFrontLinklist(pLinklist *pL, LDataType data);//頭插
19 void PopBackLinklist(pLinklist *pL);//尾刪
20 void PopFrontLinklist(pLinklist *pL);//頭刪
21 void PrintLinklist(Linklist *pL);//打印單鏈表
22 pLinklist FindLinklist(pLinklist *pL, LDataType data);//查找指定元素,返回指定元素的位置
23 void InsertLinklist(pLinklist *pL, pLinklist p, LDataType data);//指定位置插入
24 void RemoveLinklist(pLinklist *pL, LDataType data);//刪除第一個指定元素
25 void RemoveAllLinklist(pLinklist *pL, LDataType data);//刪除所有的指定元素
26 int IsEmptyLinklist(pLinklist pL);//判斷鏈表是否為空,為空返回1;不為空返回0;
27 void DestoryLinklist(pLinklist *pL);//銷毀單鏈表
1 //Linklist.c
2 #include"Linklist.h"
3 pLinklist BuyNewNode(LDataType data)//生成新節點
4 { 5 pLinklist NewNode = (pLinklist)malloc(sizeof(Linklist)); 6 if (NewNode == NULL) 7 { 8 printf("動態開辟內存空間失敗\n"); 9 return; 10 } 11 NewNode->data = data; 12 NewNode->next = NULL; 13 return NewNode; 14 } 15 void InitLinklist(pLinklist *pL)//初始化
16 { 17 assert(pL != NULL); 18 (*pL) = NULL; 19 } 20 void PushBackLinklist(pLinklist *pL, LDataType data)//尾插
21 { 22 assert(pL != NULL); 23 pLinklist NewNode = BuyNewNode(data); 24 if (*pL == NULL) 25 { 26 *pL = NewNode; 27 return; 28 } 29 pLinklist cur = *pL; 30 while (cur->next) 31 { 32 cur = cur->next; 33 } 34 cur->next = NewNode; 35 } 36 void PushFrontLinklist(pLinklist *pL, LDataType data)//頭插
37 { 38 assert(pL != NULL); 39 pLinklist NewNode = BuyNewNode(data); 40 if (*pL == NULL) 41 { 42 *pL = NewNode; 43 return; 44 } 45 NewNode->next = *pL; 46 *pL = NewNode; 47 } 48 int IsEmptyLinklist(pLinklist pL)//判斷是否為空鏈表
49 { 50 if (pL == NULL) 51 return 1; 52 return 0; 53 } 54 void PopBackLinklist(pLinklist *pL)//尾刪
55 { 56 assert(pL != NULL); 57 if (IsEmptyLinklist(*pL))//鏈表為空,沒有節點
58 { 59 printf("鏈表為空,刪除操作失敗\n"); 60 return; 61 } 62 pLinklist cur = *pL; 63 pLinklist pre = NULL;//保存cur的前一個節點
64 if (cur->next == NULL)//有一個節點
65 { 66 *pL = NULL; 67 free(cur); 68 cur = NULL; 69 return; 70 } 71 while (cur->next) 72 { 73 pre = cur; 74 cur = cur->next; 75 } 76 pre->next = NULL; 77 free(cur); 78 cur = NULL; 79 } 80 void PopFrontLinklist(pLinklist *pL)//頭刪
81 { 82 assert(pL != NULL); 83 if (*pL == NULL) 84 { 85 printf("鏈表為空,刪除操作失敗\n"); 86 return; 87 } 88 pLinklist cur = *pL; 89 *pL = cur->next; 90 free(cur); 91 cur = NULL; 92 } 93 pLinklist FindLinklist(pLinklist *pL, LDataType data)//查找指定元素,返回指定元素的位置
94 { 95 assert(pL != NULL); 96 pLinklist cur = *pL; 97 while (cur) 98 { 99 if (cur->data == data) 100 { 101 return cur; 102 } 103 cur = cur->next; 104 } 105 return NULL; 106 } 107 void InsertLinklist(pLinklist *pL, pLinklist p, LDataType data)//指定位置前面插入
108 { 109 assert(pL != NULL); 110 pLinklist NewNode = BuyNewNode(data); 111 pLinklist cur = *pL; 112 while (cur->next != p) 113 { 114 cur = cur->next; 115 } 116 NewNode->next = p; 117 cur->next = NewNode; 118 } 119 void RemoveLinklist(pLinklist *pL, LDataType data)//刪除第一個指定元素
120 { 121 assert(pL != NULL); 122 pLinklist cur = NULL; 123 pLinklist p = *pL; 124 pLinklist pre = NULL; 125 cur = FindLinklist(pL, data);//找到要刪除的指定元素
126 if (cur == NULL) 127 { 128 printf("沒找到要刪除的指定元素,刪除失敗\n"); 129 return; 130 } 131 if (*pL == cur)//位於第一個節點
132 { 133 *pL= cur->next; 134 free(cur); 135 cur = NULL; 136 return; 137 } 138 while (p!= cur) 139 { 140 pre = p; 141 p = p->next; 142 } 143 pre->next = cur->next; 144 free(cur); 145 cur = NULL; 146 } 147 void RemoveAllLinklist(pLinklist *pL, LDataType data)//刪除所有的指定元素
148 { 149 assert(pL != NULL); 150 pLinklist cur = NULL; 151 pLinklist p = *pL; 152 pLinklist pre = *pL; 153 while (p) 154 { 155
156 if (p->data == data && (*pL) == p) 157 { 158 pre = p; 159 p = p->next; 160 *pL = p; 161 free(pre); 162 pre = NULL; 163 } 164 else if (p->data == data) 165 { 166 cur = p; 167 p = p->next; 168 pre->next = p; 169 free(cur); 170 cur = NULL; 171 } 172 else
173 { 174 pre = p; 175 p = p->next; 176 } 177 } 178
179 } 180 void PrintLinklist(Linklist *pL)//打印單鏈表
181 { 182 pLinklist cur = pL; 183 while (cur) 184 { 185 printf("%d-->", cur->data); 186 cur = cur->next; 187 } 188 printf("NULL\n"); 189 } 190 void DestoryLinklist(pLinklist *pL)//銷毀單鏈表,放置內存溢出
191 { 192 assert(pL != NULL); 193 pLinklist cur = *pL; 194 pLinklist pre = NULL;//保存cur的前一個節點
195 if (*pL == NULL) 196 { 197 printf("鏈表為空\n"); 198 return; 199 } 200 if (cur->next == NULL)//只有一個節點
201 { 202 *pL = NULL; 203 free(cur); 204 cur = NULL; 205 return; 206 } 207 while (cur) 208 { 209 pre = cur; 210 cur = cur->next; 211 free(pre); 212 pre = NULL; 213 } 214 }
1 //test.c測試函數
2 #include"Linklist.h"
3
4 void test() 5 { 6 pLinklist cur = NULL;//用來接收FindLinklist的返回值
7 Linklist *first = NULL; 8 InitLinklist(&first);//初始化 9 //PushBackLinklist(&first, 1);//尾插元素 10 //PushBackLinklist(&first, 2); 11 //PushBackLinklist(&first, 3); 12 //PushBackLinklist(&first, 4); 13 //PushBackLinklist(&first, 5); 14 //PrintLinklist(first);//打印單鏈表
15 PushFrontLinklist(&first, 6);//頭插元素
16 PushFrontLinklist(&first, 7); 17 PushFrontLinklist(&first, 8); 18 PushFrontLinklist(&first, 9); 19 PushFrontLinklist(&first, 10); 20 //PopBackLinklist(&first);//尾刪一個節點 21 //PopFrontLinklist(&first);//頭刪一個節點 22 //PrintLinklist(first);//打印單鏈表 23 //DestoryLinklist(&first); 24 //cur = FindLinklist(&first, 8); 25 //InsertLinklist(&first, cur, 11); 26 //printf("在8前面插入11得:"); 27 //PrintLinklist(first);//打印單鏈表 28 //printf("刪除11得:"); 29 //RemoveLinklist(&first, 11); 30 //PrintLinklist(first);
31 PushFrontLinklist(&first, 7); 32 PushFrontLinklist(&first, 7); 33 //RemoveLinklist(&first, 7);
34 RemoveAllLinklist(&first, 7); 35 PrintLinklist(first); 36
37 //RemoveAllLinklist(&first, 7);//刪除所有的7
38 } 39 int main() 40 { 41 test(); 42 system("pause"); 43 return 0; 44 }
4.尾插法建立單鏈表如下圖:
測試圖: 運行圖:
5.頭插法建立單鏈表如下圖:
測試圖: 運行圖:
6.頭刪和尾刪一個結點:
測試圖: 運行圖:
7.在指定位置插入元素:
測試圖: 運行圖:
除這幾個操作外,還有刪除指定元素RemoveLinklist(刪除第一個找到的指定元素),刪除所有的指定元素RemoveAllLinklist。因為本文中單鏈表的結點是動態開辟的,因此還要實現銷毀函數DestoryLinklist,防止內存泄漏。