文章目录
双链表
观看这里的uu建议先看单链表
线性表之单链表点击这里
一、前言
线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列等。
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
链表是典型的线性表之一,以链式结构的形式存储。
链表又被分为单链表和双链表。
本片仅对双链表详细说明。
二、双链表
1·双链表的概念及其结构
概念:双链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针双向链接次序实现的。
逻辑结构图如下:
根据双链表的逻辑结构可以想到双链表这种结构体中包含存入的数据、指向下一个结构体的结构体指针和指向上一个结构体的结构体指针。
双链表最大的优势就是双链表结构体的成员变量中包含上一个节点的地址,这样操作起来非常方便并且不需要遍历找上一节点的地址,基本没有效率损失,完美解决了单链表存在缺点。
2·双链表的分类
实际中要实现的双链表的结构非常多样,以下是两种双链表结构:
- 不带头非循环双链表
- 带头循环双链表
实际中最常用的是带头循环双链表。
带头循环双链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
双链表是否带头的区别:当不带头要改变头指针的指向时,那么就需要传二级指针形参来改变头指针的指向;当带头时只需要传一级指针,操作头节点来处理后续数据的操作即可。
双链表是否循环的区别:当非循环时进行尾部操作时,头节点与尾节点没有逻辑上的联系,尾部操作不是很方便;当循环时进行尾部操作时,头节点与尾节点有逻辑上的联系,使用起来方便许多。
说明:
双链表无论带头或不带头、循环或非循环的实现都大同小异,这里只详细说明带头+循环双链表的实现。
3·双链表的接口函数
双链表头文件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;
// 创建返回链表的头结点.
LTNode* ListCreate();
// 动态申请一个节点
LTNode* BuyListNode(LTDataType x);
// 双向链表销毁
void ListDestory(LTNode* plist);
// 双向链表打印
void ListPrint(LTNode* plist);
// 双向链表尾插
void ListPushBack(LTNode* plist, LTDataType x);
// 双向链表尾删
void ListPopBack(LTNode* plist);
// 双向链表头插
void ListPushFront(LTNode* plist, LTDataType x);
// 双向链表头删
void ListPopFront(LTNode* plist);
// 双向链表查找
ListNode* ListFind(LTNode* plist, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(LTNode* pos);
三、双链表的实现
说明:以下函数的实现思路在代码注释中已详细解释。
1·申请头节点、申请新节点、打印链表和释放链表
申请头节点函数在List.c中如下:
// 创建返回链表的头结点.
LTNode* ListCreate()
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
//开辟失败则报错且终止程序
if (phead == NULL)
{
printf("malloc fail");
exit(-1);
}
else
{
//头节点的下一节点和上一节点都指向自己,存入数据为随机值
phead->next = phead;
phead->prev = phead;
//返回头节点
return phead;
}
}
申请新节点函数在List.c中如下:
// 动态申请一个节点
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
//开辟失败则报错且终止程序
if (newnode == NULL)
{
printf("malloc fail");
exit(-1);
}
else
{
newnode->data = x;
return newnode;