单链表综合操作

链表是C语言面试中常见的问题,那么我们今天就实现一个作为练习。之前看过一些关于这方面的资料和 文章,写的都不尽人意,这次写一个具有启发性的历程,由于时间关系,有些东西还是不能一一罗列实现,因为必须要跳过进行下面的学习了,而且我留下的也都是主干之后的细枝末叶,需要花点时间优化。

eg:---考虑周全的代码---

 1 /*   main.c   */
 2 
 3 
 4 #include <stdio.h>
 5 #include<malloc.h>
 6 #include<stdlib.h>
 7 #include"Mylist.h"
 8 int main(void)
 9 {
10     Mylist *list1=(Mylist*)malloc(sizeof(Mylist));
11     list1->value = 16;
12     list1->next = NULL;
13     Mylist* root = list1;//创建根节点root和第一个节点
14 
15 
16     insert(&root, 15);//有序插入
17     insert(&root, 13);
18     insert(&root, 13);
19     insert(&root, 20);
20     //print(root);//打印节点数据
21 
22     insert1(&root, 13);//无序插入
23     insert1(&root, 13);
24     insert1(&root, 19);
25     insert1(&root, 13);
26     print(root);//打印节点数据
27 
28     //Del_data1(root, 13);err,将出现错误,具体转到定义查看
29     //Del_data(&root, 13);//删除节点
30     //print(root);
31     
32     search(root, 13);
33     Del_data(&root, 13);
34     print(root);
35     search(root, 13);
36 
37     DestroyList(root);//销毁链表
38     return 0;
39 }

从main函数可以看出,我的创建链表头的方式就需要优化,但是这只是一个模拟,我只需要创建一个链表头,后续用插入或者删除对其进行操作,但是,我们应该优化成一个函数,在这个函数,一般命名creat函数中,实现链表的多个节点创建,这不是本文所关心的,而且实现也是容易的,所以先用一个头节点模拟,但是要注意,请用malloc开辟内存,否则后面free会出错,这个bug把我弄地挺恼火了。

 1 /*   Mylist.h   */
 2 
 3 
 4 #ifndef _MYLIST_H_
 5 #define _MYLIST_H_
 6 typedef int listtyp;
 7 typedef struct _SigList {
 8     listtyp value;
 9     //Mylist *link; err,至于为什么,好好回看c和指针,这里Mylist类型还没有创建
10     struct _SigList *next;
11 }Mylist;
12 
13 int insert(register Mylist **linkp, listtyp new_value);
14 int insert1(register Mylist **linkp, listtyp new_value);
15 
16 void print(Mylist*root);
17 int DestroyList(Mylist* list);
18 int ClearList(Mylist* L);
19 void Del_data1(Mylist* list, listtyp del_value);
20 void Del_data(Mylist** list, listtyp del_value);
21 int search(Mylist* list, listtyp element);
22 #endif

