线性表——双向链表

一.什么是双向链表

双向链表也属于线性表中的一种,分为普通双向链表和双向循环链表,结构如下图

双向链表也分为普通双向链表和带头结点的双向链表。普通双向链表就是不带头节点的双向链表,这种双向链表在插入和删除时都要判断头指针是否为空,分支多,很容易出bug;带头结点(也叫哨兵位)的双向链表无论链表是否为空,操作均统一,不易出现问题。

二.双向链表的初始化、增、删、查、改

1.双向链表的初始化

双向链表的空间也分为数据域和指针域,但是指针域要有两个指针,分别时指向上一个结点的指针和指向下一个结点的指针,因此结构应该是这样

复制代码
struct DNode
{
    ElemType data;
    struct DNode* prior;
    struct DNode* next;
};

在头文件声明如下

现在来对双向链表进行初始化,即创建一个不存储任何有效数据的头结点。当然如果不想使用头节点,可以只创建一个头指针,把头指针置为空即可,这里选择带头节点的初始化。

这样就得到了个不存储任何数据的头结点,在头文件声明后我们调试看初始化是否正常

能看到是可以初始化成功的,不过这里有个问题,初始化完成后,头节点的prior和next是野指针,我们需要手动给它们置为空。

2.双向链表的头插操作

上面我们已经初始化了头文件,假设插入的结点指针为pos,头插的时候只需要让pos的prior指向phead,pos的next置等于phead的next,phead的next指向pos,phead的原next的prior也要指向pos,即

用代码实现就是

这里有个问题,如果是第一次插入,那么

复制代码
phead->next->next->prior = temp;

不就越界访问了吗?所以这里要加一个判断语句,如果phead->next不为空,再执行这个语句

同时在头文件声明,为了直观观察每一次操作的变化,先定义一个打印函数

现在来测试一下头插函数

3.双向链表的尾插操作

现在我想在链表的尾部插入一个结点p1,只需要让p1的prior指向尾结点,next指向尾结点的next;最后尾结点的next指向p1即可

用代码实现就是

测试代码运行结果

4.双向链表的查询函数

经过了前面的顺序表和单链表,我们发现,线性表的查询函数几乎一直,都是通过遍历来找到要查询的值的地址,双向链表也如此,传入头指针和要查询的值后,逐个遍历比较。

在头文件声明后进行测试

当要查询的值不存在时返回空,输出的地址就为00000000;存在则正常输出地址。

5.双向链表的指定位置前后插入操作

在指定位置前后插入值,需要我们提前利用查询函数得到目标地址,然后进行插入操作,双向链表此时的优势已经体现出来了,由于双向链表可以随意访问前驱结点或者其后继结点,在插入或者删除数据时十分方便。

进行指定位置前插入其实和头插函数几乎一模一样,进行指定位置的尾插操作时和尾插几乎一模一样,只需要将头指针替换为指针位置的指针即可。

前插

后插

这两个操作很相似,只是改变指针的指向不同,来测试结果

测试通过

6.双向链表的删除操作

直接单独做头删或尾删或指定位置删与前面的顺序表和单链表过于相似,因此这里选择删除想要删除的值,若值不存在则返回false,若删除成功则返回true。因为是按值删除,所以要遍历链表查找值,直接调用前面定义好的查询函数,在释放前先完成指针的指向交换,再进行free

同时在头文件声明,来测试代码的可行性

成功将9 21 10等值删除。

那么双向链表的基本操作也就这些了,当然其它操作SunnyByte还在学习中...

相关推荐
jimy16 小时前
C 语言的 static 关键字作用
c语言·开发语言·算法
风筝在晴天搁浅7 小时前
LeetCode 143.重排链表
算法·leetcode·链表
handler017 小时前
算法:图的基本概念
c语言·开发语言·c++·笔记·算法·图论
木木_王7 小时前
嵌入式Linux学习 | 数据结构 (Day03)顺序表与单链表 超详细解析(含 C 语言实现 + 作业 + 避坑指南)
linux·c语言·数据结构·学习
wefg18 小时前
【C语言】用 C 语言实现多态
c语言·开发语言
我不是懒洋洋8 小时前
手写一个B+树:从原理到数据库索引实战
c语言·c++·经验分享
leo__52019 小时前
IEC 104 协议 C 语言实现
c语言·数据库
啧不应该啊20 小时前
Day1 Python 与 C 的类型区别
c语言·开发语言
cen__y21 小时前
Linux07(信号01)
linux·运维·服务器·c语言·开发语言