数据结构---详解双向链表
前面我们讲过单向链表,但是链表的结构非常多。
一、链表的分类
- 虽然上面展示了很多链表的结构,但是我们实际上最常用的还是两种结构:单链表和双向链表。
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。
- 带头双向循环链表:结构复杂,一般用在单独储存数据。
二、双向链表的实现
- 首先创建三个文件
头文件List.h:声明函数和定义链表
源文件List.c:用来具体实现函数
测试文件test.c:用来测试工作
1、双向链表的初始化
- 方法一:直接一个函数生成哨兵位,然后返回哨兵位
LTNode* LTInit()
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->data = -1; //这里的值随便定,data无效
phead->next = phead->prev = phead;
return phead;
}
- 方法二:传指针,进行加工成哨兵位
void LTInit(LTNode** pphead)
{
assert(pphead);
*pphead = (LTNode*)malloc(sizeof(LTNode));
(*pphead)->data = -1;
(*pphead)->next = (*pphead)->prev = *pphead;
}
2、双向链表的销毁
void LTDesTroy(LTNode* phead)
{
//创建一个指向头节点的下一个结点的指针
LTNode* pcur = phead->next;
while (pcur != phead)
{
//创建一个next指针记录位置,避免后续释放的时候找不到下一个结点
LTNode* next = pcur->next;
free(pcur);
pcur = next;
}
free(phead);
phead = NULL;
}
3、双向链表的打印
- 单向链表的打印靠循环循环的结束标志是pcur == NULL,那双向链表又该怎么打印呀?我们指定双向链表是循环的,如果跟单向链表一样的结束条件会出现死循环的情况,我们会发现前面初始化哨兵位的时候,哨兵位无有效数据,同时循环一轮还是回到哨兵位的位置,那我们就可以将pcur != phead作为循环的结束条件
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
printf("%d-> ", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
4、双向链表的尾插
- 创建一个新节点,想让新节点的prev指针和next指针分别指向phead->prev和phead,在让尾节点的next指针指向newnode和phead->prev指向newnode。
void LTPushBack(LTNode* phead, LTDatatype x)
{
assert(phead);
//创建一个新节点
LTNode* newnode = LTBuyNode(x);
newnode->prev = phead->prev; //看图1
newnode->next = phead;
phead->prev->next = newnode; //图2
phead->prev = newnode;
}
- 图一
- 图二
5、双向链表的头插
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;
}
- 图一
- 图二
6、双向链表判空
- 链表的头节点的next指针指向自己的时候说明链表里面没有数据
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
7、双向链表尾删
void LTPopBack(LTNode* phead)
{
assert(!LTEmpty(phead));
LTNode* del = phead->prev;
del->prev->next = phead;
phead->prev = del->prev;
free(del);
del = NULL;
}
- 看下图更好的理解
8、双向链表头删
void LTPopFront(LTNode* phead)
{
assert(!LTEmpty(phead));
LTNode* del = phead->next;
del->next->prev = phead;
phead->next = del->next;
free(del);
del = NULL;
}
9、在pos之后插入数据
void LTInsert(LTNode* pos, LTDatatype x)
{
assert(pos);
LTNode* newnode = LTBuyNode(x);
newnode->next = pos->next; //看红色的箭头
newnode->prev = pos;
pos->next->prev = newnode; //看粉色的箭头
pos->next = newnode;
}
10、双向链表查找数据
LTNode* LTFind(LTNode* phead, LTDatatype x)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
if (pcur->data == x)
{
return pcur;
}
pcur = pcur->next;
}
return NULL;
}
11、删除pos位置数据
void LTErase(LTNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
三、双向链表代码总览
1、List.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* prev;
struct ListNode* next;
}LTNode;
//双向链表的初始化
LTNode* LTInit();
//void LTInit(LTNode** pphead);
//链表销毁
//传一级指针,保证接口一致性,但需要手动将实参置为NULL
void LTDesTroy(LTNode* phead);
//创建新节点
LTNode* LTBuyNode(LTDatatype x);
//打印
void LTPrint(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDatatype x);
//头插
void LTPushFront(LTNode* phead, LTDatatype x);
//判断空
bool LTEmpty(LTNode* phead);
//尾删
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);
2、List.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"
//双向链表初始化两种方法
LTNode* LTInit()
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->data = -1;
phead->next = phead->prev = phead;
return phead;
}
//void LTInit(LTNode** pphead)
//{
// assert(pphead);
// *pphead = (LTNode*)malloc(sizeof(LTNode));
// (*pphead)->data = -1;
// (*pphead)->next = (*pphead)->prev = *pphead;
//}
//链表的销毁
void LTDesTroy(LTNode* phead)
{
LTNode* pcur = phead->next;
while (pcur != phead)
{
LTNode* next = pcur->next;
free(pcur);
pcur = next;
}
free(phead);
phead = NULL;
}
//打印
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* pcur = phead->next;
while (pcur != phead)
{
printf("%d-> ", pcur->data);
pcur = pcur->next;
}
printf("\n");
}
//创建一个节点
LTNode* LTBuyNode(LTDatatype x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
return NULL;
node->data = x;
node->prev = node->prev = node;
return node;
}
//尾插
void LTPushBack(LTNode* phead, LTDatatype x)
{
assert(phead);
//创建一个新节点
LTNode* newnode = LTBuyNode(x);
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);
newnode->next = phead->next;
newnode->prev = phead;
phead->next->prev = newnode;
phead->next = newnode;
}
//判断空
bool LTEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
//尾删
void LTPopBack(LTNode* phead)
{
assert(!LTEmpty(phead));
LTNode* del = phead->prev;
del->prev->next = phead;
phead->prev = del->prev;
free(del);
del = NULL;
}
//头删
void LTPopFront(LTNode* phead)
{
assert(!LTEmpty(phead));
LTNode* del = phead->next;
del->next->prev = phead;
phead->next = del->next;
free(del);
del = NULL;
}
//在pos之后插入数据
void LTInsert(LTNode* pos, LTDatatype x)
{
assert(pos);
LTNode* newnode = LTBuyNode(x);
newnode->next = pos->next;
newnode->prev = pos;
pos->next->prev = newnode;
pos->next = newnode;
}
//查找数据
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 LTErase(LTNode* pos)
{
assert(pos);
pos->prev->next = pos->next;
pos->next->prev = pos->prev;
free(pos);
pos = NULL;
}
3、test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"
void test()
{
//第一种初始化方式
LTNode* plist = LTInit();
//第二种初始化方式
//LTNode* plist = NULL;
//LTInit(&plist);
//尾插
LTPushBack(plist, 1);
LTPrint(plist);
LTPushBack(plist, 2);
LTPrint(plist);
LTPushBack(plist, 3);
LTPrint(plist);
LTPushBack(plist, 4);
LTPrint(plist);
//链表的销毁
/*LTDesTroy(plist);
plist = NULL;*/
//删除pos位置数据
/*LTNode* find = LTFind(plist, 2);
if (find == NULL)
{
printf("没有找到!");
}
else
{
printf("找到了!");
}
LTErase(find);
LTPrint(plist);*/
//在pos位置之后插入数据
/*LTNode* find = LTFind(plist, 2);
if (find == NULL)
{
printf("没有找到!");
}
else
{
printf("找到了!");
}
LTInsert(find, 99);
LTPrint(plist);*/
//查找
/*LTNode* find = LTFind(plist, 5);
if (find == NULL)
{
printf("没有找到!");
}
else
{
printf("找到了!");
}*/
//尾删
/*LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);
LTPopBack(plist);
LTPrint(plist);*/
//头删
/*LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);
LTPrint(plist);
LTPopFront(plist);*/
//头插
/*LTPushFront(plist, 1);
LTPrint(plist);
LTPushFront(plist, 2);
LTPrint(plist);
LTPushFront(plist, 3);
LTPrint(plist);
LTPushFront(plist, 4);
LTPrint(plist);*/
}
int main()
{
test();
return 0;
}