在数据结构中我们经常会接触到链表,链表也分好多种,带头结点和不带头结点、循环和不循环、单向链表和双向链表
而在众多不同结构的链表中 带头循环双链表可以说是一种最优的链表结构
相比于单链表只能访问当前结点的后续结点的局限性,双链表既可以访问前面结点也可以访问后面结点
其次循环的情况下相比于非循环的链表在访问尾节点时,非循环的链表余姚遍历整个链表才能访问尾节点,而循环链表由于它的循环特性,不用遍历链表,头结点前一个结点就是尾节点,在访问速度上大大优于非循环链表
带头相对于不带头在对链表头插时候不会改变头结点的地址,不带头的每次头插和头删都会改变头指针,而带头结点可以解决这个问题
定义我们双链表结点的结构体,包括三个部分,_next和_prev指针分别指向下一个和前一个结点,_data用来存放数据
typedef int DataType;
typedef struct DListNode//结点
{
struct DListNode* _next;
struct DListNode* _prev;
DataType _data;
}DListNode;
对于双链表的基础操作包括结点的插入,结点的删除,寻找某个结点,打印链表,销毁链表等
DListNode* BuyDListNode(DataType x);//创建新结点
DListNode* DListInit();//双链表初始化
void DListDestory(DListNode* head);//销毁链表
void DListPrint(DListNode* head);//打印链表
void DListPushBack(DListNode* head, DataType x);//尾插
void DListPushFront(DListNode* head, DataType x);//头插
void DListPopBack(DListNode* head);//尾删
void DListPopFront(DListNode* head);//头删
DListNode* DListFind(DListNode* head, DataType x);//寻找值为x的结点,返回它的地址
void DListInsert(DListNode* pos, DataType x);//在pos位置插入值为x的新结点
void DListErase(DListNode* pos);//删除pos位置结点
创建新的结点时用malloc申请空间,注意要判断是否申请成功,同时对_data赋值,对_prev 和_next指针置空,返回新开辟的结点地址
DListNode* BuyDListNode(DataType x)//创建新的结点
{
DListNode* NewNode = (DListNode*)malloc(sizeof(DListNode));
if (NewNode)
{
NewNode->_data = x;
NewNode->_next = NULL;
NewNode->_prev = NULL;
return NewNode;
}
return NULL;
}
带头循环双链表初始化时是初始化链表的 头Head,同样用malloc申请空间,此时链表里面为空,但是由于是循环链表,所以这块对Head->_next和Head->_prev都指向头,也就是自己本身,返回Head的地址
DListNode* DListInit()//链表初始化
{
DListNode* Head = (DListNode*)malloc(sizeof(DListNode));
if (Head)
{
Head->_next = Head;
Head->_prev = Head;
return Head;
}
return NULL;
}
插入操作
void DListPushBack(DListNode* head, DataType x)//尾插
{
DListNode* NewNode = BuyDListNode(x);
DListNode* cur = head;
while (cur->_next != head)
{
cur = cur->_next;
}
cur->_next = NewNode;
NewNode->_prev = cur;
NewNode->_next = head;
head->_prev = NewNode;
}
void DListPushFront(DListNode* head, DataType x)//头插
{
DListNode* NewNode = BuyDListNode(x);
DListNode* cur = head->_next;
head->_next = NewNode;
NewNode->_prev = head;
NewNode->_next = cur;
cur->_prev = NewNode;
}
void DListInsert(DListNode* pos, DataType x)//在pos位置插入值为x的新的结点
{
DListNode* tmp = pos->_prev;
DListNode* NewNode = BuyDListNode(x);
tmp->_next = NewNode;
NewNode->_prev = tmp;
NewNode->_next = pos;
pos->_prev = NewNode;
}
删除操作
void DListPopBack(DListNode* head)//尾删
{
DListNode* cur = head->_prev->_prev;
free(head->_prev);
head->_prev = cur;
cur->_next = head;
}
void DListPopFront(DListNode* head)//头删
{
DListNode* cur = head->_next->_next;
free(head->_next);
head->_next = cur;
cur->_prev = head;
}
void DListErase(DListNode* pos)//删除pos位置结点
{
DListNode* tmp1 = pos->_prev;
DListNode* tmp2 = pos->_next;
tmp1->_next = tmp2;
tmp2->_prev = tmp1;
free(pos);
pos = NULL;
}
寻找值为x的结点
DListNode* DListFind(DListNode* head, DataType x)//寻找值为x的结点,返回它的地址
{
DListNode* tmp = head->_next;
while (tmp->_next != head->_next)
{
if (tmp->_data == x)
{
return tmp;
}
tmp = tmp->_next;
}
return NULL;
}
打印链表
void DListPrint(DListNode* head)//打印链表
{
assert(head);
DListNode* cur = head;
printf("Head->");
while (cur->_next != head)
{
cur = cur->_next;
printf("%d->", cur->_data);
}
printf("Head\n");
}
销毁链表
void DListDestory(DListNode* head)
{
if (head == NULL)
{
return;
}
DListNode* cur1 = head;
DListNode* cur2 = head->_next;
while (cur2)
{
free(cur1);
cur1 = cur2;
cur2 = cur2->_next;
}
}
本文详细介绍了带头循环双链表的结构优势,包括可以方便地访问前后结点以及快速访问尾节点。文章还提供了带头循环双链表的基础操作实现,包括创建、初始化、插入、删除、查找和打印等操作,展示了这种链表结构的灵活性和实用性。
1049

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



