1.结构
1.带头双向循环链表
注意:这里的“带头”跟前面我们说的“头节点”是两个概念,实际前面的在单链表阶段称呼不严
谨,但是为了同学们更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨
的”。
“哨兵位”存在的意义:遍历循环链表避免死循环。在这里需要知道的我们在学习单链表的时候学的是没有带头的,只是将第一个节点作为头结点而已,带头的是表示一个不保存数据节点作为第一个节点。
2.双链表的实现
在这里应该有些小伙伴猜到了,我们在在之前学习的顺序表,单链表都是结构体声明,当然双链表也是的。
我们来看看是怎么实现的吧?
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next; //指针保存下⼀个节点的地址
struct ListNode* prev; //指针保存前⼀个节点的地址
LTDataType data;
}LTNode;
我们发现双链表的声明比单链表多了一个东西,一个节点即保存下一个节点的地址又保存上一个节点的地址,当然头结点除外。
当然实现双链表也需要实现一些函数,我们先来看看需要实现的函数,下一篇将展示函数的实现。
在这之前大家可以先研究研究,动手试试看。
LTNode* LTInit();
void LTDestroy(LTNode* phead);
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
LTNode *LTFind(LTNode* phead,LTDataType x);
3. 顺序表和双向链表的优缺点分析
不同点 | 顺序表 | 链表(单链表) |
存储空间上 | 物理上一定连续 | 逻辑上连续,但物理上不一定连续 |
随机访问 | 支持O(1) | 不⽀持:O(N) |
任意位置插入或者删除元素 | 可能需要搬移元素,效率低O(N) | 只需修改指针指向 |
插入 | 动态顺序表,空间不够时需要扩容 | 没有容量的概念 |
应用场景 | 元素高效存储+频繁访问 | 任意位置插入和删除频繁 |