1.创建双向链表节点 LTNode* CreateLTNode(LTDataType x); 2.初始化双向循环链表 LTNode* LTInit(); 3.打印双向循环链表 void LTPrint(LTNode* phead); 4.循环双向链表尾插 void LTPushBack(LTNode* phead, LTDataType x); 5.双向循环链表中删除尾节点 void LTPopBack(LTNode* phead); 6.双向链表的头插操作 void LTPushFront(LTNode* phead, LTDataType x); 7.双向链表的头部删除操作 void LTPopFront(LTNode* phead); 8.循环链表中查找指定值节点 LTNode* LTFind(LTNode* phead, LTDataType x); 9.该函双向链表中指定节点pos的前面插入一个新的节点 void LTInsert(LTNode* pos, LTDataType x); 10.双向链表中删除某个节 void LTErase(LTNode* pos); 11.销毁一个循环双向链表 void LTDestroy(LTNode * phead);
函数输入参数为节点的值x,函数返回一个指向节点的指针。 函数内部实现:
LTNode* CreateLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
链表中的每个节点都是LTNode类型的结构体,其中包含一个指向前一个节点的指针prev和一个指向后一个节点的指针next。该函数首先创建一个值为-1的头节点,并将头节点的前一个节点和后一个节点都指向头节点本身,以形成一个空的双向循环链表。最后返回头节点的指针。
LTNode* LTInit()
{
LTNode* phead = CreateLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
其参数为双向循环链表的头结点指针,函数内部会从头结点开始遍历链表,并依次打印每个节点的值,直到遍历到头结点为止。最终输出的内容是形如“哨兵位<=>x<=>y<=>z<=>哨兵位”的字符串,其中x、y、z分别表示链表中的元素值。
void LTPrint(LTNode* phead)
{
assert(phead);
printf("哨兵位<=>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<=>", cur->val);
cur = cur->next;
}
printf("\n");
}
将一个元素x插入到链表的最后一个节点的后面。 函数接收两个参数,一个是指向链表头结点的指针phead,另一个是要插入到链表尾部的元素x。 首先使用assert函数检查参数phead是否为NULL,如果是则直接终止程序。 接着定义两个指针tail和newnode,tail指向链表的最后一个节点,newnode是要插入到链表尾部的新节点。 然后将新节点插入到链表尾部。具体步骤如下:
这样就完成了在链表尾部插入新节点的操作。
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* newnode = CreateLTNode(x);
// phead tail newnode
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
}
具体分析如下:
注意,该函数的前提条件是链表中至少存在一个节点,否则会因为assert函数判断失败而终止程序。在使用该函数时需要注意链表的状态。
void LTPopBack(LTNode* phead)
{
assert(phead);
// 空
assert(phead->next != phead);
LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;
}
将一个新节点插入到链表的第一个位置。 输入参数:
函数流程:
注意事项:
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = CreateLTNode(x);
LTNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;
}
1. 首先使用assert函数判断传入的链表头结点指针phead是否为空,如果为空则终止程序运行。 2. 再通过assert函数判断链表是否为空,即头结点的下一个结点是否还是头结点自身,如果是则说明链表为空,同样终止程序运行。 3. 取出链表头结点的下一个结点first,以及第二个结点second。 4. 将头结点的下一个结点指向第二个结点,同时将第二个结点的前一个结点指向头结点,完成删除操作。 5. 最后使用free函数释放被删除的结点first的内存空间,并将first指针置为空。
void LTPopFront(LTNode* phead)
{
assert(phead);
// 空
assert(phead->next != phead);
LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
first = NULL;
}
参数说明:
函数实现步骤:
需要注意的是,该函数是针对循环链表的查找实现,因此需要判断 cur 指针是否回到了头节点 phead,如果回到了头节点,则表示遍历完整个链表,需要退出循环。
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
节点的值为x。函数实现需要注意以下几点:
// 在pos前面的插入
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* newnode = CreateLTNode(x);
// posprev newnode pos
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
输入参数是要删除的节点指针pos。 首先通过断言语句assert(pos)来检查输入参数是否为空。 然后通过pos指针找到它的前驱节点posPrev和后继节点posNext,将它们之间的连接断开,即将posPrev的next指针指向posNext,将posNext的prev指针指向posPrev。 最后通过free函数释放pos指向的内存空间,完成删除操作。
// 删除pos位置
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posNext = pos->next;
LTNode* posPrev = pos->prev;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
}
参数phead是链表的头指针,其指向一个LTNode类型的结构体,该结构体中有两个指针,分别指向链表的头节点和尾节点。 首先,函数中使用了断言assert(phead),判断参数phead是否为空指针,如果是,程序会终止运行,有助于在调用函数时发现错误。 然后,定义一个指针cur指向链表第一个节点,然后使用while循环遍历除了头节点之外的所有节点,直到遍历完所有节点为止。 在循环中,使用一个指针next指向当前节点的下一个节点,然后释放当前节点的内存空间,最后将cur指向下一个节点。 循环结束后,释放链表头节点的内存空间,销毁整个链表。
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
}
1.循环双向链表尾插操作
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead, x);
}
2. 双向链表的头插操作
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTInsert(phead->next, x);
}
1.双向循环链表中删除尾节点
void LTPopBack(LTNode* phead)
{
assert(phead);
LTErase(phead->prev);
}
2. 双向链表的头部删除操作
void LTPopFront(LTNode* phead)
{
assert(phead);
// 空
assert(phead->next != phead);
LTErase(phead->next);
}
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType val;
}LTNode;
LTNode* CreateLTNode(LTDataType x);
LTNode* LTInit();
void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDataType x);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);
void LTDestroy(LTNode * phead);
#include"List.h"
LTNode* CreateLTNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
LTNode* LTInit()
{
LTNode* phead = CreateLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPrint(LTNode* phead)
{
assert(phead);
printf("哨兵位<=>");
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<=>", cur->val);
cur = cur->next;
}
printf("\n");
}
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
//LTNode* tail = phead->prev;
//LTNode* newnode = CreateLTNode(x);
phead tail newnode
//tail->next = newnode;
//newnode->prev = tail;
//newnode->next = phead;
//phead->prev = newnode;
LTInsert(phead, x);
}
void LTPopBack(LTNode* phead)
{
assert(phead);
// 空
assert(phead->next != phead);
/*LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;*/
LTErase(phead->prev);
}
//void LTPushFront(LTNode* phead, LTDataType x)
//{
// assert(phead);
// LTNode* newnode = CreateLTNode(x);
//
// newnode->next = phead->next;
// phead->next->prev = newnode;
//
// phead->next = newnode;
// newnode->prev = phead;
//
//}
void LTPushFront(LTNode* phead, LTDataType x)
{
/*assert(phead);
LTNode* newnode = CreateLTNode(x);
LTNode* first = phead->next;
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;*/
LTInsert(phead->next, x);
}
void LTPopFront(LTNode* phead)
{
assert(phead);
// 空
assert(phead->next != phead);
/*LTNode* first = phead->next;
LTNode* second = first->next;
phead->next = second;
second->prev = phead;
free(first);
first = NULL;*/
LTErase(phead->next);
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// 在pos前面的插入
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* posPrev = pos->prev;
LTNode* newnode = CreateLTNode(x);
// posprev newnode pos
posPrev->next = newnode;
newnode->prev = posPrev;
newnode->next = pos;
pos->prev = newnode;
}
// 删除pos位置
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posNext = pos->next;
LTNode* posPrev = pos->prev;
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
//pos = NULL;
}
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
//phead = NULL;
}
#include "List.h"
void TestList1()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 5);
LTPushBack(plist, 4);
LTPrint(plist);
LTPushFront(plist, 10);
LTPrint(plist);
}
void TestList2()
{
LTNode* plist = LTInit();
LTPushFront(plist, 10);
LTPushFront(plist, 20);
LTPushFront(plist, 30);
LTPushFront(plist, 40);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
//LTPopFront(plist);
//LTPrint(plist);
}
void TestList3()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 5);
LTPushBack(plist, 4);
LTPrint(plist);
LTNode* pos = LTFind(plist, 3);
if (pos)
{
pos->val *= 10;
}
LTPrint(plist);
LTInsert(pos, 30000);
LTPrint(plist);
LTInsert(plist, -1);
LTPrint(plist);
LTInsert(plist, -2);
LTPrint(plist);
}
void TestList4()
{
LTNode* plist = LTInit();
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPushBack(plist, 5);
LTPushBack(plist, 4);
LTPrint(plist);
LTNode* pos = LTFind(plist, 3);
if (pos)
{
LTErase(pos);
pos = NULL;
}
LTPrint(plist);
LTDestroy(plist);
plist = NULL;
}
int main()
{
TestList4();
return 0;
}
带头双向循环链表的优点:
带头双向循环链表的缺点:
总的来说,带头双向循环链表在需要频繁对链表进行插入和删除操作时,以及需要实现无限循环链表时非常有用。但是相比于单向链表,需要额外维护一个指向前驱节点的指针,同时实现也较为复杂。