1.概念
带头链表⾥的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这⾥“放哨的”
2.代码位置
3.双向链表的实现
3.1双向链表的初始化和打印及销毁
。h
#pragma once//用于确保一个头文件只被编译一次
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义双向链表结点的结构
typedef int LTDatatype;
typedef struct ListNode
{
LTDatatype data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
//为了保持接口的一致性 所以都是传一级指针
//初始化 就是通过调用初始化的方法接收返回值
//要不通过参数给我一个结点
// 传指向哨兵位节点的指针,但目前为空,在初始化时,才申请一个结点的空间
// void LTInit(LTNode** pphead);
//要不通过返回值给我结点
LTNode* LTInit();
//销毁
void LTDesTroy(LTNode* pphead);//传一级 需要手动把plist置为NULL
//传二级 销毁 void LTDesTroy(LTNode** pphead);
void LTPrint(LTNode* phead);
.c
//带头链表的头结点,也叫哨兵位,不存储任何有效元素,只放哨
//第一个结点:第一个有效的结点 哨兵位:头结点
LTNode* LTBuyNode(LTDatatype x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail!");
exit(1);
}
newnode->data = x;
newnode->next = newnode->prev = newnode;
return newnode;
}
//初始化
LTNode* LTInit()
{
LTNode* phead = LTBuyNode(-1);//-1就是哨兵位
return phead;
}
//打印
void LTPrint(LTNode* phead)
{
LTNode* pcur = phead->next;
//头结点不打印
while (pcur != phead)
{
printf("%d->", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
//销毁传二级的时候
//void LTDesTroy(LTNode** pphead)
//{
// assert(pphead&&* pphead);
// LTNode* pcur = (*pphead)->next;
// while (pcur != *pphead)//左右都是一级指针
// {
// LTNode* Next = pcur->next;
// free(pcur);
// pcur = Next;
// }
// //销毁哨兵位结点
// free(*pphead);
// *pphead = NULL;
// pcur = NULL;
//}
//初始化传二级指针的时候
//void LTInit(LTNode** pphead)
//{
// //创建一个头结点(哨兵位)
// *pphead = LTBuyNode(-1);
//}
3.2双向链表的插入
//插入
//第一个参传一级还是二级,要看pphead指向的节点会不会发生改变
//如果发生改变,那么pphead的改变要影响实参,传二级
//如果不发生改变,pphead不会影响实参,传一级
// 本题哨兵位永远不会改变
void LTPushBack(LTNode* phead, LTDatatype x);
void LTPushFront(LTNode* phead, LTDatatype x);
//在pos位置之后插入结点
void LTInsert(LTNode* pos, LTDatatype x);```
void LTPushBack(LTNode* phead, LTDatatype x)
{
assert(phead);//不能传NULL
LTNode* newnode = LTBuyNode(x);
//画图解决 phead phead->prev newnode
newnode->next = phead;
newnode->prev = phead->prev;
phead->prev->next = newnode;
phead->prev = newnode;
}
void LTPushFront(LTNode* phead, LTDatatype x)
{
assert(phead);
LTNode* newnode = LTBuyNode(x);
newnode->next = phead->next;
newnode->prev = phead;
phead->next->prev = newnode;
phead->next = newnode;
}
//在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;
}
## 3.3双向链表的删除
```c
//删除
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);
bool LTEmpty(LTNode* phead);//判断链表是否为空
//删除指定位置的结点
void LTErase(LTNode* pos);
//判断链表是否为空
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead; //若答案为等则为true
}
//删除
void LTPopBack(LTNode* phead)
{
//不用删哨兵位,所以传一级
assert(phead);
assert(!LTEmpty(phead));
//phead prev(del->prev) del(phead->prev)
LTNode* del = phead->prev;
LTNode* prev = del->prev;
prev->next = phead;
phead->prev = prev;
free(del);
del = NULL;
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(!LTEmpty(phead));
//phead del(phead->next) del->next
LTNode* del = phead->next;
//长的先行
del->next->prev = phead;
phead->next = del->next;
free(del);
del = NULL;
}
//删除指定位置的结点
void LTErase(LTNode* pos)
{
assert(pos);
// pos->prev pos pos->next
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
3.4双向链表查找指定位置的结点
LTNode* LTFind(LTNode* phead, LTDatatype x);
//先找到才能对pos位置进行操作
LTNode* LTFind(LTNode* phead, LTDatatype x)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
4.顺序表与链表的分析比较
介绍完链表和顺序表之后,将二者进行比较