C语言实现单链表,主要功能为空链表创建,链表初始化(头插法),链表元素读取,按位置插入,(有序链表)按值插入,按位置删除,按值删除,清空链表,销毁链表。
关键思路:(1)将结点创建结构体;(2)链表中添加头结点,以便统一操作;(3)使用结点一级指针和二级指针的异同点;(4)链表的最小操作单位是结点;(5)操作的起始位置是头结点还是第一个结点,及起始索引是0还是1.
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 //涉及到结构体自引用 6 typedef struct Node{ 7 int data; 8 struct Node *next; 9 }Node; 10 11 //创建空链表 12 //必须用二级指针,涉及到头指针的创建 13 int iniList(Node **List){ 14 *List = (Node *)malloc(sizeof(Node)); 15 if (NULL == *List){ 16 return 0; 17 } 18 19 (*List)->next = NULL; //创建头结点 20 return 1; 21 } 22 23 //初始化链表(头插法) 24 //必须二级指针 25 int iniListHead(Node **List, int n){ 26 *List = (Node *)malloc(sizeof(Node)); 27 if (NULL == *List){ 28 return 0; 29 } 30 (*List)->next = NULL; 31 srand(time(0)); 32 33 int i = 0; 34 while (i < n){ 35 Node *tmpNode = (Node *)malloc(sizeof(Node)); 36 if (NULL == tmpNode){ 37 return 0; 38 } 39 tmpNode->data = rand() % 100 + 1; 40 tmpNode->next = (*List)->next; 41 (*List)->next = tmpNode; 42 43 44 ++i; 45 } 46 return 1; 47 } 48 49 //初始化链表(尾插法) 50 //必须二级指针 51 //需要借助辅助变量pCurrent,将每次尾插的新元素当做当前元素 52 int iniListTail(Node **List, int n){ 53 *List = (Node *)malloc(sizeof(Node)); 54 if (NULL == *List){ 55 return 0; 56 } 57 (*List)->next = NULL; 58 srand(time(0)); 59 60 Node *pCurrent = *List; 61 62 int i = 0; 63 while (i < n){ 64 Node *tmpNode = (Node *)malloc(sizeof(Node)); 65 if (NULL == tmpNode){ 66 return 0; 67 } 68 tmpNode->data = rand() % 100 + 1; 69 tmpNode->next = NULL; 70 pCurrent->next = tmpNode; 71 pCurrent = tmpNode; 72 73 ++i; 74 } 75 return 1; 76 } 77 78 //清空链表(不删除头结点) 79 //一级,二级指针均可 80 //首先找到链表地址,然后移动至表尾 81 //判断条件为指针域是否为空,即到达结尾 82 int deleteList(Node *List){ 83 84 //这种方法无法删除尾结点 85 //Node *p= List; 86 //Node *q = NULL; 87 // 88 //while (p->next){ 89 // q = p; 90 // free(p); 91 // p = q->next; 92 //} 93 94 Node *p = List->next; 95 Node *q = NULL; 96 97 while (p){ 98 q = p->next; 99 free(p); 100 p = q; 101 } 102 103 List->next = NULL; 104 return 1; 105 } 106 107 //销毁链表 108 //必须使用二级指针,销毁头结点和头指针 109 //最后将链表头指针置空 110 int desrotyList(Node **List){ 111 112 Node *p = *List; 113 Node *q = NULL; 114 115 //如果为空链表,直接删除头结点 116 //如果不是空链表,从头结点开始删除 117 while (p){ 118 q = p->next; 119 free(p); 120 p = q; 121 } 122 (*List) = NULL; 123 124 //下面是从第一个结点开始删除 125 //最后释放掉头结点 126 //Node *p = (*List)->next; 127 //Node *q = NULL; 128 // 129 //while (p){ 130 // q = p->next; 131 // free(p); 132 // p = q; 133 //} 134 //free(*List); 135 //(*List) = NULL; 136 137 return 1; 138 } 139 140 //链表获取元素 141 //一级,二级指针均可 142 //头结点无意义,从第一个结点开始遍历,i从1开始 143 //每次都指向下一结点,到pos-1即可 144 int getList(Node *List, int pos, int *element){ 145 Node *p = List->next; 146 147 int i = 1; 148 while (p && i < pos){ 149 p = p->next; 150 ++i; 151 } 152 *element = p->data; 153 return 1; 154 } 155 156 //链表按位置插入 157 //一级,二级指针均可 158 //从头结点开始,有可能插入在第一个位置,遍历从1开始 159 int insertListPos(Node *List, int pos, int value){ 160 Node *p = List; 161 162 int i = 1; 163 while (p && i < pos){ 164 p = p->next; 165 ++i; 166 } 167 Node *tmpNode = (Node *)malloc(sizeof(Node)); 168 tmpNode->data = value; 169 tmpNode->next = p->next; 170 p->next = tmpNode; 171 return 1; 172 } 173 174 //有序链表,按值插入 175 //一二级指针均可 176 int insertListValue(Node *List, int value){ 177 Node *pCur = List->next; 178 Node *pPer = List; 179 while (pCur && pCur->data < value){ 180 pPer = pCur; 181 pCur = pCur->next; 182 } 183 184 Node *tmpNode = (Node *)malloc(sizeof(Node)); 185 if (NULL == tmpNode){ 186 return 0; 187 } 188 tmpNode->data = value; 189 tmpNode->next = pPer->next; 190 pPer->next = tmpNode; 191 return 1; 192 } 193 194 //链表按位置删除 195 //一二级指针均可 196 //记得释放结点内存 197 //如果删除第一个结点,需要操纵头结点 198 int deleteListPos(Node *List, int pos){ 199 Node *p = List; 200 201 int i = 1; 202 while (p && i < pos){ 203 p = p->next; 204 ++i; 205 } 206 if (NULL == p->next) 207 return 0; 208 209 Node *tmpNode = p->next; 210 p->next = p->next->next; 211 212 free(tmpNode); 213 return 1; 214 } 215 216 //链表按值删除元素 217 //一二级指针均可 218 //从第一个结点开始 219 int deleteListValue(Node *List, int value){ 220 Node *pCur = List->next; 221 Node *pPer = List; 222 223 while (pCur && pCur->data != value){ 224 pPer = pCur; 225 pCur = pCur->next; 226 } 227 if (pCur == NULL){ //空链表不删除任何结点 228 return 0; 229 } 230 else{ 231 pPer->next = pCur->next; 232 free(pCur); 233 } 234 return 1; 235 } 236 237 int main(){ 238 239 Node *testList = NULL; 240 iniList(&testList); 241 //iniListHead(&testList, 3); 242 //iniListTail(&testList, 3); 243 244 245 insertListPos(testList, 1, 2); 246 insertListPos(testList, 2, 4); 247 insertListPos(testList, 3, 5); 248 insertListPos(testList, 4, 10); 249 //insertListPos(testList, 1, 1); 250 insertListValue(testList, 1); 251 252 //deleteListPos(testList, 1); 253 //deleteListValue(testList, 4); 254 255 //deleteList(testList); 256 257 //printf("%d ", testList); 258 //desrotyList(&testList); 259 //printf("%d ", testList); 260 261 Node * tmpNode = testList->next; 262 while (tmpNode){ 263 printf("%d ", tmpNode->data); 264 tmpNode = tmpNode->next; 265 } 266 267 printf("---------------------- "); 268 269 int a = 0; 270 getList(testList, 2, &a); 271 printf("%d ", a); 272 273 system("pause"); 274 275 return 0; 276 }
通过C++实现C语言的链表,主要区别:(1)struct可以不通过typedef,直接使用Node;(2)将malloc和free更换为new和delete
1 #include<iostream> 2 #include<ctime> 3 4 using namespace std; 5 6 struct Node{ 7 int data; 8 Node *next; 9 }; 10 11 //创建空链表 12 //必须用二级指针,涉及到头指针的创建 13 int iniList(Node **List){ 14 *List = new Node; 15 16 (*List)->next = NULL; //创建头结点 17 return 1; 18 } 19 20 //初始化链表(头插法) 21 //必须二级指针 22 int iniListHead(Node **List, int n){ 23 *List = new Node; 24 25 (*List)->next = NULL; 26 srand(time(0)); 27 28 int i = 0; 29 while (i < n){ 30 Node *tmpNode = new Node; 31 32 tmpNode->data = rand() % 100 + 1; 33 tmpNode->next = (*List)->next; 34 (*List)->next = tmpNode; 35 36 ++i; 37 } 38 return 1; 39 } 40 41 //初始化链表(尾插法) 42 //必须二级指针 43 //需要借助辅助变量pCurrent,将每次尾插的新元素当做当前元素 44 int iniListTail(Node **List, int n){ 45 *List = new Node; 46 47 (*List)->next = NULL; 48 srand(time(0)); 49 50 Node *pCurrent = *List; 51 52 int i = 0; 53 while (i < n){ 54 Node *tmpNode = new Node; 55 56 tmpNode->data = rand() % 100 + 1; 57 tmpNode->next = NULL; 58 pCurrent->next = tmpNode; 59 pCurrent = tmpNode; 60 61 ++i; 62 } 63 return 1; 64 } 65 66 //清空链表(不删除头结点) 67 //一级,二级指针均可 68 //首先找到链表地址,然后移动至表尾 69 //判断条件为指针域是否为空,即到达结尾 70 int deleteList(Node *List){ 71 72 //这种方法无法删除尾结点 73 //Node *p= List; 74 //Node *q = NULL; 75 // 76 //while (p->next){ 77 // q = p; 78 // free(p); 79 // p = q->next; 80 //} 81 82 Node *p = List->next; 83 Node *q = NULL; 84 85 while (p){ 86 q = p->next; 87 delete p; 88 p = q; 89 } 90 91 List->next = NULL; 92 return 1; 93 } 94 95 //销毁链表 96 //必须使用二级指针,销毁头结点和头指针 97 //最后将链表头指针置空 98 int desrotyList(Node **List){ 99 100 Node *p = *List; 101 Node *q = NULL; 102 103 //如果为空链表,直接删除头结点 104 //如果不是空链表,从头结点开始删除 105 while (p){ 106 q = p->next; 107 delete p; 108 p = q; 109 } 110 (*List) = NULL; 111 112 //下面是从第一个结点开始删除 113 //最后释放掉头结点 114 //Node *p = (*List)->next; 115 //Node *q = NULL; 116 // 117 //while (p){ 118 // q = p->next; 119 // free(p); 120 // p = q; 121 //} 122 //free(*List); 123 //(*List) = NULL; 124 125 return 1; 126 } 127 128 //链表获取元素 129 //一级,二级指针均可 130 //头结点无意义,从第一个结点开始遍历,i从1开始 131 //每次都指向下一结点,到pos-1即可 132 int getList(Node *List, int pos, int *element){ 133 Node *p = List->next; 134 135 int i = 1; 136 while (p && i < pos){ 137 p = p->next; 138 ++i; 139 } 140 *element = p->data; 141 return 1; 142 } 143 144 //链表按位置插入 145 //一级,二级指针均可 146 //从头结点开始,有可能插入在第一个位置,遍历从1开始 147 int insertListPos(Node *List, int pos, int value){ 148 Node *p = List; 149 150 int i = 1; 151 while (p && i < pos){ 152 p = p->next; 153 ++i; 154 } 155 Node *tmpNode = new Node; 156 tmpNode->data = value; 157 tmpNode->next = p->next; 158 p->next = tmpNode; 159 return 1; 160 } 161 162 //有序链表,按值插入 163 //一二级指针均可 164 int insertListValue(Node *List, int value){ 165 Node *pCur = List->next; 166 Node *pPer = NULL; 167 while (pCur && pCur->data < value){ 168 pPer = pCur; 169 pCur = pCur->next; 170 } 171 172 Node *tmpNode = new Node; 173 if (NULL == tmpNode){ 174 return 0; 175 } 176 tmpNode->data = value; 177 tmpNode->next = pPer->next; 178 pPer->next = tmpNode; 179 return 1; 180 } 181 182 //链表按位置删除 183 //一二级指针均可 184 //记得释放结点内存 185 //如果删除第一个结点,需要操纵头结点 186 int deleteListPos(Node *List, int pos){ 187 Node *p = List; 188 189 int i = 1; 190 while (p && i < pos){ 191 p = p->next; 192 ++i; 193 } 194 Node *tmpNode = p->next; 195 p->next = p->next->next; 196 197 delete tmpNode; 198 return 1; 199 } 200 201 //链表按值删除元素 202 //一二级指针均可 203 //从第一个结点开始 204 int deleteListValue(Node *List, int value){ 205 Node *pCur = List->next; 206 Node *pPer = List; 207 208 while (pCur && pCur->data != value){ 209 pPer = pCur; 210 pCur = pCur->next; 211 } 212 if (pCur == NULL){ 213 return 0; 214 } 215 else{ 216 pPer->next = pCur->next; 217 delete pCur; 218 } 219 return 1; 220 }
结点结构体
将结点创建为结构体,其中包含数据域和指针域成员,指针域涉及结构体自引用。指针域成员之所以为结点类指针,一是为了编译时能明确结构体大小,二是为了指向下一个结点,因此初始化时不用开辟内存,只需要赋值为NULL即可。当然了,开辟内存也是可以的,这样在删除结点,及清空链表时需要记得将其释放,多此一举,不提倡。
1 //涉及到结构体自引用 2 typedef struct Node{ 3 int data; 4 struct Node *next; 5 }Node;
空链表创建(二级指针)
空链表创建时,建议创建头结点。在插入和删除第一个位置的元素时,需要用结点的二级指针移动头指针,但普通结点的插入和删除并不需要移动头指针。为了便于操作的统一(指插入和删除操作),这里先创建头结点。
另外,在创建空链表时,需要传入二级指针,主要是为了操作头指针,只要涉及操作头指针的都需要使用二级指针。
1 //创建空链表 2 //必须用二级指针,涉及到头指针的创建 3 int iniList(Node **List){ 4 *List = (Node *)malloc(sizeof(Node)); 5 if (NULL == *List){ 6 return 0; 7 } 8 9 (*List)->next = NULL; //创建头结点,将其指针域置空 10 return 1; 11 }
链表初始化(二级指针)
链表初始化,即是创建空链表,并对链表中的n个元素进行赋值。一般来说,有头插法、尾插法两种,头插法是在头结点后插入,尾插法是在链表尾插入。
头插法
头插法一般思路:(1)创建带头结点的空链表;(2)创建新结点,对新结点赋值;(3)新结点指向头结点的下一个结点,头结点指向新结点。
1 //初始化链表(头插法) 2 //必须二级指针 3 int iniListHead(Node **List, int n){ 4 *List = (Node *)malloc(sizeof(Node)); 5 if (NULL == *List){ 6 return 0; 7 } 8 (*List)->next = NULL; 9 srand(time(0)); 10 11 int i = 0; 12 while (i < n){ 13 Node *tmpNode = (Node *)malloc(sizeof(Node)); 14 if (NULL == tmpNode){ 15 return 0; 16 } 17 tmpNode->data = rand() % 100 + 1; 18 tmpNode->next = (*List)->next; 19 (*List)->next = tmpNode; 20 21 22 ++i; 23 } 24 return 1; 25 }
尾插法
尾插法一般思路:(1)创建带头结点的空链表;(2)创建新结点,对新结点数据域赋值,指针域置空;(3)建立临时变量指向头结点,头结点指向新结点;(4)将临时变量往后移动,指向新结点。
1 //初始化链表(尾插法) 2 //必须二级指针 3 //需要借助辅助变量pCurrent,将每次尾插的新元素当做当前元素 4 int iniListTail(Node **List, int n){ 5 *List = (Node *)malloc(sizeof(Node)); 6 if (NULL == *List){ 7 return 0; 8 } 9 (*List)->next = NULL; 10 srand(time(0)); 11 12 Node *pCurrent = *List; 13 14 int i = 0; 15 while (i < n){ 16 Node *tmpNode = (Node *)malloc(sizeof(Node)); 17 if (NULL == tmpNode){ 18 return 0; 19 } 20 tmpNode->data = rand() % 100 + 1; 21 tmpNode->next = NULL; 22 pCurrent->next = tmpNode; 23 pCurrent = tmpNode; 24 25 ++i; 26 } 27 return 1; 28 }
链表元素读取(一、二级指针)
链表元素读取,需要涉及到索引位置。我们知道头结点的数据域没有意义,因此起始位置从第一个结点开始,遍历值从1开始,到pos-1为止,此时p指向pos结点。
1 //链表获取元素 2 //一级,二级指针均可 3 //头结点无意义,从第一个结点开始遍历,i从1开始 4 //每次都指向下一结点,到pos-1即可 5 int getList(Node *List, int pos, int *element){ 6 Node *p = List->next; 7 8 int i = 1; 9 while (p && i < pos){ 10 p = p->next; 11 ++i; 12 } 13 *element = p->data; 14 return 1; 15 }
按位置插入(一、二级指针)
插入时,需要考虑任意位置,由于头结点的设立,我们不需要单独考虑第一个位置的结点插入。
一般思路:(1)起始位置从头结点开始,遍历值从1开始,到pos-1为止,此时指向pos-1结点;(2)创建新结点,给新结点数据域赋值;(3)新结点指向pos位置的下一结点,pos位置指向新结点。
1 //链表按位置插入 2 //一级,二级指针均可 3 //从头结点开始,有可能插入在第一个位置,遍历从1开始 4 int insertListPos(Node *List, int pos, int value){ 5 Node *p = List; 6 7 int i = 1; 8 while (p && i < pos){ 9 p = p->next; 10 ++i; 11 } 12 Node *tmpNode = (Node *)malloc(sizeof(Node)); 13 tmpNode->data = value; 14 tmpNode->next = p->next; 15 p->next = tmpNode; 16 return 1; 17 }
有序链表按值插入(一、二级指针)
有序链表中涉及到按值插入,按值删除时,需要建立两个临时变量,不同于按位置插入,我们可以在pos前一个停止遍历,按值插入,我们需要遍历找到插入位置,操作插入位置的前一个结点。
一般思路:(1)从第一个结点开始遍历;(2)创建per变量标记当前结点的前一结点;(3)创建新结点,操作per结点;(4)常规插入操作。
1 //有序链表,按值插入 2 //一二级指针均可 3 int insertListValue(Node *List, int value){ 4 Node *pCur = List->next; 5 Node *pPer = NULL; 6 while (pCur && pCur->data < value){ 7 pPer = pCur; 8 pCur = pCur->next; 9 } 10 11 Node *tmpNode = (Node *)malloc(sizeof(Node)); 12 if (NULL == tmpNode){ 13 return 0; 14 } 15 tmpNode->data = value; 16 tmpNode->next = pPer->next; 17 pPer->next = tmpNode; 18 return 1; 19 }
按位置删除(一、二级指针)
删除时,由于头结点的设立,因此不需要特殊考虑第一个位置的操作和头指针的移动。需要设置临时变量是释放p->next后,后面还会使用p->next的结点,这样会造成访问出错。
一般思路:(1)起始位置从头结点开始,遍历从1开始,到pos-1为止,此时指向pos-1结点;(2)创建临时变量,指向pos结点;(3)将pos-1结点指向pos的下一结点,释放掉pos结点。
1 //链表按位置删除 2 //一二级指针均可 3 //记得释放结点内存 4 //如果删除第一个结点,需要操纵头结点 5 int deleteListPos(Node *List, int pos){ 6 Node *p = List; 7 8 int i = 1; 9 while (p && i < pos){ 10 p = p->next; 11 ++i; 12 } 13 Node *tmpNode = p->next; 14 p->next = p->next->next; 15 16 free(tmpNode); 17 return 1; 18 }
按值删除(一、二级指针)
按值删除与按值插入一样,需要两个临时变量。
一般思路:(1)起始位置从第一个结点开始,(2)设立per结点指针保存当前结点的上一结点;(3)常规删除操作。
1 //链表按值删除元素 2 //一二级指针均可 3 //从第一个结点开始 4 int deleteListValue(Node *List, int value){ 5 Node *pCur = List->next; 6 Node *pPer = List; 7 8 while (pCur && pCur->data != value){ 9 pPer = pCur; 10 pCur = pCur->next; 11 } 12 if (pCur == NULL){ //空链表不删除任何结点 13 return 0; 14 } 15 else{ 16 pPer->next = pCur->next; 17 free(pCur); 18 } 19 return 1; 20 }
清空链表(一、二级指针)
清空链表,只是将链表中的结点删除,并释放掉结点空间,保留头结点,并将头结点的指针域置空。
一般思路:(1)起始位置从第一个结点开始;(2)设置临时变量q指向当前结点p的下一结点,释放掉当前结点p,将临时变量q赋给p;(3)最后将头结点的指针置空。
1 //清空链表(不删除头结点) 2 //一级,二级指针均可 3 //首先找到链表地址,然后移动至表尾 4 //判断条件为指针域是否为空,即到达结尾 5 int deleteList(Node *List){ 6 7 //这种方法无法删除尾结点,当p->next是尾结点上一节点时,走一遍程序 8 //Node *p= List; 9 //Node *q = NULL; 10 // 11 //while (p->next){ 12 // q = p; 13 // free(p); 14 // p = q->next; 15 //} 16 17 Node *p = List->next; 18 Node *q = NULL; 19 20 while (p){ 21 q = p->next; 22 free(p); 23 p = q; 24 } 25 26 List->next = NULL; 27 return 1; 28 }
销毁链表(二级指针)
销毁链表,是在清空链表的基础上,删除头结点,将头指针置空,涉及到头指针的操作,需要二级指针。
思路一:(1)与清空链表操作相同,从第一个结点开始删除;(2)最后释放掉头结点,将头指针置空
思路二:(1)起始位置从头结点开始删除;(2)将头指针置空
1 //销毁链表 2 //必须使用二级指针,销毁头结点和头指针 3 //最后将链表头指针置空 4 int desrotyList(Node **List){ 5 6 Node *p = *List; 7 Node *q = NULL; 8 9 //如果为空链表,直接删除头结点 10 //如果不是空链表,从头结点开始删除 11 while (p){ 12 q = p->next; 13 free(p); 14 p = q; 15 } 16 (*List) = NULL; 17 18 //下面是从第一个结点开始删除 19 //最后释放掉头结点 20 //Node *p = (*List)->next; 21 //Node *q = NULL; 22 // 23 //while (p){ 24 // q = p->next; 25 // free(p); 26 // p = q; 27 //} 28 //free(*List); 29 //(*List) = NULL; 30 31 return 1; 32 }
二级指针和一级指针插入解析
-------------------------------------------------------------------------------------------------------------
如果上面的资料对你有启发,麻烦点个推荐,让更多人的人看到哦。
关注公众号【两猿社】,懂点互联网,懂点IC的程序猿,带你丰富项目经验哦。