目录
1.链表的概念
定义:链表是一种物理储存结构上非连续,非顺序的存储结构,数据元素是依靠各各节点中的指针进行连接,同时链表也是线性表的一种
特点:链表是由一个一个节点相互连接组成,且节点的生成是动态生成的(使用malloc调用空间),通常一个节点会由数据域和指针域两个部分组成
数据域:用于储存数据元素
指针域:用于存储下个一个节点的指针
单链表(不带头不循环单向链表)如下:
链表中的节点:
链表中的节点通常是由结构体的形式组成如图:
typedef struct list { int data;//数据域 struct list* next;//指针域 }LIST;
链表的主要作用 :
链表主要是依靠节点将各个数据连接在一起形成一个表,就好似火车一样,同时每一个表都会有一个节点作为根节点,然后以该节点进行后续节点的增加,插入,删除
链表与数组之间的区别:
1.链表与数组最大的区别在于储存的方式,链表是依靠各各节点将离散的数据连接起来形成表,而数组每次生成都会开辟一块连续的空间进行储存;
2.且链表的数据域可以存放各类自定义数据如结构体,而数组是固定类型,整形数组就全部都是整形;
3.数组有起始地址和结束地址,而链表并没有头尾之分,但为了方便操作链表中的节点的插入和删除,所以定义根节点
2.单链表的创建与尾插增加
1.先创建第一个节点(根节点),同时第一节点的next指向空
2.再次创建一个节点,遍历链表找到尾部的节点,将尾部的节点的指针指向第新的节点,此时第新节点的指针应该指向空
后面的新建节点以此反复便可
3.遍历链表打印
只需要输出该节点的数据域后通过指针到达下一节点,直到节点为空
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct list
{
int data;//数据域
struct list* next;//指针域
}LIST;
//创建一个新节点
LIST* listnewnode()
{
int x;
LIST* newnode = (LIST*)malloc(sizeof(LIST));
if (newnode == NULL)//如果申请失败就报错
{
perror("malloc\n");
exit(1);
}
scanf("%d", &x);
newnode->data = x;//数据域赋值
newnode->next = NULL;//指针域为空
return newnode;//将新节点返回
}
//尾插
void slistback(LIST** phead)
{
assert(phead);//判断phead是否为空
LIST* newnode = listnewnode();//接收新节点
if (*phead == NULL)//判断链表是否为空如果为空则该次获取的节点为根节点
{
*phead = newnode;
}
else
{
LIST* ptail = *phead;
while (ptail->next)//找到尾节点
{
ptail = ptail->next;
}
ptail->next = newnode;//将新节点插入尾部
}
}
//遍历链表
void slistprint(LIST* phead)
{
assert(phead);
while (phead)//只要该节点为空便停止
{
printf("%d->", phead->data);
phead = phead->next;
}
printf("NULL\n");
}
int main()
{
LIST* node = NULL;//创建一个指向结构体的空指针
int a;
scanf("%d", &a);
while (a--)
{
slistback(&node);
}
slistprint(node);
return 0;
}
3.链表节点的查找
直接进行链表的遍历,对比每一个节点的数据域,如果不是那就前往下一个节点,找到则返回该节点,遍历后返回空。查找函数在后续的节点操作会比较常用
LIST* slistfind(LIST** phead)
{
assert(phead && *phead);
int x = 0;
scanf("%d", &x);
LIST* find = *phead;//find代替*phead进行循环
while (find)//为空退出循环
{
if (find->data == x)//对比是否是想找的节点
{
return find;//找到返回该节点
break;
}
find = find->next;//不是就到下一个节点
}
//找不到就返回NULL标识
return NULL;
}
4.链表的删除
调用链表的查找函数,查找到该数后返回该数地址,然后进行删除
注意:在删除的过程中需要创建一个保留删除节点指针域的指针,避免在删除后找不到后续数据
void slistgetdele(LIST** phead)
{
assert(phead && *phead);
LIST* pos = slistfind(phead);//找到该数的地址
LIST* find2 = *phead;
LIST* prev = pos->next;//保存下一个节点
while (find2->data != pos->data)//遍历找到该值
{
find2 = find2->next;
}
free(find2);//释放该节点
find2 = NULL;//该节点至空
*phead = prev;//将下一个的节点接上去
}
5.指定插入新的节点
同样是调用链表查找函数,查到该数返回该地址,进行插入,插在该数的前面或者后面由自己定,
这里我使用在该数后面插入数据。
创建一个新节点和保存插入节点后一位的数据的指针,新节点将其放在要插入的节点后面,再将插入的节点和新节点相连,新的节点的指针域此时应该指向保留后一位节点的指针以此达成插入的目的
注意:如果没有保存后一节点的指针,在将新节点和插入的节点相连后将会找不到后续的节点
void slistgetset(LIST** phead)
{
assert(phead && *phead);
LIST* pos = slistfind(phead);//找到该数
LIST* newnode = listnewnode();//创建新节点
LIST* find1 = *phead;//代替根节点
LIST* find2 = pos->next;//保存下一个节点
while (find1->data != pos->data)//对比数据
{
find1 = find1->next;//不是就前往下一个节点
}
find1->next = newnode;//连接新节点
newnode->next = find2;//新节点指针连接下一个节点
}
6.链表节点的修改
依旧是调用查找函数,找到该数的地址,然后修改数值
void slistalter(LIST** phead)
{
assert(phead && *phead);
LIST* pos = slistfind(phead);//查找该数
LIST* ptail = *phead;//代替根节点
while (ptail->data != pos->data)//对比
{
ptail = ptail->next;//下一个节点
}
scanf("%d", &ptail->data);//修改节点
}
7.销毁链表
创建一个新节点用于保存下一个节点,避免释放前一个节点后找不到后面的节点,最后将指向链表的节点置空
void slistdestroy(LIST** phead)
{
assert(phead && *phead);
LIST* prev = *phead;//代替根节点
while (prev)
{
LIST* next = prev->next;//保存下一个节点
free(prev);//释放前一个节点
prev = next;//保存的下一个节点成为根节点
}
*phead = NULL;//将指向链表的节点设置为空
}
7.双向链表
在学完单链表后就可以接触双链表了,双链表是在单链表的基础上增加了一个指针,可以用于反向遍历同时头尾相连,达成循环一样的效果,双链表形成和功能函数如下:
//头文件 #pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> //自定义类型 typedef int INT; typedef struct list { struct list* prev; INT data; struct list* next; }LIST; //初始化 void listset(LIST**phead); //尾插 void listback(LIST* phead, int x); //头插 void listfront(LIST* phead, int x); //头删 void listprontdel(LIST* phead); //尾删 void listbackdel(LIST* phead); //查找 LIST* lsitfind(LIST* phead, int x); //指定插 void LTInsert(LIST* pos, int x); //指定删 void LTIrase(LIST* pos, int x); //查找修改 void findset(LIST* pos, int x); //打印 void listprint(LIST* phead); //销毁链表 void destory(LIST* phead);
//功能实现的.c文件 //创建一个节点 LIST* Newnode(int x) { LIST* node = (LIST*)malloc(sizeof(LIST)); if (node == NULL) { perror("malloc"); exit(1); } node->data = x; node->prev = node; node->next = node; return node; } //初始化 void listset(LIST** phead) { assert(phead); *phead = Newnode(-1); } //尾插 void listback(LIST* phead, int x) { assert(phead); LIST*newnode= Newnode(x); newnode->prev = phead->prev;//哨兵位的前一位是指向自己的 newnode->next = phead;//下一个指向哨兵位 phead->prev->next = newnode;//这里跳过哨兵位 phead->prev = newnode; } //头插 void listfront(LIST* phead, int x) { assert(phead); LIST* newnode = Newnode(x); newnode->prev = phead; newnode->next = phead->next; phead->next->prev = newnode; phead->next = newnode; } //头删 void listprontdel(LIST* phead) { assert(phead); LIST* del = phead->next; del->next->prev = phead; phead->next = del->next; free(del); del = NULL; } //尾删 void listbackdel(LIST* phead) { assert(phead&&phead->next!=phead); LIST* del = phead->prev; del->prev->next = phead; phead->prev = del->prev; free(del); del = NULL; } //查找 LIST* lsitfind(LIST* phead, int x) { assert(phead); LIST* pcur = phead->next; while (pcur != phead) { if (pcur->data == x) { return pcur; } pcur = pcur->next; } return NULL; } //指定插(pos之前) void LTInsert(LIST* pos, int x) { assert(pos); LIST*newnode= Newnode(x); newnode->next = pos; newnode->prev = pos->prev; pos->prev->next = newnode; pos->prev = newnode; } //指定删 void LTIrase(LIST* pos, int x) { LIST* del = pos; del->prev->next = del->next; del->next->prev = del->prev; free(del); del = NULL; } //查找修改 void findset(LIST* pos, int x) { assert(pos); pos->data = x; } //打印 void listprint(LIST* phead) { LIST* pcur = phead->next;//从哨兵位的下一个节点开始 while (pcur != phead) { printf("%d->",pcur->data); pcur = pcur->next; } printf("\n"); } //销毁链表 void destory(LIST* phead) { assert(phead); LIST* pcur = phead->next; while (pcur != phead) { LIST* next = pcur->next; free(pcur); pcur = next; } free(phead); phead = NULL; }
//功能测试.c文件 void test() { LIST* NEW = NULL; listset(&NEW); printf("头插\n"); listfront(NEW,3); listfront(NEW,4); listfront(NEW,99); listprint(NEW); printf("尾插\n"); listback(NEW, 1); listback(NEW,2); listback(NEW,98); listprint(NEW); printf("尾删和头删\n"); listprontdel(NEW); listbackdel(NEW); listprint(NEW); printf("查找2的位置进行插入55\n"); LIST*pos= lsitfind(NEW,2);//查找返回值接收 if (pos == NULL) { printf("未找到该数\n"); } else { printf("找到该数了\n"); } LTInsert(pos, 55); listprint(NEW); printf("删除2\n"); LTIrase(pos,2); listprint(NEW); printf("将55改成6\n"); pos = lsitfind(NEW, 55); findset(pos, 2); listprint(NEW); destory(NEW); NEW = NULL; } int main() { test(); return 0; }
本章节的内容就到这里结束了,感谢所有能够读到这里的读者,希望本篇对你有用