前言
此处介绍一种以头节点为维护方式的双向循环链表,这也是在日常生活工作中使用比较频繁的一种链表结构。因为此种链表是双向的且循环,首尾相连,所以在对链表进行一些操作的时候和单向链表相比较就会方便很多。
链表的基本结构(图示)
next指针和单链表相似,由头节点开始,依次指向下一个节点,不同之处在于链表最后一个节点的指针指向头节点;prev指针称为前驱,他总是指向当前节点的上一个节点,头节点的prev指针指向最后一个节点。所以在双向循环链表中可以通过头节点的prev指针找到链表的最后的一个节点,也正是这个优点让双向循环链表变得方便简单
双向循环链表节点的定义
1 | typedef int DataType; |
链表的初始化
不同于单向链表的初始化,双向链表的初始化是指链表中只有头节点这一个节点,故也可以说是头节点的初始化.头节点中的数据域我们是不用的,所以也可以在头节点的数据域中存放一些关于该链表的信息
使两个指针都指向他自己,在这里注意不同于单链表的头指针维护方式,这里我们需要先创建一个节点来当头节点,借此来维护整个链表1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17Node *creatNode(DataType data)
{
Node *node = (Node *)malloc(sizeof(Node));
node->data = data;
node->next = node->prev = NULL;
return node;
}
//初始化头节点
Node *InitDSList(Node **psHead)
{
assert(psHead != NULL);
Node *newNode = creatNode(0); //此处0没实际用处,可以当成是size来记录链表插入了多少个节点
newNode->next = newNode;
newNode->prev = newNode;
*psHead = newNode;
}
链表的清空
释放链表中的所有节点,除了头节点,后将头节点指针初始化1
2
3
4
5
6
7
8
9
10
11
12
13//清空链表(保留头节点)
void ClearDSList(Node *head)
{
Node *del = NULL;
Node *cur = head->next;
while (cur != head)
{
del = cur;
cur = cur->next;
free(del);
}
head->next = head->prev = head;
}
销毁链表
包括头节点在内全部释放1
2
3
4
5
6//销毁链表
void DestroyDSList(Node *head)
{
ClearDSList(head);
free(head);
}
任意位置插入新的节点
1 | // 插入到 pos 的前面 (pos一定在链表中) |
头插
1 | //头插 |
尾插
1 | //尾插 |
任意位置节点删除
1 | //删除节点pos pos是链表中的任意一个结点(头结点) |
头删
1 | void PopFront(Node *head) |
尾删
1 | void PopBack(Node *head) |
测试代码及运行结果
1 | void test() |
运行结果: