目录
前言
即顺序表之后,今天给大家带来另外一个线性结构——链表。
1.链表的概念及结构
![](https://i-blog.csdnimg.cn/blog_migrate/f788ac0b76fbf7f718c2399b4e8833e7.png)
2.链表的实现
2.1 准备工作
和顺序表一样,这里我们准备三个文件
Slist.c——对链表相关功能的实现
Slist.h——进行有关函数的声明
Text.c——对有关功能的实现
2.2 链表结构声明
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
这里我们用data存储相关内存,用结构体指针next存储下一个节点的位置。
2.3 动态节点的申请
由于链表是通过一个一个节点在逻辑上连续实现的,所以创建节点是我们必须需要的操作,这里我们就将其封装成一个函数,以便每次的使用
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
2.4 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode*newnode= BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail=*pplist;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
这里我们采用的是无哨兵位的单链表,所以头指针为空时我们直接将头指针指向新节点,如果头指针后有节点我们就用tail指针找到尾节点,然后让尾节点的next指向新节点实现插入,还有一点是这里我们要传二级指针,由于形参是对实参的复制,所以我们要改变头节点时,只传一级指针时,并不可以改变头节点的指向,比如
此时头指针s还是指向NULL,我们只是把形参这个指针指向了新节点而已,并没有改变链表结构。
对于具体的尾插操作这里我给大家实际演示一下:
2.5 打印函数的实现
实现打印函数,以便我们更好的测试我们所实现的函数
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur)
{
printf("%d-> ", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
这里解释一下为什么传的是一级指针,由于我们这里只是打印链表,对链表的头节点不进行改变,所以这里我们直接传一级指针即可。
接下来我们就对我们的后插进行测试:
void text3()
{
SListNode* s = NULL;
SListPushBack(&s, 1);
SListPushBack(&s, 2);
SListPushBack(&s, 3);
SListPushBack(&s, 4);
SListPrint(s);
}
int main()
{
text3();
return 0;
}
结果展示:
2.6 单链表尾删
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
if ((*pplist)->next==NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* tail = *pplist;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
这里我给大家解释一下上面的两个断言,首先我们二级指针存储的是单链表头节点的地址,所以该不可能为空,其次由于是进行删除操作,所以单链表不可能为空,所以解引用后,该是不可能为空指针的,所以我们要对该进行断言操作。
其次这里的删除一共有两种情况:
第一是头指针后只有一个节点:
这里我们只需要把头指针给释放掉就行。
第二种情况就是头指针后不止一个节点,这里我们如果只使用一个指针对其进行删除操作,那么我们只需要找到尾节点的头一个节点,然后通过next释放掉尾节点,将尾节next置空,具体如下
还有一种我们可以使用两个指针,一个保存尾节点,一个保存尾节点的头一个节点 ,然后释放尾节点,对尾节点头一个节点的next进行置空,具体如下:
这里代码就不给大家演示,大家可以自己写一写看。
测试如下:
void text3()
{
SListNode* s = NULL;
SListPushBack(&s, 1);
SListPushBack(&s, 2);
SListPushBack(&s, 3);
SListPushBack(&s, 4);
SListPrint(s);
SListPopBack(&s);
SListPopBack(&s);
SListPrint(s);
}
int main()
{
text3();
return 0;
}
结果演示:
2.7 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
对于头插这里我们进行的操作就比较简单了,首先向内存创建一个节点,然后新节点的next指向头节点,然后把头指针指向新节点,具体如下:
第一步:
第二步:
测试如下:
void text2()
{
SListNode* s = NULL;
SListPushFront(&s, 1);
SListPushFront(&s, 2);
SListPushFront(&s, 3);
SListPushFront(&s, 4);
SListPrint(s);
}
int main()
{
text2();
return 0;
}
结果如下:
2.8 单链表头删
void SListPopFront(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
SListNode* first = *pplist;
*pplist = first->next;
free(first);
first = NULL;
}
对于头删,这里我们需要用一个指针记录头节点位置,然后把头指针指向头节点的下一个节点,然后把头节点释放了即可,具体如下:
这样我们就完成了头删。
测试如下:
void text2()
{
SListNode* s = NULL;
SListPushFront(&s, 1);
SListPushFront(&s, 2);
SListPushFront(&s, 3);
SListPushFront(&s, 4);
SListPrint(s);
SListPopFront(&s);
SListPrint(s);
SListPopFront(&s);
SListPrint(s);
}
int main()
{
text2();
return 0;
}
结果如下:
2.9 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
if (plist == NULL)
{
return NULL;
}
SListNode* dest=plist;
while (dest)
{
if (dest->data == x)
{
return dest;
}
dest = dest->next;
}
return NULL;
}
这里我们还是以内容查找,思路很简单,就是遍历,找到后返回找到的那个节点,没找到返回空指针即可。由于这里我们不好测试,我们配合下面的在某个位置插入给大家演示。
2.10 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
SListNode* newnode=BuySListNode(x);
assert(pos);
newnode->next = pos->next;
pos->next = newnode;
}
这里我们需要配合查找找到需要插入的元素,这里的逻辑也比较简单,我给大家用演示一下
测试如下:
void text1()
{
SListNode* s = NULL;
SListPushBack(&s, 1);
SListPushBack(&s, 2);
SListPushBack(&s, 3);
SListPushBack(&s, 4);
SListPrint(s);
SLTDateType x = 0;
scanf("%d", &x);
SListNode* pos = SListFind(s, x);
SListInsertAfter(pos, 6);
SListPrint(s);
}
int main()
{
text1();
return 0;
}
结果如下:
2.11 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
if (pos == NULL&&pos->next)
{
return;
}
else
{
SListNode* find;
find = pos->next;
pos->next = pos->next->next;
free(find);
find = NULL;
}
}
首先判断该位置和下一个位置是不是空,为空直接退出程序,这里我们用指针find记录pos下一个节点,然后将pos的next指向下下个节点即可。
具体如下:
测试如下:
void text1()
{
SListNode* s = NULL;
SListPushBack(&s, 1);
SListPushBack(&s, 2);
SListPushBack(&s, 3);
SListPushBack(&s, 4);
SListPrint(s);
SLTDateType x = 0;
scanf("%d", &x);
SListNode* pos = SListFind(s, x);
SListInsertAfter(pos, 6);
SListPrint(s);
SListNode*pos1= SListFind(s, x);
SListEraseAfter(pos1);
SListPrint(s);
}
int main()
{
text1();
return 0;
}
结果如下:
2.12 单链表的销毁
void SListDestroy(SListNode* plist)
{
SListNode* p = plist;
SListNode* q;
while (p)
{
q = p->next;
free(p);
p = q;
}
plist = NULL;
}
对于销毁操作,我们需要使用双指针,用q记录要销毁节点的下一个,p用来销毁指针,然后不断对链表进行遍历。
总代码
Slist.c
#include"slist.h"
SListNode* BuySListNode(SLTDateType x)
{
SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
if (newnode == NULL)
{
perror("malloc fail");
return NULL;
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SListPrint(SListNode* plist)
{
SListNode* cur = plist;
while (cur)
{
printf("%d-> ", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void SListPushBack(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode*newnode= BuySListNode(x);
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
SListNode* tail=*pplist;
while (tail->next)
{
tail = tail->next;
}
tail->next = newnode;
}
}
void SListPushFront(SListNode** pplist, SLTDateType x)
{
assert(pplist);
SListNode* newnode = BuySListNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
void SListPopBack(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
if ((*pplist)->next==NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SListNode* tail = *pplist;
while (tail->next->next)
{
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SListPopFront(SListNode** pplist)
{
assert(pplist);
assert(*pplist);
SListNode* first = *pplist;
*pplist = first->next;
free(first);
first = NULL;
}
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
if (plist == NULL)
{
return NULL;
}
SListNode* dest=plist;
while (dest)
{
if (dest->data == x)
{
return dest;
}
dest = dest->next;
}
return NULL;
}
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
SListNode* newnode=BuySListNode(x);
assert(pos);
newnode->next = pos->next;
pos->next = newnode;
}
void SListEraseAfter(SListNode* pos)
{
if (pos == NULL&&pos->next)
{
return;
}
else
{
SListNode* find;
find = pos->next;
pos->next = pos->next->next;
free(find);
find = NULL;
}
}
void SListDestroy(SListNode* plist)
{
SListNode* p = plist;
SListNode* q;
while (p)
{
q = p->next;
free(p);
p = q;
}
plist = NULL;
}
Slist.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
SLTDateType data;
struct SListNode* next;
}SListNode;
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode* plist);
Text.c
#include"slist.h"
void text3()
{
SListNode* s = NULL;
SListPushBack(&s, 1);
SListPushBack(&s, 2);
SListPushBack(&s, 3);
SListPushBack(&s, 4);
SListPrint(s);
SListPopBack(&s);
SListPopBack(&s);
SListPrint(s);
}
void text2()
{
SListNode* s = NULL;
SListPushFront(&s, 1);
SListPushFront(&s, 2);
SListPushFront(&s, 3);
SListPushFront(&s, 4);
SListPrint(s);
SListPopFront(&s);
SListPrint(s);
SListPopFront(&s);
SListPrint(s);
}
void text1()
{
SListNode* s = NULL;
SListPushBack(&s, 1);
SListPushBack(&s, 2);
SListPushBack(&s, 3);
SListPushBack(&s, 4);
SListPrint(s);
SLTDateType x = 0;
scanf("%d", &x);
SListNode* pos = SListFind(s, x);
SListInsertAfter(pos, 6);
SListPrint(s);
SListNode*pos1= SListFind(s, x);
SListEraseAfter(pos1);
SListPrint(s);
}
int main()
{
text1();
return 0;
}