橘生淮南

记录学习中的点点滴滴

0%

《数据结构》C语言版——单链表学习笔记

链表中的数据是以结点来表示的,每个结点的构成:**元素(数据元素的映象) + 指针(指示后继元素存储位置)**,元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

●头结点是在链表的开始结点之前附加的一个结点(结点没有有效的数据元素)
●第一个结点(或称首元结点)是链表中存储第一个有效数据元素的结点

在链表这里,小萌新想说一定要把’’指针的指向,谁是谁的前驱结点,谁是谁的后继结点‘’搞清楚,多画图,慢慢领会!

话不多说,把代码敲起来!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
#include <stdio.h>
#include <malloc.h>
#include <assert.h>//断言——如果条件返回错误,则终止程序执行

typedef int ElemType;//定义元素类型为int型,即为存放整型数据的单链表

/*给出结点结构*/
typedef struct ListNode
{
ElemType data;//数据域
struct ListNode *next;//指针域(指向下一个结点的地址)
}ListNode,*pNode;//定义结点类型,定义结点的指针类型

/*给出单链表结构*/
typedef struct List
{
pNode first;//first指向头结点(单链表中有效结点的前一个结点,即头结点)
pNode last;//last指向最后一个结点(尾结点)
size_t size;//结点的个数
}List;//定义结构体变量


/*初始化单链表*/
void InitList(List *list)
{
list->first=list->last=(ListNode*)malloc(sizeof(ListNode));//申请一个结点,同时用指针first和指针last指向这个结点
assert(list->first!=NULL);//判断申请空间是否为空
list->first->next=NULL;//头结点的指针域初始化为空(头结点的指针域存放第一个有效结点的地址)
list->size=0;//初始化有效结点个数为0个
}

/*尾部插入*/
void push_back(List *list,ElemType x)
{
ListNode *s=(ListNode*)malloc(sizeof(ListNode));//申请一个结点,用指针s指向这个结点(用指针s保存这个结点的地址)
assert(s!=NULL);//判断是否申请空间成功
s->data=x;//x存放在新结点的数据域
s->next=NULL;//新结点的指针域为空
//链接
list->last->next=s;
//list->last代表最后一个结点,list->last->next代表最后一个结点的指针域,该指针域保存指针s(即保存新结点地址)
list->last=s;//last指针后移,用last指针代替s指针指向最后一个结点(即新插入的结点)
list->size++;//插入后,结点数增加
}

/*显示单链表*/
void show_list(List *list)
{
ListNode *p=list->first->next;//定义指针p指向单链表中的第一个有效结点(注意不是头结点)
while(p!=NULL)//如果结点不为空
{
printf("%d-->",p->data);//打印该结点的数据域
p=p->next;//p指向下一个结点
}
printf("该结点已空\n");
}

/*头部插入*/
void push_front(List *list,ElemType x)
{
ListNode *s=(ListNode*)malloc(sizeof(ListNode));
assert(s!=NULL);
s->data=x;//x存放在新结点的数据域
s->next=list->first->next;//新结点指针域保存第一个有效结点的地址(本来第一个有效结点的地址是让头结点的next保存的)
list->first->next=s;//用头结点的指针域保存指针s(即新结点的地址)
if(list->size==0)//如果此时只有头结点,当插入第一个有效结点时
{
list->last=s;//则更改last指针,让其指向第一个有效结点
}
list->size++;//插入后,结点数增加
}

/*尾删*/
void pop_back(List *list)
{
if(list->size==0)//除头结点外,一个有效结点都没有,无法进行删除操作
return;
ListNode *p=list->first;//定义一个指针p指向头结点
while(p->next!=list->last)//当头结点后(即list->first->next)不是尾结点时
p=p->next;//p指向下一个结点
free(list->last);//当头结点后是尾结点时,释放尾结点
list->last=p;//此时指针last指向p指针所指的结点
list->last->next=NULL;//last结点的指针域置为空
list->size--;//删除后,结点数减少
}

/*头删*/
void pop_front(List *list)
{
if(list->size==0)//除头结点外,一个有效结点都没有
return;
ListNode *p=list->first->next;//定义一个指针p指向头结点后第一个有效结点
list->first->next=p->next;//头结点的后继结点是第二个有效结点(即p->next),头结点的指针域保存第二个有效结点的地址
free(p);//释放p指针所指的结点,实现头删
if(list->size==1)//如果此时只有头结点和最后一个结点,删除last结点时,需要把last指针指向头结点
{
list->last=list->first;
}
list->size--;//删除后,结点数减少
}

/*按值插入(从小到大)*/
void insert_val(List *list,ElemType x)
{
//申请要插入的数据
ListNode *s=(ListNode*)malloc(sizeof(ListNode));//申请一个结点,用指针s指向这个结点(指针s保存了这个结点的地址)
assert(s!=NULL);//判断是否申请空间成功
s->data=x;//x存放在新结点的数据域
s->next=NULL;//新结点的指针域为空

ListNode *p=list->first;//定义一个p指针指向头结点
while(p->next!=NULL && p->next->data < x)//当头结点有后继结点并且后继结点的数据域小于要插入的数值
p=p->next;//p指向下一个结点
if(p->next==NULL)//如果头结点后无其他结点
{
list->last=s;//把last指针指向新的结点
}
s->next=p->next;//否则新结点的指针域保存第一个有效结点的地址(本来第一个有效结点的地址是让头结点的next保存的)
p->next=s;//头结点的指针域保存新结点的地址,实现插入
list->size++;
}

