目录
1.介绍双链表
2.双链表的实现
2.1头文件
2.2双链表的声明
2.3创造一个空间
2.4双链表的初始化
2.5双链表的销毁
2.6双链表的打印
2.7双链表的尾插和尾删
2.8头插和头删
2.9找数据的位置
2.10在指定位置前插入
2.11删除指定位置的数据
双链表的全称叫做带头双向循环链表,它有个头节点叫做“哨兵位”。哨兵位不存储数据。
“哨兵位”存在的意义:遍历循环链表避免死循环。单链表的全称叫做不带头单向不循环链表。区别就是双链表一个格子里要存储前一个空间的指针和后一个空间的指针,然后双链表的尾节点的next指针是指向头结点的,形成一个循环
#pragma once
#include
#include
#include
#include
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
//。。。。
}LTNode;
//void LTInit2(LTNode** pphead);
//y为要查找的位置的值
//x为新开辟空间的值
LTNode* LTInit1();
void LTDestroy(LTNode* phead);
void LTPrint(LTNode* phead);
bool LTEmpty(LTNode* phead);
void LTPushBack(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopFront(LTNode* phead);
//在pos位置之后插入数据
void LTInsert(LTNode* phead, LTDataType y,LTDataType y);
void LTErase(LTNode* phead,LTDataType y);
//找位置
LTNode* LTFind(LTNode* phead, LTDataType x);
typedef int LTDataType;
typedef struct ListNode
{
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
//。。。。
}LTNode;
LTdatatype 是之后要存储数据的类型,2.将 struct listnode 重命名为 LTNode。
prev代表指向前一个空间的指针。
next代表的是指向后一个空间的指针。
LTNode* ListBuyNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (NULL == node)
{
perror("malloc failed:");
return;
}
node->data = x;
node->next = node->prev = NULL;
return node;
}
这一步和其他链表一样,需要开辟一个空间,存储数据,新的节点用于插入链表。
LTNode* LTInit1()
{
LTNode* phead = (LTNode*)malloc(sizeof(phead->data));
if (NULL == phead)
{
perror("malloc failed:");
return;
}
phead->data = -1;
phead->next = phead->prev = phead;
return phead;
}
因为双链表有一个“哨兵位”,所以初始化主要是创建一个哨兵位。因为现在就一个头结点,
所以phead next 和prev 都是指向哨兵位,后续要使用就直接更改就好。
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
LTNode* next = pcur->next;
free(pcur);
pcur = next;
}
free(phead);
phead = NULL;
}
双链表的销毁和单链表类似,需要一个个进行销毁。定义一个pcur 指向 phead next 的节点。然后一一遍历 直到除了哨兵位的节点都被释放了,最后释放头部就可以了。
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
printf("%d ->", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
首先定义一个 pcur 变量用来遍历链表。直到链表的数据都打印结束,走到phead哨兵位。
最后在换行。
void LTPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* prev = phead->prev;
LTNode* node = ListBuyNode(x);
node->next = phead;
node->prev = phead->prev;
prev->next = node;
phead->prev = node;
}
链表的尾插需要考虑的是,哨兵位和哨兵位前一个结点(链表最后一个节点)。
实际上两者就可以执行尾插了,不需要遍历。
先用listbutnode 创造一个新的节点。新节点的next指向 哨兵位(因为是循环链表)。新节点的prev指针指向的就是phead 本来的前一个结点(尾结点)。
最后在处理原链表的哨兵位和尾结点的指针。prev 节点的 next 指针指向 node。
phead 的prev 指针指向node。
void LTPopBack(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* prev = phead->prev;
phead->prev = prev->prev;
prev->prev->next = phead;
free(prev);
prev = NULL;
}
尾删就是把最后一个节点删除,删除的时候要想到前一个结点的next指针,和哨兵位的prev指针怎么办。先定义一个指针指向尾结点。最后把这个指针free即可。
删除节点要判断是否这个链表只有哨兵位
void LTPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* node = ListBuyNode(x);
node->next = phead->next;
node->prev = phead;
phead->next->prev = node;
phead->next = node;
}
头插要考虑两个节点,就是哨兵的next指针,和哨兵位的下一个节点的prev指针。
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* del = phead->next;
phead->next = del->next;
del->next->prev = phead;
free(del);
del = NULL;
}
头删要考虑两个节点,哨兵位的next指针,和要删除节点的下一个节点的prev指针。
删除节点要判断是否这个链表只有哨兵位
LTNode* LTFind(LTNode* phead, LTDataType x)
{
assert(phead && phead->next != phead);
LTNode* pcur = phead;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
遍历链表找到相同的元素,返回这个位置的指针。
要判断是否这个链表只有哨兵位。
void LTInsert(LTNode* phead, LTDataType y,LTDataType x)
{
LTNode* pos = findpos(phead, y);
if (pos == NULL)
{
printf("没找到数据阿弟!\n");
return;
}
LTNode* node = ListBuyNode(x);
node->next = pos->next;
node->prev = pos;
pos->next->prev = node;
pos->next = node;
}
因为是指定位置,所以要用到找位置的接口函数。调用即可。
如果找到了位置,那就开辟一块空间将数据存进去。
指定位置插入要考虑的两个节点是,指定位置前一个节点的next指针,我指定节点的prev指针。
和node指针的插入
void LTErase(LTNode* phead, LTDataType y)
{
assert(phead && phead->next != phead);
LTNode* pos = findpos(phead, y);
if (pos == NULL)
{
printf("没找到数据阿弟!\n");
return;
}
pos->next->prev = pos->prev;
pos->prev->next = pos->next;
free(pos);
pos = NULL;
}
一样的先查找位置。
删除指定位置要考虑的两个节点是,删除节点的前一个节点的next指针,和后一个节点的prev指针。
最后free pos 指针即可。
要判断是否这个链表只有哨兵位。