目录
1.双向链表的结构
2.双向链表的实现
2.1初始化
以参数的形式初始化链表:
以返回值的形式初始化链表:
2.2尾插
2.3打印
2.4头插
2.5尾删
2.6头删
2.7查找
2.8在指定位置之后插入数据编辑
2.9删除pos节点
2.10销毁
3.整理代码
3.1 List.h
3.2 List.c
3.3 Test.c
1.双向链表的结构
带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在那里“放哨”。
“哨兵位”存在的意义: 遍历循环链表避免死循环。
2.双向链表的实现
不带头节点的单链表为空链表的判条件:head=NULL,因为head本来指向链表的第一个节点,如果head为NULL,说明链表没有节点,为空链表。
而带头结点的单链表为空链表的判定条件:head->next=NULL;
双向链表为空链表的判定条件:链表中只剩下一个头结点。
2.1初始化
申请节点函数
LTNode* LTBuyNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail!");
exit(1);
}
node->data = x;
node->next = node->prev = node;//自己指向自己
return node;
}
以参数的形式初始化链表:
开始的时候创建一个空链表,LTNode* plist = NULL;我们要对空链表进行初始化,保证让链表只有一个头结点(哨兵位),为我们后续插入操作做准备。由于要改变plist的指向,所以传二级指针。
void LTInit(LTNode** pphead)
{
//给双向链表创建一个哨兵位
*pphead = LTBuyNode(-1);//哨兵位里面不需要有值,为了调用申请节点函数,传一个值-1好了
}
以返回值的形式初始化链表:
初始化可以不传递二级指针,以返回值的形式返回哨兵位,调用完LTInit函数后,plist指针接收其返回值:LTNode* plist = LTInit();
LTNode* LTInit()
{
LTNode* phead = LTBuyNode(-1);
return phead;
}
2.2尾插
void LTPushBack(LTNode* phead, LTDataType x)//不改变哨兵位的地址,传一级指针即可
{
assert(phead);
LTNode* newnode = LTBuyNode(x);
//phead phead->prev newnode
newnode->prev = phead->prev;
newnode->next = phead;
phead->prev->next = newnode;
phead->prev = newnode;//注意:这两行代码不能颠倒位置
}
2.3打印
void LTPrint(LTNode* phead)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
2.4头插
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = LTBuyNode(x);
//phead newnode phead->next
newnode->next = phead->next;
newnode->prev = phead;
newnode->next->prev = newnode;
phead->next = newnode;//注意:这两行代码不能颠倒位置
}
2.5尾删
void LTPopBack(LTNode* phead)
{
//链表必须有效且链表不能为空(只有一个哨兵位)
assert(phead && phead->next!=phead);
LTNode* del = phead->prev;
//phead del->prev del
del->prev->next = phead;
phead->prev = del->prev;
//删除del节点
free(del);
del = NULL;
}
2.6头删
void LTPopFront(LTNode* phead)
{
assert(phead && phead->next != phead);
LTNode* del = phead->next;
//phead del del->next
phead->next = del->next;
del->next->prev = phead;
//删除del节点
free(del);
del = NULL;
}
2.7查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
//没有找到
return NULL;
}
2.8在指定位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = LTBuyNode(x);
//pos newnode pos->next
newnode->next = pos->next;
newnode->prev = pos;
pos->next->prev = newnode;
pos->next = newnode;
}
2.9删除pos节点
为了保持接口的一致性,这里传的是一级指针,对形参的修改不影响实参,pos置为空不影响find , find此时是野指针,所以调用完 LTERase 函数后要手动将find置为空(find=NULL;)
void LTErase(LTNode* pos)
{
assert(pos);
// pos->prev pos pos->next
pos->next->prev = pos->prev;
pos->prev->next = pos->next;
//删除pos节点
free(pos);
pos = NULL;
}
2.10销毁
同样为了保持接口的一致性,这里也传一级指针,对形参的修改不影响实参,phead置为空不影响实参plist,所以调用完 LTDesTroy 函数后要手动将plist置为空(plist=NULL;)
void LTDesTroy(LTNode* phead)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
LTNode* next = pcur->next;
free(pcur);
pcur = next;
}
//此时pcur指向phead,而phead还没有被销毁
free(phead);
phead = NULL;
}
3.整理代码
3.1 List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//定义双向链表节点的结构
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
//初始化
//void LTInit(LTNode** pphead);//以二级指针形式初始化
LTNode* LTInit();//以返回值形式初始化
//打印
void LTPrint(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead,LTDataType x);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头删
void LTPopFront(LTNode* phead);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos节点
void LTErase(LTNode* pos);
//销毁
void LTDesTroy(LTNode* phead);
3.2 List.c
#include"List.h"
//申请节点
LTNode* LTBuyNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
perror("malloc fail!");
exit(1);
}
node->data = x;
node->next = node->prev = node;//自己指向自己
return node;
}
//初始化
//以返回值形式初始化
LTNode* LTInit()
{
LTNode* phead = LTBuyNode(-1);
return phead;
}
//以二级指针形式初始化
//void LTInit(LTNode** pphead)
//{
// *pphead = LTBuyNode(-1);
//}
//打印
void LTPrint(LTNode* phead)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
//尾插
void LTPushBack(LTNode* phead, LTDataType x)//不改变哨兵位的地址,传一级指针即可
{
assert(phead);
LTNode* newnode = LTBuyNode(x);
//phead phead->prev newnode
newnode->prev = phead->prev;
newnode->next = phead;
phead->prev->next = newnode;
phead->prev = newnode;//注意:这两行代码不能颠倒位置
}
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = LTBuyNode(x);
//phead newnode phead->next
newnode->next = phead->next;
newnode->prev = phead;
newnode->next->prev = newnode;
phead->next = newnode;//注意:这两行代码不能颠倒位置
}
//尾删
void LTPopBack(LTNode* phead)
{
//链表必须有效且链表不能为空(只有一个哨兵位)
assert(phead && phead->next!=phead);
LTNode* del = phead->prev;
//phead del->prev del
del->prev->next = phead;
phead->prev = del->prev;
//删除del节点
free(del);
del = NULL;
}
//头删
void LTPopFront(LTNode* phead)
{
assert(phead && phead->next != phead);
LTNode* del = phead->next;
//phead del del->next
phead->next = del->next;
del->next->prev = phead;
//删除del节点
free(del);
del = NULL;
}
//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
//没有找到
return NULL;
}
//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* newnode = LTBuyNode(x);
//pos newnode pos->next
newnode->next = pos->next;
newnode->prev = pos;
pos->next->prev = newnode;
pos->next = newnode;
}
//删除pos节点
void LTErase(LTNode* pos)
{
assert(pos);
// pos->prev pos pos->next
pos->next->prev = pos->prev;
pos->prev->next = pos->next;
//删除pos节点
free(pos);
pos = NULL;
}
//销毁
void LTDesTroy(LTNode* phead)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
LTNode* next = pcur->next;
free(pcur);
pcur = next;
}
//此时pcur指向phead,而phead还没有被销毁
free(phead);
phead = NULL;
}
3.3 Test.c
#include"List.h"
void ListTest01()
{
//传二级指针形式初始化
/*LTNode* plist = NULL;
LTInit(&plist); */
//以返回值形式初始化
LTNode* plist = LTInit();
//尾插
LTPushBack(plist, 1);
LTPushBack(plist, 2);
LTPushBack(plist, 3);
LTPrint(plist);//打印
//头插
LTPushFront(plist,4);
LTPushFront(plist,5);
LTPrint(plist);//打印
//尾删
LTPopBack(plist);
LTPrint(plist);//打印
//头删
LTPopFront(plist);
LTPrint(plist);//打印
//查找
LTNode* find = LTFind(plist,4);
//删除pos节点
LTErase(find);
LTPrint(plist);//打印
find = NULL;//手动置空
//销毁
LTDesTroy(plist);
plist = NULL;//手动置空
}
int main()
{
ListTest01();
return 0;
}