单链表完整版

实现单链表的头插头删,尾插尾删,中间插入,中间删除,查找

slist.h

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
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<stdlib.h>
typedef int slistdate;//对int重命名,只会方便把int改成double或其他数据类型

struct slistnode
{
slistdate date;//
struct slistnode*next;//指针地址,指向下一个节点
};
typedef struct slistnode slistnode;//方便写

//可能会改变链表的头指针就传二级指针
void slistpushback(slistnode**pphead, slistdate x);//尾插

void slistpushfront(slistnode**pphead, slistdate x);//头插
//不会改变链表的头指针,就传一级指针
void slistprint(slistnode*phead);

void slistpopback(slistnode**pphead);//尾删
void slistpopfront(slistnode**pphead);//头删

slistnode* slistnodefind(slistnode*pphead, slistdate x);//找某一个节点
void slistinsert(slistnode**pphead, slistnode* pos,slistdate x);//插入一个节点

void slisterase(slistnode**pphead, slistnode* pos, slistdate x);

slist.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
#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"


void slistprint(slistnode*phead)
{
slistnode*cur = phead;
while (cur != NULL)//遍历是cur不等于空就往下走,走到尾部还不等于空,走到下一个节点,是空的
{
printf("%d->", cur->date);
cur = cur->next;//cur指向下一个指针
}
printf("NULL\n");
}


//开辟节点做的事情很多次,所以直接用一个函数去做开辟节点的事情就可以了

slistnode*buynode(slistdate x)
{
slistnode*newnode = (slistnode*)malloc(sizeof(slistnode));// 要尾插就要动态开辟一个节点出来
newnode->date = x;//将newnode初始化
newnode->next = NULL;//next赋值为空
return newnode;
}


void slistpushback(slistnode**pphead, slistdate x)
{
slistnode*newnode = buynode(x);
//那么我们这个时候要找尾
//找到尾节点的指针
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
slistnode*tail = *pphead;//我们要让tail走到尾部去,而非走到空
while (tail->next != NULL)
{
tail = tail->next;
}//找到了尾节点,链接新节点
tail->next=newnode;
}
}


void slistpushfront(slistnode**pphead, slistdate x)
{
//头插
//也要malloc出来一个节点
slistnode*newnode = buynode(x);
newnode->next = *pphead;//指向头节点,随后要让phead存第一个节点的地址
*pphead = newnode;//phead存入newnode地址,phead作为头节点,就把newnode当作了头节点
}




//尾删要分3种情况分析
//1.没有节点
//2.一个节点
//3.多个节点
void slistpopback(slistnode**pphead)//尾删
{

//1.想删除就找到那个节点,free掉就可以了,因为都是malloc出来的
//但是,如果将tail找到了那个尾节点,直接free掉,那么前一个节点的指针就会变成野指针,它的指针仍有存在值,但是没有指向的目标,
//因此,我们要找到它的前一个节点,再定义一个prev,作为尾节点找到最后一个节点,他的前一个节点

//1.没有节点,即*pphead没有指向,就直接返回不用删除
if (*pphead == NULL)
{
return;
}
//只有一个节点
else if ((*pphead)->next == NULL)//由于*和->的优先级相同,所以为了不报错,就应该把*pphead用括号包含
{
//直接free掉8
free(*pphead);
*pphead = NULL;//再将*pphead置成空指针,防止它变成野指针
}
//有多个节点
else
{
//先定义tail用来找尾,prev找尾前一个节点
slistnode*tail = *pphead;
slistnode*prev = NULL;//prev置成空指针
//找尾节点
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
//此时prev就是尾节点
free(tail);
prev->next = NULL;
}
}


void slistpopfront(slistnode**pphead)//头删
{
//先保存它下一个指针,next
slistnode*next = (*pphead)->next;//next保存第二个节点的地址,之后将其作为第一个节点
//删除头节点
free(*pphead);
*pphead = next;
}

slistnode* slistnodefind(slistnode*pphead, slistdate x)//找某一个节点
{
slistnode*cur = pphead;
while (cur)
{
if (cur->date == x)//找到了要找的数据
{
return cur;//就把cur的数据返回
}
cur = cur->next;
}
return NULL;//找节点,节点是slistnode*的数据,因此返回值也应该要是slistnode*来接收,没找到,就返回空指针
}

void slistinsert(slistnode**pphead, slistnode* pos, slistdate x)//插入一个节点//插入一个节点
{
//要插入一个节点,首先就是要先malloc一个节点
//有头插的可能如果pos指向第一个节点,又要插入一个节点,就相当于头插,
if (pos == *pphead)
{
slistpushfront(pphead, 30);
}

slistnode *newnode = buynode(30);
//pos的前一个节点的next指向newnode,newnode—>next=pos
//要用一个prev来找到pos的前一个
slistnode*prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
//找到了
prev->next = newnode;
newnode->next = pos;
}

//删除pos处的位置,要让前一个指向后一个,
void slisterase(slistnode**pphead, slistnode* pos, slistdate x)
{
slistnode* prev = *pphead;
//假如pos是在头部,那么就找不到它的前一个

if (pos == *pphead)
{
//相当于头删
slistpopfront(pphead);
}
//也要找到前一个,前一个的next指向后一个
else
{
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}

test.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
#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"
//链表的概念及结构
//链表是一种物理存储结构上非连续,非顺序的存储结构,数据元素的逻辑结构是通过链表中的指针来链接


void testslist()
{
slistnode*plist = NULL;//一开始定义一个指针,但啥都没有,所以先赋值空指针,作为头节点,存入第一个节点的地址
slistpushback(&plist, 1);//尾插//也要传地址才可以,实参传给形参,形参是实参的临时拷贝
slistpushback(&plist, 2);
slistpushback(&plist, 3);
slistpushback(&plist, 4);
slistpushfront(&plist, 0);//头插
slistpopback(&plist);
slistpopfront(&plist);//头删
slistnodefind(plist, 3);//找某一个节点
slistnode*pos = slistnodefind(plist, 3);//把要插入到位置找出来,pos就是现在指向的位置
if (pos)//pos不等于空才会进入这个里面
{
slistinsert(&plist, pos, 32);//在pos指向的位置插入数据为30的节点
}
slistnode*p = slistnodefind(plist, 2);
if (p)
{
slisterase(&plist, p, 2);
}

slistprint(plist);
}
int main()
{
//需要定义一个指针指向头部
testslist();

return 0;
}

单链表完整版
http://example.com/2021/11/04/单链表完整版/
作者
Zevin
发布于
2021年11月4日
许可协议