带头双向循环链表:带头指的是带有哨兵位,双向指的是每个元素内包含着两个指针,分别指向该元素的上一个元素和下一个元素,循环表示该链表的尾的指向下一个元素的指针指向头,头的指向上一个元素的指针指向尾;
用图表示为
用c语言实现该结构,并且可以对该结构进行增、删、查、改,其中增又包括头插,尾插,随机插入;删除包括头删、尾删,随机删除;
首先,引出所使用的头文件,及该结构
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
typedef int LTDateType;
//该链表的数据种类
typedef struct LTNode
{
LTDateType date;
struct LTNode* prev;
//指向上一个元素
struct LTNode* next;
//指向下一个元素
}LTNode;
接下来要创建一个生成节点的函数
LTNode * BuyLTNode(LTDateType x)
{
LTNode* newnode = malloc(sizeof(LTNode)); //开辟空间存储数据
newnode->date = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
释放节点内存函数
void LTNodeDestory(LTNode * phead)
{
assert(phead);//断言,如果头节点为空,则结束程序,并返回错误原因
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead=NULL;
}
接着定义对该结构体进行初始化的函数
//初始化
void LTNodeInit(LTNode** pphead);
实现该函数
void LTNodeInit(LTNode** pphead)
//需要传二级指针,否则无法成功的初始化,这是由于无法改变结构体的数据
{
LTNode* newnode = BuyLTNode(0);
//生成哨兵位节点,其中date需要赋值
*pphead = newnode;
newnode->prev = *pphead;
newnode->next = *pphead;
//使两个指针分别指向自己
}
定义一个尾插函数
//尾插
void LTNodePushBack(LTNode * phead,LTDateType x);
实现该函数
void LTNodePushBack(LTNode * phead,LTDateType x)
{
LTNode* newnode = BuyLTNode(x);//生成需要插入的节点
LTNode* tail = phead->prev;//直接找到该链表的尾节点
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;//将该节点插入
}
定义一个头插函数
//头插
void LTNodePushFront(LTNode * phead, LTDateType x);
实现该函数
void LTNodePushFront(LTNode * phead, LTDateType x)
{
LTNode* newnode = BuyLTNode(x);
LTNode* first = phead->next;//直接找到头节点
phead->next = newnode;
newnode->prev = phead;
newnode->next = first;
first->prev = newnode;//将新节点变成第一个节点
}
定义一个尾删函数
//尾删
void LTNodeDeleBack(LTNode * phead);
实现该函数
void LTNodeDeleBack(LTNode * phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* end = phead->prev;
LTNode* last = end->prev;//找到尾节点和尾节点的上上一个节点
phead->prev = last;
last->next = phead;//改变尾节点
free(end);
end = NULL;//释放尾节点
}
定义一个头删函数
//头删
void LTNodeDeleFront(LTNode * phead);
实现该函数
void LTNodeDeleFront(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;//释放原第一个节点
}
定义一个查找函数
//查找
LTNode * LTNodeFind(LTNode * phead, LTDateType x);
实现该函数
LTNode * LTNodeFind(LTNode * phead, LTDateType x)
{
assert(phead);
LTNode* cur = phead->next;//用来遍历链表
while (cur != phead)
{
if (cur->date == x)
return cur;//存在该数据,返回数据位置
cur = cur->next;
}
return NULL;//不存在返回空指针
}
定义一个随机插入函数
//随机插入,在pos位置的节点之前插入一个节点
void LTNodeInsert(LTNode* pos, LTDateType x);
实现该函数
void LTNodeInsert(LTNode * pos, LTDateType x)
{
LTNode* newnode = BuyLTNode(x);//生成新结点
LTNode* prev = pos->prev;//找到要插入节点之前的节点
prev->next = newnode;
newnode->next = pos;
newnode->prev = prev;
pos->prev = newnode;//进行插入
}
定义一个随机删除的函数
//随机删除
void LTNodeDelete(LTNode * pos);
实现该函数
void LTNodeDelete(LTNode * pos)
{
LTNode* prev = pos->prev;
LTNode* next = pos->next;//找到要删除节点的上一个和下一个节点
prev->next = next;
next->prev = prev;//把两个节点连在一起
free(pos);
pos = NULL;//释放节点
}