一.基本框架
首先创立三个源文件分模块编写。
初始化定义一些结构。
定义新节点,每次需要一个新节点时就从该函数调用。
二.初始化哨兵位
这里设立哨兵位的好处就是可以快速找尾并且不用判断链表是否为空(在单链表中我们需要判断,如果链表为空就给头节点赋值,但在这因为存在哨兵位故链表不可能为空)
三.尾插
四.打印链表
五.尾删
在删除之前需要判断一下链表是否为空,所以增加一个函数。
接着进行尾删
六.头插和头删
七.任意位置插入和删除
在此之前需要找到该节点
开始插入和删除
八.销毁
这里链表就完成了,其实可以看出后面我们写的任意位置插入和删除,那么头插头删尾插尾删,其实是可以直接用这两个函数替代的,具体怎么替代就不再写啦,原理很简单,大家可以试试看。
九.源代码
test.c
#include<stdio.h>
#include"List.h"
//测试链表
void Test()
{
LTNode* plist = LTInit();//初始化哨兵位
LTPushBack(plist, 1);//插入一个值为1的节点
LTPushBack(plist, 2);//插入一个值为2的节点
LTPushBack(plist, 3);//插入一个值为3的节点
LTPushBack(plist, 4);//插入一个值为4的节点
LTPopBack(plist);//删除最后一个
LTPushFront(plist, 5);//头插5
LTPushFront(plist, 4);//头插4
LTPushFront(plist, 3);//头插3
LTPopFront(plist);//头删
LTNode*pos=TLFind(plist,5);//找到值为5的节点
LTInsert(pos,6);//在pos前面位置插入6
LTErase(pos);//删除pos节点
LTPrint(plist);//打印链表
Dstroy(plist);//销毁链表
}
int main()
{
Test();
return 0;
}
list.h
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#define LTDataType int
typedef struct ListNode
{
struct ListNode* pre;
struct ListNode* next;
LTDataType data;//这里依然设定特定结构为了防止以后储存不同类型数据需要不断更改类型
}LTNode;
LTNode* LTInit();//初始化哨兵位
void LTPushBack(LTNode* phead, LTDataType x);//尾插
void LTPrint(LTNode*phead);//打印链表
void LTPopBack(LTNode* phead);//尾删
void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPopFront(LTNode* phead);//头删
LTNode* TLFind(LTNode* pos, LTDataType x);//找到改节点
void LTInsert(LTNode* pos, LTDataType x);//在第pos个节点前插入
void LTErase(LTNode* pos);//删除pos节点
void Dstroy(LTNode* phead);//销毁链表
list.c
#include"List.h"
LTNode* BuyList(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof (LTNode));//开辟一个新节点
if (node == NULL)
{
perror("malloc fail");
return NULL;
}//防止开辟失败
node->next = NULL;
node->pre = NULL;
node->data = x;//初始化节点
return node;
}//开辟节点
LTNode* LTInit()//初始化
{
LTNode*phead = BuyList(-1);//初始化值为-1
phead->next = phead;
phead->pre = phead;//初始化两个指针都指向自己
return phead;
}
bool LTEmpty(LTNode* phead)//判断是否为空
{
assert(phead);
return phead->next == phead;
}
void LTPushBack(LTNode* phead, LTDataType x)//尾插
{
assert(phead);//断言头节点是否为空
LTNode* newnode = BuyList(x);//创建一个值为x的节点
LTNode*tail = phead->pre;//让尾部指向之前最后一个节点
tail->next = newnode;//之前最后一个节点指向现在最后一个节点
newnode->next = phead;//新节点的下一个节点指向哨兵位
newnode->pre = tail;//新节点的前一个指向之前最后一个节点
phead->pre = newnode;//哨兵位的前一个节点指向新节点
}
void LTPrint(LTNode*phead)//打印链表
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("\n");
}
void LTPopBack(LTNode* phead)//尾删
{
assert(phead);
assert(!LTEmpty(phead));//断言链表是否为空
LTNode* tail = phead->pre;//找到尾
LTNode* tailPre = tail->pre;//找到尾部前一个
phead->pre = tailPre;//让哨兵位前一个指向倒数第二个节点
tailPre->next = phead;//倒数第二个指向哨兵位
free(tail);//释放尾节点
tail = NULL;
}
void LTPushFront(LTNode* phead, LTDataType x)//头插
{
LTNode* newnode = BuyList(x);//创建一个值为x的节点
newnode->next = phead->next;
newnode->pre = phead;//改变新节点的两个指针
phead->next->pre = newnode;//改变原第一个节点的前一个指针
phead->next = newnode;//改变哨兵位的后一个指针
}
void LTPopFront(LTNode* phead)//头删
{
assert(phead);
assert(!LTEmpty(phead));//判断释放为空
LTNode* tail = phead->next;
LTNode* tailP = tail->next;
phead->next = tailP;
tailP->pre = phead;
free(tail);
tail = NULL;
}
LTNode* TLFind(LTNode* pos, LTDataType x)//找到改节点
{
assert(pos);
LTNode* cur = pos->next;
while (cur!= NULL)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LTInsert(LTNode* pos, LTDataType x)//在第pos个节点前插入
{
assert(pos);
LTNode* newnode = BuyList(x);
newnode->next = pos;
newnode->pre = pos->pre;//改变新节点的两个指针
pos->pre->next = newnode;//改变原pos前节点的后指针
pos->pre = newnode;//改变Pos的前指针
}
void LTErase(LTNode* pos)//删除pos节点
{
assert(pos);
LTNode* p = pos->pre;
LTNode* n = pos->next;
p->next = n;
n->pre = p;
free(pos);
}
void Dstroy(LTNode* phead)//销毁链表
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* n = cur->next;
free(cur);
cur = n;
}
free(phead);
}