一、鏈表簡介
1 數據結構中,鏈表是最基礎的。然而鏈表根據不同的需求分成的種類很多,單向或雙向鏈表,循環或非循環鏈表,帶頭節點或者不帶頭節點的鏈表。
2 本文實現——帶頭節點的單鏈表。
3 由於僅僅是學習鏈表的基本操作,所以在數據字段僅僅設置一個字段;
由於僅僅是學習基本操作,不涉及復雜的算法思想,所以不會很難,主要以代碼為主,附上必要的解釋即可。
二、具體實現
整體分析:帶有頭節點的單鏈表的操作很方便,主要體現在插入和刪除時不需要判斷是否是第一個元素。
1) 頭文件定義如下:

1 #ifndef LinearList_LinkList_h 2 #define LinearList_LinkList_h 3 4 #include <stdio.h> 5 #include <stdlib.h> 6 7 #define OK 1 8 #define ERROR 0 9 10 11 typedef int Status; 12 typedef int ElemType; 13 14 15 typedef struct LNode { 16 ElemType data; 17 struct LNode *pNext; 18 }LNode, *LinkList; 19 20 //LinkList with head 21 Status CreateLinkListFromHead(LinkList *L, int nInputLength); 22 Status CreateLinkListFromRear(LinkList *L, int nInputLength); 23 Status InsertToLinkList(LinkList *L, int nIndex, ElemType eElem); 24 Status DeleteFromLinkList(LinkList *L, int nIndex, ElemType *pElem); 25 Status DestroyLinkList(LinkList *L); 26 Status PrintLinkList(LinkList L); 27 28 #endif
2)具體實現:
1 建立單鏈表:
思路: a)校驗參數
b)校驗長度是否合法(不校驗也行。這里主要是為了防止非法輸入,快速返回)
c)建立頭節點。(也有一種方法:單獨寫一個建立頭節點的函數。此處直接嵌入在建表的過程中。)
d)循環建立節點,然后選擇不同的插入方法插入。
詳解插入:
a)插入代碼:
設pNode指向待插入位置的前一個插入點,pInsertNode指向待插入的節點本身,則插入代碼如下:
pInsertNode->pNext = pNode->pNext;
pNode->pNext = pInsertNode;
b)插入的方法:
頭插法:每次插入點都選擇在頭節點的后一個節點,也就是整個有效鏈表的第一個位置。
pNode指向待插入節點本身,L指向表頭節點。
此時代碼如下:
(在建立頭節點時,一定要先設置頭節點的指針域為空 L->pNext = NULL,否則到最后就無法設置表尾為空了。)
pNode->pNext = L->pNext;
L->pNext = pNode;
代碼如下:

1 Status CreateLinkListFromHead(LinkList *L, int nInputLength) { 2 if (NULL == L) { 3 printf("Error parament."); 4 return ERROR; 5 } 6 7 *L = (LinkList)malloc(sizeof(LNode)); 8 if (NULL == *L) { 9 printf("Out of memory."); 10 return ERROR; 11 } 12 (*L)->pNext = NULL; //because list is created from head. So make the list empty 13 14 if (nInputLength < 1) { 15 printf("Error length."); 16 return ERROR; 17 } 18 19 LNode *pNode; 20 int nValue; 21 22 printf("Input the values:"); 23 for (int i = 1; i <= nInputLength; i++) { 24 scanf("%d", &nValue); 25 26 pNode = (LinkList)malloc(sizeof(LNode)); 27 if (NULL == pNode) { 28 printf("Out of memory."); 29 return ERROR; 30 } 31 pNode->data = nValue; 32 33 pNode->pNext = (*L)->pNext; 34 (*L)->pNext = pNode; 35 } 36 37 return OK; 38 }
尾插法:每次插入點都選擇在鏈表的表尾處。因此需要額外增加一個指針(pRear)用於標示鏈表表尾。
起初,pRear指向表頭節點pRear = L;pNode指向待插入節點本身
插入代碼如下:
pRear->pNext = pNode;
pRear = pRear->pNext;
到最后一定要設置表尾為空:pRear->pNext = NULL;
代碼如下:

1 Status CreateLinkListFromRear(LinkList *L, int nInputLength) { 2 if (NULL == L) { 3 printf("Error parament."); 4 return ERROR; 5 } 6 7 //build the head 8 *L = (LinkList)malloc(sizeof(LNode)); 9 if (NULL == *L) { 10 printf("Out of memory."); 11 return ERROR; 12 } 13 (*L)->pNext = NULL; //make the list empty 14 15 if (nInputLength < 1) { 16 printf("Invalid input length."); 17 return ERROR; 18 } 19 20 int nValue; 21 LNode *pNode; 22 LNode *pRear = *L; 23 24 printf("Input the values:"); 25 for (int i = 1; i <= nInputLength; i++) { 26 scanf("%d", &nValue); 27 28 pNode = (LinkList)malloc(sizeof(LNode)); 29 if (NULL == pNode) { 30 printf("Out of memory."); 31 return ERROR; 32 } 33 pNode->data = nValue; 34 35 pRear->pNext = pNode; 36 pRear = pRear->pNext; 37 } 38 39 pRear->pNext = NULL; 40 41 return OK; 42 }
2 插入節點:
思路:a)校驗參數
b)建立待插入節點
c)找到待插入點
d)插入
分析:純粹的插入就是那兩行代碼,然而重點在於插入點的合法性判斷以及插入點的定位。
合法:當插入點大於表長時應該返回錯誤;
插入點:一般來講,會定位到當前帶插入點的前一個位置;
(其實也可以直接定位到帶插入點處,插入后交換前后元素即可。但當數據元素過多時不適合。)
代碼如下:

