目录
一、学习目标
二、概念
节点设计:
节点初始化(链表初始化):
节点头插:
插入数据的变形:
遍历显示:
有序插入:
四、总结
一、学习目标
- 知识点:
- 一文掌握数据结构的双向循环链表
- 通过打怪实战来提升自己的理解
二、概念
对链表而言,双向均可遍历是最方便的,另外首尾相连循环遍历也可大大增加链表操作的便捷性。因此,双向循环链表,是在实际运用中是最常见的链表形态。
节点设计:
// 数据域设计
typedef struct DataType
{
int Num ;
char Name[32];
}DataType;
// 节点设计
typedef struct Node
{
// 数据域
DataType Data;
// 指针域
struct Node * Prev ; // 前驱指针
struct Node * Next ; // 后继指针
}Node , *P_Node ;
节点初始化(链表初始化):
P_Node NewNode( DataType * NewData )
{
// 在堆内存中申请一个节点
P_Node New = calloc(1 , sizeof(Node));
// 数据域初始化
if (NewData != NULL)
{
memcpy( &New->Data , NewData , sizeof(DataType) );
}
// 指针域初始化
New->Next = New->Prev = New ;
return New ;
}
节点头插:
void Add2Head(P_Node head , P_Node New )
{
// 中心思想:先对新节点进行操作,然后再操作链表
// 1. 让【新节点】的【后继指针】指向【第一个有效数据节点】
New->Next = head->Next ;
// 2. 让【新节点】的【前驱指针】指向头节点
New->Prev = head ;
// 3. 让链表的【头节点】的后继指针指向新节点
head->Next = New;
// 4. 让【第一个有效数据节点】的前驱指针指向新节点
New->Next->Prev = New ;
return ;
}
插入数据的变形:
在一下示例中该函数即可以用于实现头插也可以实现用于尾插。
中心思想: 当需要把一个新数据插入到链表中的头节点的上一个位置称为尾差,反之插入下一个位置为头插。因此头插与尾插的区别就在于把新数据插入到前方或后方的区别。
得到结论:
把新数据节点插入到目标节点的前方为尾插,因此给一下函数传递的参数就可以写成 Add2List ( 前驱节点 , 新数据 , 目标节点 ) ;
把新数据节点插入到目标节点的后方为头插 , 因此给一下函数传递的参数就可以写成 Add2List ( 目标节点 , 新数据 , 后继节点 ) ;
void Add2List ( P_Node Prev , P_Node New , P_Node Next )
{
Prev->Next = New ;
New->Next = Next ;
Next->Prev = New ;
New->Prev = Prev ;
}
以上操作函数中的4个步骤不再需要关系先后顺序,因为每一个指针都是独立的,在操作过程中不会丢失。
遍历显示:
void DisplayList( P_Node head )
{
for (P_Node tmp = head->Next ; tmp != head ; tmp = tmp->Next)
{
printf("[%d]%s\n" , tmp->Data.Num , tmp->Data.Name);
}
}
有序插入:
void Add2List ( P_Node Prev , P_Node New , P_Node Next )
{
Prev->Next = New ;
New->Next = Next ;
Next->Prev = New ;
New->Prev = Prev ;
}
void Add2ListOrder( P_Node head , P_Node New )
{
P_Node tmp = NULL ;
for (tmp = head->Next ; tmp != head && // 条件1 tmp 不指向头节点
tmp->Data.Num < New->Data.Num ; // 条件2 tmp 的数据小于 新数据的
tmp = tmp->Next ); // tmp 往后遍历
Add2List( tmp->Prev , New , tmp );
}
打怪实战:
-
- 理解现有代码
- 尝试自行编写以上操作
- 尝试实现链表的其他操作,比如增删改查
- 【拓展】链表生成后使用其他关键字进行排序
- 【拓展】思考双向循环链表的通用性
BOOS进阶:
-
- 回顾本周所学,把不懂的标记出来尽快解决
- 尝试自行编写双向循环链表的操作(增删改查从新排序等)
- 【拓展】自行编写双向链表的通用性操作
- 【拓展】 假设有两个双向循环链表L1,L2尝试编写一个函数用于合并他们,可以尝试完成一下两种合并需求:
- 直接把L2连接到L1的尾部
- 把L2有序地合并到L1中
四、总结
本文介绍了数据结构的双向循环链表的基础概念和操作,理解本文所有知识点后,便可打败其中的小怪,拿下经验值~
本文参照 粤嵌文哥 部分课件经整理和修改后发布在C站,如有转载,请联系本人