带头双向循环链表是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点,这也是对比单链表所具有的优点。
先定义结构体
typedef struct DSListNode
{
//前驱和后继
struct DSListNode* prev;
struct DSListNode* next;
//数据域
DSLtDataType data;
}DSLTNode;
双向循环链表(仅列举一个结点d1)
prev:the previous 上一个
next:the next 下一个
(其他函数放在文末)
带头就是这种链表比起其他链表带有一个额外的头结点phead(哨兵位);循环就是在头和尾之间有直接的联系,不用遍历链表找尾;双向就是它的前驱和后继可以直接找到前后结点,它的结构可以完美解决顺序表的缺点,这种结构的好处是在尾插尾删和pos位置删除插入的时候都不用像单链表那样过多地去考虑找尾或头和尾丢失的情况,提高程序效率。
//不用循环找尾,phead->prev直接指向尾
DSLTNode* tail = phead->prev
//-----------------头插
void DSLTPushFront(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
DSLTNode* newnode = GetNewNode(x);//获取新节点
DSLTNode* cur = phead;
DSLTNode* next = phead->next;//保存原来的第二个结点防止丢失
cur->next = newnode;//先链接新节点和哨兵位
newnode->prev = cur;
newnode->next = next;//链接新节点和原来的第二个结点
next->prev = newnode;
}
bool DSLTEmpty(DSLTNode* phead)
{
assert(phead);
return phead->next == phead;//只剩哨兵位
}
//----------------头删
void DSLTPopFront(DSLTNode* phead)
{
assert(phead);
assert(!DSLTEmpty(phead));//暴力检查,只剩哨兵位直接结束
DSLTNode* first = phead->next;
DSLTNode* scend = first->next;
//断开phead->next的链接
phead->next = scend;
scend->prev = phead;
free(first);
first = NULL;
}
//---------------------尾插
void DSLTPushBack(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
DSLTNode* tail = phead->prev;//找尾
DSLTNode* newnode = GetNewNode(x);
//链接新结点
tail->next = newnode;
newnode->prev = tail;
//头尾链接
newnode->next = phead;
phead->prev = newnode;
}
尾删也是一样的原理:
//--------------------尾删
void DSLTPopBack(DSLTNode* phead)
{
assert(phead);
assert(!DSLTEmpty(phead));
DSLTNode* tail = phead->prev;
DSLTNode* prev = tail->prev;
prev->next = phead;
phead->prev = prev;
free(tail);
tail = NULL;
}
void DSLTInsert(DSLTNode* pos, DSLtDataType x)
{
assert(pos);
DSLTNode* prev = pos->prev;
DSLTNode* newnode = GetNewNode(x);
newnode->next = pos;
pos->prev = newnode;
prev->next = newnode;
newnode->prev = prev;
}
pos位置删除:
void DSLTEarse(DSLTNode* pos)
{
assert(pos);
DSLTNode* next = pos->next;
DSLTNode* prev = pos->prev;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
对于普通前插,我们可以用Pos位置的前插来实现:把phead->next传给pos前插函数,相当于在原链表的哨兵位和第二个结点之间插入数据
void DSLTPushFront(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
DSLTInsert(phead->next, x);
}
前删同理:
void DSLTPopFront(DSLTNode* phead)
{
assert(phead);
DSLTEarse(phead->next);
}
后插可以传phead过去,这样就相当于在phead前插
void DSLTPushBack(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
DSLTInsert(phead, x);
}
而后删可以相当于哨兵位前的pos位置删除,把phead->prev传给pos位置删除函数
void DSLTPopBack(DSLTNode* phead)
{
assert(phead);
assert(!DSLTEmpty(phead));
DSLTEarse(phead->prev);
}
剩下的是总的代码还有find函数的传参和调试,仅供参考:
头文件DSLT.h:
#pragma once
#include
#include
#include
#include
typedef int DSLtDataType;
typedef struct DSListNode
{
struct DSListNode* next;
struct DSListNode* prev;
DSLtDataType data;
}DSLTNode;
//不使用二级指针
DSLTNode* DSLTInit();
void DSLTPrint(DSLTNode* phead);
DSLTNode* GetNewNode(DSLtDataType x);
bool DSLTEmpty(DSLTNode* phead);
DSLTNode* DSLTFind(DSLTNode* phead, DSLtDataType x);
size_t ListSize(DSLTNode* phead);
void DSLTDestroy(DSLTNode* phead);
void DSLTPushFront(DSLTNode* phead,DSLtDataType x);
void DSLTPopFront(DSLTNode* phead);
void DSLTPushBack(DSLTNode* phead, DSLtDataType x);
void DSLTPopBack(DSLTNode* phead);
void DSLTInsert(DSLTNode* pos, DSLtDataType x);
void DSLTEarse(DSLTNode* pos);
测试函数DSList.c
#define _CRT_SECURE_NO_WARNINGS
#include "DSLT.h"
DSLTNode* DSLTInit()
{
DSLTNode* guard = (DSLTNode*)malloc(sizeof(DSLTNode));
if (!guard)
{
perror("malloc fail");
exit(-1);
}
guard->next = guard;
guard->prev = guard;
return guard;
//哨兵位不存放值
}
void DSLTPrint(DSLTNode* phead)
{
assert(phead);
printf("phead<=>");
DSLTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("\n");
}
DSLTNode* GetNewNode(DSLtDataType x)
{
DSLTNode* newnode= (DSLTNode*)malloc(sizeof(DSLTNode));
if (!newnode)
{
perror("malloc fail");
exit(-1);
}
newnode->next = NULL;
newnode->prev = NULL;
newnode->data = x;
return newnode;
}
bool DSLTEmpty(DSLTNode* phead)
{
assert(phead);
return phead->next == phead;
}
DSLTNode* DSLTFind(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
DSLTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
size_t ListSize(DSLTNode* phead)
{
assert(phead);
size_t n = 0;
DSLTNode* cur = phead->next;
while (cur != phead)
{
++n;
cur = cur->next;
}
return n;
}
void DSLTDestroy(DSLTNode* phead)
{
assert(phead);
DSLTNode* cur = phead->next;
while (cur != phead)
{
DSLTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
void DSLTPushFront(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
/*DSLTNode* newnode = GetNewNode(x);
DSLTNode* cur = phead;
DSLTNode* next = phead->next;
cur->next = newnode;
newnode->prev = cur;
newnode->next = next;
next->prev = newnode;*/
DSLTInsert(phead->next, x);
}
void DSLTPopFront(DSLTNode* phead)
{
assert(phead);
/*assert(!DSLTEmpty(phead));
DSLTNode* first = phead->next;
DSLTNode* scend = first->next;
phead->next = scend;
scend->prev = phead;
free(first);
first = NULL;*/
DSLTEarse(phead->next);
}
void DSLTPushBack(DSLTNode* phead, DSLtDataType x)
{
assert(phead);
/*DSLTNode* tail = phead->prev;
DSLTNode* newnode = GetNewNode(x);
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;*/
DSLTInsert(phead, x);//相当于在phead前插
}
void DSLTPopBack(DSLTNode* phead)
{
assert(phead);
assert(!DSLTEmpty(phead));
/*DSLTNode* tail = phead->prev;
DSLTNode* prev = tail->prev;
prev->next = phead;
phead->prev = prev;
free(tail);
tail = NULL;*/
DSLTEarse(phead->prev);
}
//pos之前插入
void DSLTInsert(DSLTNode* pos, DSLtDataType x)
{
assert(pos);
DSLTNode* prev = pos->prev;
DSLTNode* newnode = GetNewNode(x);
newnode->next = pos;
pos->prev = newnode;
prev->next = newnode;
newnode->prev = prev;
}
//pos位置删除
void DSLTEarse(DSLTNode* pos)
{
assert(pos);
DSLTNode* next = pos->next;
DSLTNode* prev = pos->prev;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}
测试函数test.c:
#define _CRT_SECURE_NO_WARNINGS
#include "DSLT.h"
void DSLTTest1()
{
DSLTNode* plist = DSLTInit();
DSLTPushFront(plist, 1);
DSLTPushFront(plist, 2);
DSLTPushFront(plist, 3);
DSLTPushFront(plist, 4);
DSLTPushFront(plist, 5);
DSLTPrint(plist);
DSLTPopFront(plist);
DSLTPrint(plist);
DSLTPopFront(plist);
DSLTPrint(plist);
DSLTDestroy(plist);
}
void DSLTTest2()
{
DSLTNode* plist = DSLTInit();
DSLTPushBack(plist, 1);
DSLTPushBack(plist, 2);
DSLTPushBack(plist, 3);
DSLTPushBack(plist, 4);
DSLTPushBack(plist, 5);
DSLTPrint(plist);
DSLTPopBack(plist);
DSLTPopBack(plist);
DSLTPrint(plist);
DSLTDestroy(plist);
}
void DSLTTest3()
{
DSLTNode* plist = DSLTInit();
DSLTPushBack(plist, 1);
DSLTPushBack(plist, 2);
DSLTPushBack(plist, 3);
DSLTPushBack(plist, 4);
DSLTPushBack(plist, 5);
DSLTPrint(plist);
DSLTNode* tmp = DSLTFind(plist,1);
if (tmp == NULL)
{
printf("没找到!\n");
}
else
{
DSLTInsert(tmp, 100);
DSLTEarse(tmp);
}
DSLTPrint(plist);
DSLTDestroy(plist);
}
int main()
{
//DSLTTest1();//头插头删
DSLTTest2();//尾插尾删
//DSLTTest3();//pos位置
return 0;
}