1 Status InsertToLinkList(LinkList *L, int nIndex, ElemType eElem) { 2 if (NULL == L) { 3 printf("Error parament."); 4 return ERROR; 5 } 6 7 if (nIndex < 1) { 8 printf("Invalid insert point."); 9 return ERROR; 10 } 11 12 int i = 0; 13 LNode *pNode; 14 LNode *qNode; 15 16 qNode = (LinkList)malloc(sizeof(LNode)); 17 if (NULL == qNode) { 18 printf("Out of memory."); 19 return ERROR; 20 } 21 qNode->data = eElem; 22 23 //the key--find the insert point 24 pNode = *L; 25 while (NULL != pNode->pNext && i < nIndex - 1) { 26 pNode = pNode->pNext; 27 i++; 28 } 29 30 //invalid insert point 31 if (i < nIndex - 1) { 32 printf("Invalid Insert point."); 33 return ERROR; 34 } 35 36 //insert 37 qNode->pNext = pNode->pNext; 38 pNode->pNext = qNode; 39 40 41 return OK; 42 }
3 刪除節點:(刪除和插入類似)
思路:a)校驗參數
b)判斷鏈表是否為空
c)找到待插入點
d)取出待刪除元素中的值
e)刪除
分析:純粹的刪除代碼也就固定的兩行,然而重點在於刪除點的定位和刪除后指針的處理
刪除點定位:也是需要定位到待刪除點的前一個位置;
刪除后處理:free和置空
代碼如下:

1 Status DeleteFromLinkList(LinkList *L, int nIndex, ElemType *pElem) { 2 if (NULL == L || NULL == pElem) { 3 printf("Error parament."); 4 return ERROR; 5 } 6 7 if (NULL == (*L)->pNext) { 8 printf("The list is empty."); 9 return ERROR; 10 } 11 12 if (nIndex < 1) { 13 printf("Invalid delete point."); 14 return ERROR; 15 } 16 17 int i = 0; 18 LNode *qNode; 19 LNode *pNode = *L; // in case of the first valid delete point, the pNode should point to the head. 20 21 while (NULL != pNode->pNext && i < nIndex - 1) { 22 pNode = pNode->pNext; 23 i++; 24 } 25 26 if (NULL == pNode->pNext) { // nIndex - 1 == i 27 printf("Invalid delete point."); 28 return ERROR; 29 } 30 31 qNode = pNode->pNext; 32 pNode->pNext = qNode->pNext; 33 34 *pElem = qNode->data; 35 free(qNode); 36 qNode = NULL; 37 38 return OK; 39 }
4 銷毀鏈表
思路:a)校驗參數
b)判斷是否為空
c)遍歷鏈表,依次刪除。
分析:銷毀鏈表比較簡單,只需要完整遍歷這條鏈表即可。
代碼如下:

1 Status DestroyLinkList(LinkList *L) { 2 if (NULL == L) { 3 printf("Error parament."); 4 return ERROR; 5 } 6 7 if (NULL == (*L)->pNext) { 8 printf("The list is empty."); 9 return ERROR; 10 } 11 12 LNode *pNode = (*L)->pNext; 13 while (NULL != pNode) { 14 (*L)->pNext = pNode->pNext; 15 free(pNode); 16 17 pNode = (*L)->pNext; 18 } 19 20 (*L)->pNext = NULL; 21 pNode = NULL; 22 23 return OK; 24 }
三、附上完整版鏈表實現代碼,方便完整拷貝,測試等。

