一、链表简介
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 }