双向链表所需要头文件

首先重定义类型名 意义我前几篇讲过几次了,这里就不在赘述了,(顺序表,单链表的开头都有说明)

 

然后我们需要一个结构体

结构体包含 : 存储数据的 a 指向一个节点的指针 next 指向上一个节点的指针 prve

双向链表实现的函数功能有 :1.申请动态内存空间 2.初始化 3.双向链表的头,尾的插入与删除,在合法的任意位置进行插入和删除 4.释放链表,5.打印链表

1.申请动态内存空间

因为进行插入和初始化需要用到此函数 所以我们要先实现此功能

1.先申请一个动态内存,并名为newnode

2.把x存入newnode存放数据的a中

3.把newnode的上,下指针都置为空

4.最后再返回此申请的空间

2.初始化

我们先创建一个链表的头 并且把上,下指针置空,并且返回链表的头

并且我们再命名一个plist,用来接收,之后穿参数的时候,就可以直接用pilst

 3.双向链表的尾部插入

1.先创建一个指针 指向头的一个节点

2.申请一块内存 用来插入

因为双向链表的优势 ,我们不需要像单链表那样依次遍历找到最后一个节点,双向链表,表头,的上一个就是链表的最后一个节点

所以我们可以一步就找到表尾

尾插入原理:

找到表尾后,1.在表尾后再插入一个新数据(用next指针指向newnode(新数据)),2.把新数据指向原先
的表尾(用prve指向原先的表尾)就完成了一次的连接

1.再让现在的表尾 (就是newnode(新数据))指向表头(newnode->next=phead(表头))  2.(再让表头指向上一个的指针
(就是prve)指向newnode(新数据))     

这里可能会有点绕,需要读者慢慢理解,但是原理是简单的 ,就是让1个数据与它的上一个数据和下一个数据进行对接

4.双向链表的尾部删除

 原理:

1.找到表尾,再找到表尾的上一个数据(倒数第二个数据)

2.然后让倒数第二个数据与表头对接,倒数第二个数据就成为了表尾数据,

3.再把原先的表尾数据进行释放

end就是最后一个节点,cur就是倒数第二个节点

1.让cur指向下一个的指针指向表头 

2.把表头的指向上一个的指针指向cur

3.释放原先的表尾,并且把指针置空

 读者要搞清楚 next 和prve的作用 再搞清楚end和cur的位置,最后运用他们进行对接

5.双向链表的头部插入

我们先找到两个位置 一个是表头 一个是表头的下一个节点(
这个节点才是真正存放数据的节点,表头只是其带头作用,里面只是存放0,并且打印链表都是从表头的一个节点开始打印)

找到两个位置之后,创建一个动态内存 进行插入

接下来大致原理就是 让插入的数据与表头和表头的下一个节点,在他们两个中间插入(就是对接)

表头原先指向的是表头的下一个节点,1.现在我们让表头的指向下一个节点的指针指向插入的数据

 2.让插入的数据指向上一个节点的指针指向表头  3.让插入的数据指向下一个节点的指针指向原先表头的下一个节点(进行对接)

6.双向链表的头部删除

头部删除也是删除表头的下一个节点

那么我们要找到两个位置 1.表头,2.表头的下一个节点的下一个节点(就是要删除的位置的下一个节点)

这里我们把表头命名为cur 把表头的下一个节点的下一个节点(就是要删除的位置的下一个节点)命名为end

1.然后我们让 cur和end进行对接

 2.然后释放要删除数据

把cur指向下一个节点的指向指向end 把end指向上一个节点的指针指向cur

7.双向链表的查找

 这里我们只需要从表头的下一个节点开始,一直到表尾,进行遍历,如果找到就返回改位置。如果知道表尾都没找到(就是循环结束的时候)那么我们就返回空(NULL)

然后我们用pos来接收 

 7.对pos的前一个位置进行插入数据

我们首先需要先对pos进行判断,如果找到了,才开始插入,若找不到,就不执行函数

因为是插入数据 所以我们要先申请一个内存

又因为我们已经得到了pos的位置,所以我们可以直接找到pos的上一个数据

原理在于找两个位置 一个是pos 一个是pos的上一个数据

pos我们已经有了它的位置 pos的上一个位置我们只需要用指针指向它就可以了

pos的上一个位置命名为end

然后让新数据在pos的上一个数据与pos中间进行插入(就是对接) 

8.对pos这个位置进行删除

 因为我们有了pos的位置

那么我们用指针 next和prve找到 pos的上一个位置(命名为cur),和pos的下一个位置(end)进行对接

然后再释放掉pos,为把pos置空

 9.链表的打印