1 #include "LinkList.h" 2 3 4 /*-----------------LinkList withhead-------------------------------------------------*/ 5 6 Status CreateLinkListFromHead(LinkList *L, int nInputLength) { 7 if (NULL == L) { 8 printf("Error parament."); 9 return ERROR; 10 } 11 12 *L = (LinkList)malloc(sizeof(LNode)); 13 if (NULL == *L) { 14 printf("Out of memory."); 15 return ERROR; 16 } 17 (*L)->pNext = NULL; //because list is created from head. So make the list empty 18 19 if (nInputLength < 1) { 20 printf("Error length."); 21 return ERROR; 22 } 23 24 LNode *pNode; 25 int nValue; 26 27 printf("Input the values:"); 28 for (int i = 1; i <= nInputLength; i++) { 29 scanf("%d", &nValue); 30 31 pNode = (LinkList)malloc(sizeof(LNode)); 32 if (NULL == pNode) { 33 printf("Out of memory."); 34 return ERROR; 35 } 36 pNode->data = nValue; 37 38 pNode->pNext = (*L)->pNext; 39 (*L)->pNext = pNode; 40 } 41 42 return OK; 43 } 44 45 Status CreateLinkListFromRear(LinkList *L, int nInputLength) { 46 if (NULL == L) { 47 printf("Error parament."); 48 return ERROR; 49 } 50 51 //build the head 52 *L = (LinkList)malloc(sizeof(LNode)); 53 if (NULL == *L) { 54 printf("Out of memory."); 55 return ERROR; 56 } 57 (*L)->pNext = NULL; //make the list empty 58 59 if (nInputLength < 1) { 60 printf("Invalid input length."); 61 return ERROR; 62 } 63 64 int nValue; 65 LNode *pNode; 66 LNode *pRear = *L; 67 68 printf("Input the values:"); 69 for (int i = 1; i <= nInputLength; i++) { 70 scanf("%d", &nValue); 71 72 pNode = (LinkList)malloc(sizeof(LNode)); 73 if (NULL == pNode) { 74 printf("Out of memory."); 75 return ERROR; 76 } 77 pNode->data = nValue; 78 79 pRear->pNext = pNode; 80 pRear = pRear->pNext; 81 } 82 83 pRear->pNext = NULL; 84 85 return OK; 86 } 87 88 89 Status InsertToLinkList(LinkList *L, int nIndex, ElemType eElem) { 90 if (NULL == L) { 91 printf("Error parament."); 92 return ERROR; 93 } 94 95 if (nIndex < 1) { 96 printf("Invalid insert point."); 97 return ERROR; 98 } 99 100 int i = 0; 101 LNode *pNode; 102 LNode *qNode; 103 104 qNode = (LinkList)malloc(sizeof(LNode)); 105 if (NULL == qNode) { 106 printf("Out of memory."); 107 return ERROR; 108 } 109 qNode->data = eElem; 110 111 //the key--find the insert point 112 pNode = *L; 113 while (NULL != pNode->pNext && i < nIndex - 1) { 114 pNode = pNode->pNext; 115 i++; 116 } 117 118 //invalid insert point 119 if (i < nIndex - 1) { 120 printf("Invalid Insert point."); 121 return ERROR; 122 } 123 124 //insert 125 qNode->pNext = pNode->pNext; 126 pNode->pNext = qNode; 127 128 129 return OK; 130 } 131 132 Status DeleteFromLinkList(LinkList *L, int nIndex, ElemType *pElem) { 133 if (NULL == L || NULL == pElem) { 134 printf("Error parament."); 135 return ERROR; 136 } 137 138 if (NULL == (*L)->pNext) { 139 printf("The list is empty."); 140 return ERROR; 141 } 142 143 if (nIndex < 1) { 144 printf("Invalid delete point."); 145 return ERROR; 146 } 147 148 int i = 0; 149 LNode *qNode; 150 LNode *pNode = *L; // in case of the first valid delete point, the pNode should point to the head. 151 152 while (NULL != pNode->pNext && i < nIndex - 1) { 153 pNode = pNode->pNext; 154 i++; 155 } 156 157 if (NULL == pNode->pNext) { // nIndex - 1 == i 158 printf("Invalid delete point."); 159 return ERROR; 160 } 161 162 qNode = pNode->pNext; 163 pNode->pNext = qNode->pNext; 164 165 *pElem = qNode->data; 166 free(qNode); 167 qNode = NULL; 168 169 return OK; 170 } 171 172 Status DestroyLinkList(LinkList *L) { 173 if (NULL == L) { 174 printf("Error parament."); 175 return ERROR; 176 } 177 178 if (NULL == (*L)->pNext) { 179 printf("The list is empty."); 180 return ERROR; 181 } 182 183 LNode *pNode = (*L)->pNext; 184 while (NULL != pNode) { 185 (*L)->pNext = pNode->pNext; 186 free(pNode); 187 188 pNode = (*L)->pNext; 189 } 190 191 (*L)->pNext = NULL; 192 pNode = NULL; 193 194 return OK; 195 } 196 197 Status PrintLinkList(LinkList L) { 198 if (NULL == L) { 199 printf("Error parament."); 200 return ERROR; 201 } 202 203 if (NULL == L->pNext) { 204 printf("The list is empty."); 205 return ERROR; 206 } 207 208 LNode *pNode = L->pNext; 209 210 while (NULL != pNode) { 211 printf("%d ", pNode->data); 212 pNode = pNode->pNext; 213 } 214 215 return OK; 216 }