
带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了。

typedef int LTDataType; //类型可以根据需求更改 更方便
typedef struct ListNode
{
struct ListNode* prve; //指向前一个节点
struct ListNode* next; //指向下一个节点
LTDataType data; //节点元素
}LTNode;
因为是带头双向循环链表,所以先创建返回链表的头节点哨兵位
//创建返回链表的头节点哨兵位
LTNode* LTCreate()
{
LTNode* head = malloc(sizeof(LTNode)); //在堆上开辟一块哨兵位节点空间
if (head == NULL)
{
perror(" LTCreate malloc");
exit(-1);
}
head->data = -1; //可以给上-1
head->prve = head; //前指针指向自己
head->next = head; //后指针指向自己
return head;
}

用后头节点之后我们可以开始创建节点进行尾插:
//双链表尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL) //是否开辟成功
{
perror("malloc fail");
exit(-1);
}
newnode->data = x; // 给值
newnode->next = NULL; //新的节点 指针可以置空 在连接时指向正确的位置
newnode->prve = NULL;
LTNode* tail = phead->prve; //因为是尾插 双向循环链表哨兵位的前指针指向最后一个节点
//很容易就找最后一个节点,不需要像单链表去遍历查找
tail->next = newnode; //最后一个节点的next指向新节点
newnode->prve = tail; //新节点的prve指向最后一个节点
newnode->next = phead; //此时新节点已经是最后一个节点了
// 处理一下新的尾节点的next 新节点的next指向哨兵位
phead->prve = newnode; //最后将头节点的prve指向新的节点
}

掌握了尾插之后,实现一下头插:
LTNode* BuyNewNode(LTDataType x) //封装的创建新节点函数
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail");
exit(-1);
}
node->data = x;
node->next = NULL;
node->prve = NULL;
return node;
}
//双向链表头插
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyNewNode(x); //因为需要头插尾插 将创建新节点封装一下
//新增头插节点,其实就跟尾插相似,但是需要注意,新节点必须先链接哨兵位的next的节点
newnode->next = phead->next;
phead->next->prve = newnode;
newnode->prve = phead;
phead->next = newnode;
//如果容易出错,可以保存哨兵位的next的节点,如下
//LTNode* nextnode = head->next;
//newnode->next = nextnode;
//nextnode->prve = newnode;
//newnode->prve = phead;
//phead->next = newnode;
}

单链表的头插比尾插效率高,而双向链表的头插和尾插效率相同。
实现尾删与头删
bool Empty(LTNode* plist)
{
assert(plist);
return plist->next == plist;//哨兵位的next如果指向哨兵位,则为空链表
}
//双链表尾删
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(!Empty(phead)); //需要判断链表不为空才能进行操作,创建一个判断函数
LTNode* tail = phead->prve; //找到需要删除的尾节点
tail->prve->next = phead; //尾节点的前一个节点链接头节点
phead->prve = tail->prve; //哨兵位的prve指针指向尾节点的前一个节点
free(tail); //释放
tail = NULL;
}

//双向链表头删
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!Empty(phead)); //判断链表不为空
LTNode* cur = phead->next; //保存删除的节点
phead->next->next->prve = phead;
phead->next = cur->next;
free(cur);
cur = NULL;
}

尾插和头插,尾删和头删,在双向循环链表中效率相同。
实现Find函数:
//双向链表查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* node = phead->next; //遍历查找传参的节点
while (node != phead)
{
if (node->data == x)
{
return node;
}
node = node->next;
}
return NULL;
}
接下来创建配合Find函数使用的INSERT函数,根据查找的POS位置,在POS位置前插入新节点:
//双向链表在pos的前面插入
void LTInsert(LTNode* pos, LTDataType x)
{ //使用find函数查找
assert(pos);
LTNode* newnode = BuyNewNode(x); //创建新节点
LTNode* cur = pos->prve; //找到pos的前一个节点
//插入新节点,将新节点与前后节点指针进行链接
cur->next = newnode;
newnode->prve = cur;
newnode->next = pos;
pos->prve = newnode;
}
再利用Find,创建Erase函数,删除Pos位置节点;
//双向链表删除pos位置节点
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* prve = pos->prve; //找到pos的前一个节点
LTNode* next = pos->next; //找到pos的后一个节点
prve->next = next; //前后节点链接
next->prve = prve;
free(pos); //删除pos位置
pos = NULL;
}
最后创建一个DESTROY函数,结束程序前释放空间
//双向链表销毁
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* node = cur->next;
free(cur);
cur = NULL;
cur = node;
}
free(phead);
}
---------------------------------------------------------------------------------------------------------------------------------
下面是完整代码:
//List.h
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* prve;
struct ListNode* next;
LTDataType data;
}LTNode;
//创建返回链表的头节点哨兵位
LTNode* LTCreate();
//双向链表销毁
void LTDestroy(LTNode* plist);
//双向链表打印
void LTPrint(LTNode* plist);
//双向链表尾插
void LTPushBack(LTNode* plist, LTDataType x);
//双向链表尾删
void LTPopBack(LTNode* plist);
//双向链表头插
void LTPushFront(LTNode* plist, LTDataType x);
//双向链表头删
void LTPopFront(LTNode* plist);
//双向链表查找
LTNode* LTFind(LTNode* plist, LTDataType x);
//双向链表在pos的前面插入
void LTInsert(LTNode* pos, LTDataType x);
//双向链表删除pos位置节点
void LTErase(LTNode* pos);
//List.c
#include "List.h"
//创建返回链表的头节点哨兵位
LTNode* LTCreate()
{
LTNode* head = malloc(sizeof(LTNode));
if (head == NULL)
{
perror(" LTCreate malloc");
exit(-1);
}
head->data = -1;
head->prve = head;
head->next = head;
return head;
}
//双链表打印
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* tail = phead->next;
printf("<=head=>");
while (tail != phead)
{
printf("%d<=>", tail->data);
tail = tail->next;
}
printf("\n");
}
LTNode* BuyNewNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail");
exit(-1);
}
node->data = x;
node->next = NULL;
node->prve = NULL;
return node;
}
//双链表尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyNewNode(x);
LTNode* tail = phead->prve;
tail->next = newnode;
newnode->prve = tail;
newnode->next = phead;
phead->prve = newnode;
}
bool Empty(LTNode* plist)
{
assert(plist);
return plist->next != plist;
}
//双链表尾删
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(Empty(phead));
LTNode* tail = phead->prve;
tail->prve->next = phead;
phead->prve = tail->prve;
free(tail);
tail = NULL;
}
//双向链表头插
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyNewNode(x);
newnode->next = phead->next;
phead->next->prve = newnode;
newnode->prve = phead;
phead->next = newnode;
}
//双向链表头删
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(Empty(phead));
LTNode* cur = phead->next;
phead->next->next->prve = phead;
phead->next = cur->next;
free(cur);
cur = NULL;
}
//双向链表查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* node = phead->next;
while (node != phead)
{
if (node->data == x)
{
return node;
}
node = node->next;
}
return NULL;
}
//双向链表在pos的前面插入
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = BuyNewNode(x);
LTNode* cur = pos->prve;
cur->next = newnode;
newnode->prve = cur;
newnode->next = pos;
pos->prve = newnode;
}
//双向链表删除pos位置节点
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* prve = pos->prve;
LTNode* next = pos->next;
prve->next = next;
next->prve = prve;
free(pos);
pos = NULL;
}
//双向链表销毁
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* node = cur->next;
free(cur);
cur = NULL;
cur = node;
}
free(phead);
}
1088

被折叠的 条评论
为什么被折叠?