从表头的下一个数据开始,进行遍历,并依次打印

10.链表的释放 

因为链表的内存是一块块申请的,所以我们要遍历,并且释放

先从找到表头的下一个节点(命名为end),再找到表头的下一个节点的下一个节点(命名为cur)

先释放表头的下一个节点,再把cur赋值给end,就此循环。就把表头以后的数据全释放完了,

最后把表头给释放,并且置空,就可以了。

双向链表的全部函数功能就实现好了!

这里添加一个注释

之所以双向链表传参不用(&)取地址 是因为SL* plist=ListCreate();
这里是用指针接收,指针接收的就是地址,我们传参数的时候就是用地址传入了。

不用二级指针是因为我们不需要改变这个双向链表的哨兵位,只是改变它的指向,这个哨兵位是不会变的(不管头插、尾插、头删、尾删)

而单链表的头插,头删是会改变这个头的,比如(头删)释放这个头,让下一个节点成为头,这些都是会改变头的,所以我们要传二级指针。

接下来是代码段 ,需要自取。
#include <stdio.h> #include <stdlib.h> #include <assert.h> typedef int
STDataType; typedef struct linode { STDataType a; struct linode* next; struct
linode* prve; }SL; SL* Bylist(STDataType x)//申请动态内存 { SL* newnode =
(SL*)malloc(sizeof(SL)); newnode->a = x; newnode->next = NULL; newnode->prve =
NULL; return newnode; } SL* ListCreate()//初始化 { SL* phead = Bylist(0);
phead->next = phead; phead->prve = phead; return phead; } void ListDestory(SL*
pHead)//释放链表 { SL* end = pHead->next; while (end != pHead) { SL* cur =
end->next; free(end); end = cur; } free(pHead); pHead = NULL; } void
ListPrint(SL* pHead)//打印链表 { SL* end = pHead->next; while (end != pHead) {
printf("%d->", end->a); end = end->next; } printf("phead"); } void
ListPushBack(SL* pHead, STDataType x)//尾插 { SL* end = pHead->prve; SL* newnode
= Bylist(x); //因为双向链表的优势 所以我们不需要依次遍历,头的上一个就是最后一个节点 end->next = newnode;
newnode->prve = end; newnode->next = pHead; pHead->prve = newnode; } void
ListPopBack(SL* pHead)//尾删 { SL* end = pHead->prve;//找到最后一个节点 SL* cur =
end->prve;//最后一个节点的上一个 cur->next = pHead; pHead->prve = cur; free(end); end =
NULL; } void ListPushFront(SL* pHead, STDataType x)//头插 { SL* end = pHead; SL*
cur = end->next; SL* newnode = Bylist(x); end->next = newnode; newnode->prve =
end; newnode->next = cur; cur->prve = newnode; } void ListPopFront(SL*
pHead)//头删 { SL* cur = pHead->next; SL* end = cur->next; pHead->next = end;
end->prve = pHead; free(cur); } SL* ListFind(SL* pHead, STDataType x)//查找 { SL*
cur = pHead->next; while (cur != pHead) { if (cur->a == x) { return cur; } cur
= cur->next; } return NULL; } void ListInsert(SL* pos, STDataType x)//插入 { SL*
newnode = Bylist(x); SL* end = pos->prve; end->next = newnode; newnode->prve =
end; newnode->next = pos; pos->prve = newnode; } void ListErase(SL* pos)//删除 {
SL* cur = pos->prve; SL* end = pos->next; cur->next = end; end->prve = cur;
free(pos); pos - NULL; } void Intenode() { // 创建返回链表的头结点. SL* plist=
ListCreate(); // 双向链表尾插 ListPushBack(plist, 1); ListPushBack(plist, 2);
ListPushBack(plist, 3); ListPushBack(plist, 4); // 双向链表尾删 ListPopBack(plist);
// 双向链表头插 ListPushFront(plist, 5); ListPushFront(plist, 6);
ListPushFront(plist, 7); ListPushFront(plist, 8); // 双向链表头删
ListPopFront(plist); // 双向链表查找 SL* pos=ListFind(plist, 5); if (pos) { //
双向链表在pos的前面进行插入 ListInsert(pos, 50); } // 双向链表删除pos位置的节点 ListErase(pos); // //
双向链表打印 ListPrint(plist); // // 双向链表销毁 ListDestory(plist); }

技术
下载桌面版
GitHub
Gitee
SourceForge
百度网盘(提取码:draw)
云服务器优惠
华为云优惠券
腾讯云优惠券
阿里云优惠券
Vultr优惠券
站点信息
问题反馈
邮箱:[email protected]
吐槽一下
QQ群:766591547
关注微信