/*按值查找*/
ListNode* find(List *list,ElemType x)
{
ListNode *p=list->first->next;//定义一个指针p指向头结点后第一个有效结点(即头结点的后继)
while(p!=NULL && p->data!=x)//如果后继结点不为空并且结点的数据域不为要查找的数值
p=p->next;//则p指向下一个结点
return p;//返回当前结点的地址
}

/*单链表长度*/
int length(List *list)
{
return list->size;
}

/*按值删除*/
void dele_val(List *list,ElemType x)
{
if(list->size==0)//除头结点外,一个有效结点都没有
return;
ListNode *p=find(list,x);//定义指针p保存find函数返回的当前结点的地址
if(p==NULL)//如果结点的地址为空
{
printf("要删除的数据不存在\n");
return;
}
if(p==list->last)//如果结点地址正好是尾结点
{
pop_back(list);//通过pop函数进行尾部删除
}
else//否则
{
ListNode *q=p->next;//定义指针q指向当前结点(要删除数据的结点)的后继结点
p->data=q->data;//用下一个结点的数据域覆盖当前要删除结点的数据域
p->next=q->next;//删除当前要删除结点的下一个结点(p->next=p->next->next)
free(q);//释放要删除数据结点的后继结点
list->size--;
}
}

/*排序*/
void sort(List *list)
{
if(list->size==0 || list->size==1)//只有头结点或只有头尾两个结点
return;
ListNode *s=list->first->next;//定义一个指针s指向头结点的后继结点
ListNode *q=s->next;//定义指针q指向s指针指向结点的后继结点
//断开链表
list->last=s;//last指针指向s
list->last->next=NULL;//尾结点的指针域为空
//按值插入
while(q!=NULL)//判断指针q指向s指针指向结点的后继结点是否为空
{
s=q;//若不为空,指针s下移结点(指针s指向断开链表后的第一个结点)
q=q->next;//q指针下移(指针q指向s指向断开链表后的第一个结点的后继)
ListNode *p=list->first;//定义一个p指针指向头结点
while(p->next!=NULL && p->next->data < s->data)//当头结点有后继结点并且后继结点的数据域小于断开链表后的第一个结点
p=p->next;//p指向下一个结点
if(p->next==NULL)//如果p所指结点的next为空,则此时p指向尾结点,相当于尾插(更改last指针的指向)
{
list->last=s;
}
s->next=p->next;//否则断开的结点在头结点与第一个有效结点之间比较,若不满足while,则进行插入
//指针s指向那个断开的结点,断开的结点的指针域保存第一个有效结点的地址
p->next=s;//头结点的指针域保存断开结点的地址
//此时断开的结点成为第一个有效结点,实现数据域从小到大排序
}
}

/*清除单链表中的数据*/
void clear(List *list)
{
if(list->size==0)//只有头结点不需要清除
return;
ListNode *p=list->first->next;//定义指针p指向第一个有效结点
while(p!=NULL)
{
list->first->next=p->next;//头结点后为第二个有效结点
free(p);//释放第一个有效结点
p=list->first->next;//p重新指向第一个有效结点(是刚才的第二个有效结点)
}
list->last=list->first;//while完毕后,链表中只剩头结点,将尾指针指向头结点
list->size=0;//结点个数置为0
}

/*摧毁单链表*/
void destory(List *list)
{
clear(list);
free(list->first);//释放头结点
list->first=list->last=NULL;//预防野指针,都置为空
}

void main()
{
List mylist;//定义一个名为mylist的单链表,内部有三个成员,均为随机值
InitList(&mylist);//初始化mylist单链表,&为地址传递
ElemType item;//定义插入的元素
ListNode *p=NULL;
int select=1;
while(select)
{
printf("***************************************************\n");
printf("* [1]push_back(尾插) [2]push_front(头插) *\n");
printf("* [3]show_list(显示) [4]pop_back(尾删) *\n");
printf("* [5]pop_front(头删) [6]insert_val(按值插)*\n");
printf("* [7]find(查找) [8]length(求长度) *\n");
printf("* [9]dele_val(按值删除) [10]sort(排序) *\n");
printf("* [11]clear(清除) [12]destory(销毁) *\n");
printf("* [0]quit(退出系统) *\n");
printf("***************************************************\n");
printf("请选择:");
scanf("%d",&select);
if(select==0)
break;//退出系统

switch(select)
{
case 1:
printf("请输入要插入的数据(-1结束):");
while(scanf("%d",&item),item!=-1)//逗号表达式,item!=-1时插入
{
push_back(&mylist,item);
}break;

case 2:
printf("请输入要插入的数据(-1结束):");
while(scanf("%d",&item),item!=-1)
{
push_front(&mylist,item);
}break;

case 3:
show_list(&mylist);
break;

case 4:
pop_back(&mylist);
break;

case 5:
pop_front(&mylist);
break;

case 6:
printf("请输入要插入的数据:");
scanf("%d",&item);
insert_val(&mylist,item);
break;

case 7:
printf("请输入要查找的数据:");
scanf("%d",&item);
p=find(&mylist,item);
if(p==NULL)
{
printf("要查找的数据在链表中不存在\n");
}
break;

case 8:
printf("单链表的长度为:%d\n",length(&mylist));
break;

case 9:
printf("请输入要删除的数据:");
scanf("%d",&item);
dele_val(&mylist,item);
break;

case 10:
sort(&mylist);
break;

case 11:
clear(&mylist);
break;

case 12:
destory(&mylist);
break;

default:
printf("输入的选择错误,请重新输入:\n");
break;
}
}
}

运行结果:
在这里插入图片描述

欢迎关注我的其它发布渠道

-------------本文结束感谢您的阅读-------------