重点在于源文件的实现:

  1 #include<stdio.h>
  2 #include<malloc.h>
  3 #include<stdlib.h>
  4 #include"Mylist.h"
  5 
  6 #define FALSE 0
  7 #define TRUE  1
  8 
  9 
 10 /*插入到一个单链表,插入可以有序插入,也可以无序插入,
 11 以有序为例,毕竟这样的场景更实用,
 12 从小到大的顺序。
 13 下面的代码有不好的地方,此时需要二级指针登场
 14 如果忘了有什么不好的地方,P240, c和指针
 15 */
 16 /*
 17 int insert(Mylist * current, listtyp new_value)
 18 {
 19     Mylist *previous;
 20     Mylist *new;
 21     //找到正确的插入位置,从小到大插入
 22     while (current!=NULL && current->value < new_value)
 23 //在这个表达式中,->优先级最高,<优先级大于 !=,&&优先级最低,在不确定优先级的时候应该加括号
 24     {
 25         previous = current;
 26         current = current->next;
 27     }
 28     
 29     new = (Mylist*)malloc(sizeof(Mylist));//
 30     if (new == NULL)
 31     {
 32         return FALSE;
 33     }
 34     new->value = new_value;
 35     new->next = current;
 36     previous->next = new;
 37     return TRUE;
 38 }*/
 39 /*
 40 还是存在特殊情况的讨论,还需要优化
 41 int insert(Mylist **rootp, listtyp new_value)
 42 {
 43     Mylist *current;
 44     Mylist *previous;
 45     Mylist *new;
 46     //得到指向第一个节点的指针
 47     current = *rootp;
 48     previous = NULL;
 49 
 50     //寻找正确的插入位置
 51     while (current!=NULL && current->value<new_value)
 52     {
 53         previous = current;
 54         current = current->next;
 55     }
 56 
 57     //为新节点分配内存
 58     new = (Mylist*)malloc(sizeof(Mylist));
 59     if (new = NULL)
 60         return FALSE;
 61     new->value = new_value;
 62     new->next = current;
 63     if (previous == NULL)
 64         *rootp = new;
 65     else
 66         previous->next = new;
 67     return TRUE;
 68 }
 69 */
 70 //最终版本之插入 ---有序
 71 int insert(register Mylist **linkp, listtyp new_value)
 72 {
 73     register Mylist *current, *new;
 74     
 75     while ((current = *linkp) != NULL && current->value < new_value)
 76     {
 77         linkp = &current->next;//我们只关心节点next字段
 78     }
 79 
 80     new = (Mylist*)malloc(sizeof(Mylist));
 81     if (new == NULL)
 82         return FALSE;
 83     new->value = new_value;
 84     new->next = current;
 85     *linkp = new;
 86     return TRUE;
 87 }
 88 //---无序插入
 89 int insert1(register Mylist **linkp, listtyp new_value)
 90 {
 91     register Mylist *current, *new;
 92 
 93     while ((current = *linkp) != NULL )
 94     {
 95         linkp = &current->next;//我们只关心节点next字段
 96     }
 97 
 98     new = (Mylist*)malloc(sizeof(Mylist));
 99     if (new == NULL)
100         return FALSE;
101     new->value = new_value;
102     new->next = current;
103     *linkp = new;
104     return TRUE;
105 }
106 /*打印函数*/
107 void print(Mylist*root)
108 {
109     while (root != NULL)
110     {
111         printf("%d 
", root->value);
112         root = root->next;
113     }
114 }
115 //销毁链表
116 int DestroyList(Mylist* list)
117 { /* 初始条件:线性表L已存在。操作结果:销毁线性表L */
118   //销毁之后就不要再去访问该链表 !!!
119     Mylist* q;
120     while (list != NULL)
121     {
122         q = list->next;
123         free(list);
124         list = q;
125     }
126     return 1;
127 }
128 //清空链表
129 int ClearList(Mylist* L) 
130 { /* 初始条件:线性表L已存在。操作结果:将L重置为空表 */
131   //清空之后 还保留链表头,还可以访问该链表
132     Mylist *p,*q;
133     p = L->next; /* p指向第一个结点 */
134     while (p) /* 没到表尾 */
135     {
136         q = p->next;
137         free(p);
138         p = q;
139     }
140     L->next = NULL; /* 头结点指针域为空 */
141     return 1;
142 }
143 /* BUG代码,其他地方都没问题,删除第一个节点将出错,不过在写这个之前我已经
144 想到了,所以用了二级指针的实现方式,还是有进步O(∩_∩)O哈哈~
145 
146 void Del_data1(Mylist* list, listtyp del_value)
147 {
148     Mylist *pre = NULL;
149     Mylist *sav_pre = list;
150     while (sav_pre != NULL && sav_pre->value != del_value)
151     {
152         pre = sav_pre;//保存前一个节点
153         sav_pre = sav_pre->next;
154     }
155     if (sav_pre->value == del_value)
156     {
157         if (sav_pre == (list))//while没有进入,首元素就要删除
158         {
159             //(*list) =sav_pre->next;//和下面一个道理
160             (list) = (list)->next;
161         }
162         else
163         {
164             pre->next = sav_pre->next;
165             printf("delete:%d
", del_value);
166         }
167         free(sav_pre);//sav_pre被删除,故释放其内存,但之后不能再访问,否则内存出错、
168     }
169 }
170 
171 */
172 /*因为上面的代码删除第一个节点时,root会被释放,而后面的打印函数需要root指向第一个节点
173   所以用二级指针,这样可以在函数中改变root的指向,并且释放删除节点的内存
174 */
175 void Del_data(Mylist** list, listtyp del_value)
176 {
177     Mylist *pre = NULL;
178     Mylist *sav_pre = *list;
179     while (sav_pre != NULL && sav_pre->value != del_value)
180     {
181         pre = sav_pre;//保存前一个节点
182         sav_pre = sav_pre->next;
183     }
184     if (sav_pre == NULL)
185     {
186         printf("链表中没有你想删除的数据。
");
187     }
188     else
189     {
190         if (sav_pre->value == del_value)
191         {
192             if (sav_pre == (*list))//while没有进入,首元素就要删除
193             {
194                 //(*list) =sav_pre->next;//和下面一个道理
195                 (*list) = (*list)->next;
196             }
197             else
198             {
199                 pre->next = sav_pre->next;
200             }
201             printf("delete:%d
", del_value);
202             free(sav_pre);//sav_pre被删除,故释放其内存,但之后不能再访问,否则内存出错、
203         }
204     }
205 }
206 
207 int search(Mylist* list, listtyp element)
208 {
209     int toll = 0;
210     int no_or_have = 0;
211     Mylist *s = list;
212     while (s->next!=NULL)//记录一共有多少数据
213     {
214         toll++;
215         s = s->next;
216     }
217     Mylist *pos = list;
218     int count = 0;
219     while (pos != NULL && pos->value != element)//放哨兵
220     {
221         count++;
222         pos = pos->next;
223     }
224     if (pos == NULL)
225     {
226         printf("没有找到查询的值
");
227     }
228     else
229     {
230         if (pos != list)//不是一开头就找到
231         {
232 
233             printf("在第%d节点处找到
", count + 1);
234             count++;
235             //因为上面找到的那个也要占位置,比如上面在2节点处找到了
236             //但是我此时位置还是在2节点,要将其跳过因为,如果在2节点找到,count
237             //并没有计数,所以这里手动加上1
238             while (pos->next != NULL)
239             {
240                 if (pos->next->value == element)
241                 {
242                     //这里的if判断不能写成if(pos->value==element),
243                     //因为此时pos还是在找的节点处,要跳过该节点
244                     //故写成    if (pos->next->value == element)
245                     printf("在第%d节点处找到
", count + 1);
246                 }
247                 count++;
248                 pos = pos->next;
249 
250             }
251 
252         }
253         else//一开始就找到
254         {
255 
256             printf("在第%d节点处找到
", count + 1);
257             count++;//因为上面找到的那个也要占位置
258 
259             while (pos->next != NULL)
260             {
261                 if (pos->next->value == element)
262                 {
263                     printf("在第%d节点处找到
", count + 1);
264                 }
265                 count++;
266                 pos = pos->next;
267 
268             }
269         }
270     }
271     return 1;
272 }
View Code

这里面的讲究很多。

简要说明,因为大部分都已经有注释。先说插入算法,参考c和指针,经过两个过程,最终实现了一个相对最好的插入程序,再说删除,因为有了上面插入的提示,如果我在删除头结点的时候,会改变root指针,但是我的打印函数print必须要是root指针,自然想到了也用二级指针去del数据,还有就是插入分为有序插入和无序直接插入,有这两个模板应该可以完成大部分插入的要求,再说查找函数,我们要能够查询到一个多次出现的数据,但是删除我是删除的第一个遇到的数据,这也就是需要我们程序员根据需要更改或者优化的地方了,到底是删除第一次遇到的还是只要是这个数据都删除。

NOTE:

其中困扰我很久的一个问题就是链表内存请用堆空间,否则销毁链表free的时候会抛down,明明快要收工的时候,却发现free就失败,最后发现我main函数中的第一个开头点没有用malloc申请空间,改了之后就好了。

插入可以有序可以无序还可以按照你的算法进行插入。

查询函数,一定要可以查询所有存在的数据。

删除函数,根据以后具体业务需要,进行改进,到底是删除全部还是删除第一次遇到的还是删除第N此遇到的,所以删除函数需要根据业务需要进行更改。

二级指针还是必须熟练掌握的。

插入函数和查找函数需要重点掌握,时间久了可能会忘记,备注是个好习惯。

原文地址:https://www.cnblogs.com/yangguang-it/p/6650001.html