前言
链表的维护方式有两种,一种是以定义一个头节点的方式来维护链表,此头节点也分为指针域和数据域,作为维护链表的头节点使用时我们不使用它的数据域,只使用指针域;还有一种维护方式是直接定义一个头指针,这个指针指向链表的第一个节点。此处文采用第二种维护方式来实现一条单向链表,即所有的操作都是单向的,只能从前往后的进行操作
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
typedef int DataType;
//此结构体不同于顺序表,顺序表中结构体便是顺序表,而此处的结构体则表示的是一个节点
typedef struct Node
{
DataType data; //数据域
struct Node *next; //指针域(存放下一个节点的地址,如果为NULL,则代表后面再无节点)
}Node;
void SListInit(Node **ppfirst); //链表的初始化
void SListPrint(Node* first); //打印
void SListDestroy(Node **ppfirst); //链表销毁
void SListPushBack(Node **ppfirst, DataType data); //尾插
void SListPopBack(Node **ppfirst); //尾删
void SListPushFront(Node **ppfirst,DataType data);//头插
void SListPopFront(Node **ppfirst);//头删
int SListGetLength(Node *first);//链表长度计算
Node* SListFind(Node* first, DataType data);//查找链表元素
void SListInsert(Node** ppfirst, int pos, DataType data);//在节点pos之前插入新的元素
void SListErase(Node** ppfirst, int pos);//删除pos这个节点
SList.c
链表的初始化
1 | void SListInit(Node **ppfirst) //二级指针ppfirst里放的是实参结构体指针first的地址,first里放的是结构体的地址 |
此处需要注意接收实参的是一个二级指针,这是因为实参是通过传址的方式带入到函数内部的,为什么要通过址的方式呢?
链表的销毁
释放每一个节点,并将头指针置为空,使用两个指针,一个用来记录下个节点的位置,一个用来删除节点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void SListDestroy(Node **ppfirst)
{
assert(ppfirst);
Node* cur = NULL; //记录下一个节点的位置
Node* del = NULL; //删除节点
cur = *ppfirst;
while (cur)
{
del = cur;
cur = cur->next;
free(del);
del = NULL;
}
*ppfirst = NULL;
}
新节点的建立
1 | Node* creatNode(DataType d) |
链表打印
1 | void SListPrint(Node* first) |
尾插
找到指针域为空的节点,将其指针域保存为新加节点的地址即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20void SListPushBack(Node **ppfirst, DataType d)
{
assert(ppfirst);
Node *newNode = creatNode(d);
//无节点
if (*ppfirst == NULL)
{
*ppfirst = newNode;
}
//有节点
else
{
Node* cur = *ppfirst;
while (cur->next)
{
cur = cur->next;
}
cur->next = newNode;
}
}
尾删
找到链表中倒数第二个元素,将其指针域指为NULL,同时释放掉最后一个节点,注意分类讨论不同情况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
26void SListPopBack(Node **ppfirst)
{
assert(ppfirst);
//空链表
if (*ppfirst == NULL)
{
printf("没有节点可以删除\n");
return;
}
else if ((*ppfirst)->next == NULL) //只有一个节点
{
free(*ppfirst);
*ppfirst = NULL;
}
//两个以上节点
else
{
Node* cur = *ppfirst;
while (cur->next->next)
{
cur = cur->next;
}
free(cur->next);
cur -> next = NULL;
}
}
头插
新节点的指针域保存原链表首节点的地址,头指针指向加入的新节点1
2
3
4
5
6
7
8
9
10
11
12void SListPushFront(Node **ppfirst, DataType d)
{
assert(ppfirst);
Node* newNode = creatNode(d);
if (*ppfirst == NULL)
{
*ppfirst = newNode;
return;
}
newNode->next = *ppfirst;
*ppfirst = newNode;
}
头删
使头指针保存第二个节点地址,销毁第一个节点1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21void SListPopFront(Node **ppfirst)
{
assert(ppfirst);
if (*ppfirst == NULL)
{
printf(" 空链表,无可删除元素\n");
return;
}
else if ((*ppfirst)->next == NULL)
{
free(*ppfirst);
*ppfirst = NULL;
return;
}
else
{
Node* del = *ppfirst;
*ppfirst = (*ppfirst)->next;
free(del);
}
}
求链表长度
1 | int SListGetLength(Node *first) |
查找某个指定的元素
此处指定的是具体的元素数字,返回的是该元素所在的地址1
2
3
4
5
6
7
8
9
10Node* SListFind(Node* first, DataType data)
{
Node* cur = first;
for (cur = first; cur != NULL; cur = cur->next)
{
if (cur->data == data)
return cur;
}
return NULL;
}
在查找到的元素前面插入一个新的元素
三种情况,分类讨论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
29void SListInsert(Node** ppfirst, Node* pos, DataType data)
{
assert(ppfirst);
if (*ppfirst == NULL)
{
printf("链表为空,不进行插入\n");
return ;
}
Node* newNode = creatNode(data);
if (*ppfirst == pos) //pos为链表头部
{
newNode->next = pos;
*ppfirst = newNode;
}
else //pos为一般位置
{
Node* cur = *ppfirst;
while (cur != NULL && cur->next != pos )
{
cur = cur->next;
}
if (cur)
{
newNode->next = pos;
cur->next = newNode;
}
}
}
删除查找的元素
此处是直接删除该元素1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void SListErase(Node** ppfirst, Node* pos)
{
assert(ppfirst);
assert(*ppfirst); //链表为空则不进行删除
if (*ppfirst == pos) //删除的位置为链表的头部
{
Node* del = *ppfirst;
*ppfirst = ((*ppfirst)->next);
free(*ppfirst);
}
else //一般删除位置
{
Node* cur = *ppfirst;
while (cur && cur->next != pos)
{
cur = cur->next;
}
cur->next = pos->next;
free(pos);
}
}
测试函数test.c
1 |
|