双向循环链表特点
**双向链接:**每个节点都包含两个指针,一个指向前一个节点(前驱节点),另一个指向后一个节点(后继节点)。这种双向连接使得在链表中可以轻松地在两个方向上遍历节点。
**循环性质:**最后一个节点的后继节点指向第一个节点,形成一个环状结构。这意味着可以无限循环遍历链表,因为没有真正的末尾。
**灵活性:**由于双向链接,可以方便地在链表中插入、删除和移动节点,而不需要像单链表那样需要迭代查找前一个节点。
**前向和后向遍历:**可以从任何节点开始,向前或向后遍历整个链表。这对于需要双向遍历的情况非常有用,例如在双向队列(deque)或编辑器中。
**常见应用:**双向循环链表常用于实现循环队列、双向队列、LRU(最近最少使用)缓存算法、文本编辑器的撤销和重做功能等。
哨兵位
数据结构中的"哨兵位"通常指的是一种特殊的元素或节点,它在数据结构中扮演了特殊的角色,通常用于简化算法的实现、边界情况的处理或者提高性能。哨兵位并不包含实际的数据,其主要作用是占据一个特定的位置,以便更容易处理数据结构的边界情况。
C语言代码实现
声明文件
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>typedef int LTDataType;typedef struct ListNode
{struct ListNode* next;struct ListNode* prev;LTDataType data;}LTNode;LTNode* BuyListNode(LTDataType x);
LTNode* ListInit(); //双向链表初始化
void LTPrint(LTNode* phead); //Print void ListPushBack(LTNode* phead, LTDataType x); //尾插
void ListPopBack(LTNode* phead); //尾删void ListPushFront(LTNode* phead, LTDataType x); //头插
void ListPopFront(LTNode* phead); //头删LTNode* LTFind(LTNode* phead, LTDataType x); //查找void LTInsert(LTNode* pos, LTDataType x); //在pos之前插入x
void LTErase(LTNode* pos); //删除pos位置的数据bool LTEmpty(LTNode* phead); //判断双向链表是否为空
size_t LTSize(LTNode* phead); //长度
void LTDestroy(LTNode* phead); //销毁链表
实现文件
#include "List.h"LTNode* BuyListNode(LTDataType x)
{LTNode* node = (LTNode*)malloc(sizeof(LTNode)); if (node == NULL) {perror("malloc fail...");return -1;}node->data = x;node->next = NULL;node->prev = NULL;return node;
}LTNode* ListInit()
{LTNode* phead = BuyListNode(-1);phead->next = phead; phead->prev = phead;return phead;
}void LTPrint(LTNode* phead) // Print double-link list
{assert(phead); // ensure phead is not empty LTNode* cur = phead->next;while (cur != phead){printf("%d <->", cur->data);cur = cur->next;}printf("\n");}void ListPushBack(LTNode* phead, LTDataType x)
{assert(phead); LTNode* newnode = BuyListNode(x);LTNode* tail = phead->prev; newnode->next = phead; newnode->prev = tail;tail->next = newnode; phead->prev = newnode; }void ListPopBack(LTNode* phead)
{assert(phead);assert(phead->next != phead); LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;tailPrev->next = phead;phead->prev = tailPrev;free(tail);
}void ListPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyListNode(x);newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode; newnode->prev = phead;
}void ListPopFront(LTNode* phead) // head deletion
{assert(phead);assert(phead->next != phead);LTNode* cur = phead->next; LTNode* cur_next = cur->next;phead->next = cur_next;cur_next->prev = phead;free(cur); //freeing this node
}LTNode* LTFind(LTNode* phead, LTDataType x) //Find the position of data x
{assert(phead);LTNode* cur = phead->next;while (cur->next != phead){if (cur->data == x) //find{return cur; //return the data's node}cur = cur->next; //if not find, pointer pointing to the next node}return; //not find
}void LTInsert(LTNode* pos, LTDataType x) //在pos之前插入x
{assert(pos);LTNode* newnode = BuyListNode(x); //new node that data is xLTNode* prev = pos->prev;newnode->prev = prev;newnode->next = pos;prev->next = newnode;pos->prev = newnode;
}void LTErase(LTNode* pos) //删除pos位置的数据
{assert(pos);LTNode* prev = pos->prev;LTNode* next = pos->next;free(pos);prev->next = next;next->prev = prev;
}bool LTEmpty(LTNode* phead) //判断双向链表是否为空
{//assert(phead);//if (phead->next == phead)//{// return true;//}//else//{// return false;//}return phead->next == phead;
}size_t LTSize(LTNode* phead) //长度
{assert(phead);size_t counter = 0;LTNode* cur = phead;while (cur != phead){counter++;cur = cur->next;}return counter;
}void LTDestroy(LTNode* phead) //销毁链表
{assert(phead);LTNode* cur = phead;while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);phead = NULL; //这里置空不起作用}
测试代码
#include "List.h"void ListTest_1()
{LTNode* phead = ListInit();ListPushBack(phead, 10);ListPushBack(phead, 20);ListPushBack(phead, 30);ListPushBack(phead, 40);LTPrint(phead); //Print double-link listLTNode* pos = LTFind(phead,30); //Find the position of data if (pos->data == 30){printf("找到了!!");}else{printf("没找到!!");}LTInsert(pos, 100);//ListPopBack(phead);//ListPopBack(phead);//ListPopBack(phead);//ListPopFront(phead); //ListPopFront(phead);//ListPopFront(phead);//ListPopFront(phead);LTErase(pos);LTPrint(phead);
}int main()
{ListTest_1();return 0;
}