一日读书一日功,一日不读十日空
书中自有颜如玉,书中自有黄金屋
一、双链表
1、双链表的结构
2、双链表的实现
1)、双向链表中节点的结构定义
2)、初始化函数 LTInit
3)、尾插函数 LTPushBack
4)、头插函数 LTPushFront
5)、尾删函数 LTPopBack
6)、头删函数 LTPopFront
7)、查找函数 LTFind
8)、在指定位置之后插入数据函数 LTInsert
9)、删除指定位置数据函数 LTErase
10)、销毁函数 LTDesTroy
二、双链表完整代码
三、顺序表和链表的优缺点对比
四、完结撒❀
前言
学习前先思考3个问题:
1.顺序表和链表的关系是什么? | |
---|---|
2.链表的分类有哪些? | |
3.顺序表和链表的优缺点有哪些? |
–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–❀–
1.顺序表和链表的关系是什么
我们之前学习了“顺序表”,“单链表”。
链表和顺序表都是线性表。
线性表是指
逻辑结构:一定是线性的。
物理结构:不一定是线性的。
物理结构是指表在内存中开辟的空间结构,顺序表的物理结构是连续的,而链表的物理结构是不连续的,但它们的逻辑结构都是连续的。
2.链表的分类有哪些?
链表根据带头或者不带头,单向或者双向,循环或者不循环一共分为8种。
我们之前所学的单链表全名是叫:不带头单向不循环链表,而现在要学习的双链表是叫带头双向循环链表。
双链表:
掌握单链表和双链表对于其他链表的实现也就不那么困难了。
3.顺序表和链表的优缺点有哪些?
这里涉及到顺序表和链表的对比,先讲解双向链表,这放到博客末尾为大家对比讲解
一、双链表
1、双链表的结构
注意:这里的“带头”跟前面我们说的“头节点”是两个概念。
带有节点里的头节点实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”
“哨兵位”存在的意义:
遍历循环链表避免出现死循环。
2、双链表的实现
对于双向链表的实现,我们依然使用List.h,List.c,test.c,三个文件进行实现。
1)、双向链表中节点的结构定义
上面我们简单介绍过双链表,其全名为:带头双向循环链表。
带头:指链表中带有哨兵位。
双向:双链表的每个节点内含有两个链表指针变量,分别指向前一个节点和后一个节点,所以就可以通过一个节点找到这个节点前后的两个节点。
循环:链表中的每个节点互相连接,最后一个节点与哨兵位相连构成一个环,整体逻辑结构可以进行循环操作。
代码如下:
//定义双向链表中节点的结构
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prev;
LTDataType data;
struct ListNode* next;
}LTNode;
这里将结构体进行了重命名为LTNode。
2)、初始化函数 LTInit
在创建双链表中的哨兵位时我们需要对其进行初始化,防止意料之外的情况发生。
根据所传形参的类型不同,我们有两种写法
代码如下:
方案1
//方案1
void LTInit(LTNode** pphead)
{
(*pphead) = (LTNode*)malloc(sizeof(LTNode));
if (*pphead == NULL)
{
perror("mallic:");
exit(1);
}
(*pphead)->data = -1;
(*pphead)->prev = (*pphead)->next = *pphead;
}
方案2
LTNode* LTBuyNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc:");
exit(1);
}
newnode->data = x;
newnode->next = newnode->prev = newnode;
return newnode;
}
//方案2
LTNode* LTInit()
{
LTNode* phead = LTBuyNode(-1);
return phead;
}
方案2里面包含了节点空间申请的函数,只是简单的创建双链表的节点,这里就不展开讲解了。
3)、尾插函数 LTPushBack
老规矩,我们开始实现管理链表数据的函数,这里讲的是头插。
假如我们要在链表中尾插一个6,那么我们是需要先创建一个节点来存储6,下面分两步:
1.将6的节点里面前(prev)后(next)链表指针变量对应与原链表的尾节点d3和哨兵位head进行连接
2.将原链表尾节点d3的后链表指针变量(next)指向6的节点,再将哨兵位head的前链表指针变量(prev)指向6的节点
完成上面两部就实现了节点的插入。
代码如下:
//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnod