当前位置: 首页 > news >正文

数据结构 04(线性:双向链表)

目录

链表的分类

双向链表

概念与结构

实现双向链表

创建头文件

创建哨兵位与申请空间

销毁

打印

尾插

头插

尾删

头删

查找

在任意位置后插入

删除任意位置

双向链表的整体实现

List.h

List.c


链表的分类

链表的结构非常多样,以下情况组合起来就有8种(2x2x2)链表结构:

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:单链表双向带头循环链表

1.无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2.带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,功能和单链表差不多但实现更简单更方便

本篇就讲的第二种链表。

双向链表

概念与结构

注意:这里的“带头”跟前面我们说的“头结点”是两个概念,实际前面的在单链表阶段称呼不严谨,但是为了同学们更好的理解就直接称为单链表的头结点。

带头链表里的头结点,实际为“哨兵位”,哨兵位结点不存储任何有效元素,只是站在这里“放哨的”。

实现双向链表

创建头文件

创建一个头文件,在里面创建和声明所需要的头文件、函数和单链表结构。如下:

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#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;//以下是对所写函数的声明,所有接下来都会写到
LTNode* BuyNode(LTDataType x);
LTNode* LTInit();
void LTDesTroy(LTNode* phead);
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);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);

创建哨兵位与申请空间

我们知道双向链表是需要一个不存储数据的哨兵位的,创建一个哨兵位也需要申请空间,下面给出空间申请的代码:

LTNode* BuyNode(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 = BuyNode(-1);return phead;
}

销毁

void LTDesTroy(LTNode* phead)
{LTNode* pcur = phead->next;     //哨兵位不存储数据,所以从next结点开始while (pcur->next != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;
}

打印

链表的打印都大差不差,需要注意一下循环结束的结点。

void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}

尾插

这里还需要回顾一下上面那张图

这张图很重要,这样才能理解各种插入操作。

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyNode(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 = BuyNode(x);newnode->prev = phead;newnode->next = phead->next;phead->next->prev = newnode;phead->next = newnode;
}

尾删

这里我使用了一个bool类型,如下:

bool LTEmpty(LTNode* phead)
{assert(phead);return (phead->next == phead);
}

返回bool类型的函数是一种返回布尔值(true 或 false)的函数。这里判断了哨兵位结点是否为空以及下一结点是否为自己。

尾删操作:

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;
}

查找

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;
}

在任意位置后插入

void LTInsert(LTNode* pos, LTDataType x) //在pos后插入
{assert(pos);LTNode* newnode = BuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}

删除任意位置

void LTErase(LTNode* pos)
{assert(pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

双向链表的整体实现

List.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#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;LTNode* BuyNode(LTDataType x);
LTNode* LTInit();
void LTDesTroy(LTNode* phead);
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);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);

List.c

#define _CRT_SECURE_NO_WARNINGS
#include"List.h"LTNode* BuyNode(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 = BuyNode(-1);return phead;
}void LTDesTroy(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur->next != phead){LTNode* next = pcur->next;free(pcur);pcur = next;}free(phead);phead = NULL;
}void LTPrint(LTNode* phead)
{LTNode* pcur = phead->next;while (pcur != phead){printf("%d -> ", pcur->data);pcur = pcur->next;}printf("NULL\n");
}void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = BuyNode(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 = BuyNode(x);newnode->prev = phead;newnode->next = phead->next;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;
}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;
}void LTInsert(LTNode* pos, LTDataType x) //在pos后插入
{assert(pos);LTNode* newnode = BuyNode(x);newnode->next = pos->next;newnode->prev = pos;pos->next->prev = newnode;pos->next = newnode;
}void LTErase(LTNode* pos)
{assert(pos);pos->next->prev = pos->prev;pos->prev->next = pos->next;free(pos);pos = NULL;
}

顺序表与链表的分析

http://www.dtcms.com/a/360284.html

相关文章:

  • Ansible 临时命令与常用模块实操指南
  • Cartographer中的gflag与lua文件
  • 国庆福建霞浦游
  • 阿里云创建自己的博客,部署wordpress
  • Java学习笔记-IO流(更新中...)
  • 嵌入式C学习笔记之链表
  • kkfileview自建cdn引入
  • 解决git push时的错误提示:“error: src refspec master does not match any”
  • 极客学院-从零开始学架构
  • 轻量化注意力+脉冲机制,Transformer在低功耗AI中再度进化
  • CAN数据链路层、网络层(ISO11898、15765)
  • 深度学习周报(8.25~8.31)
  • Unity核心概念②:帧、生命周期函数
  • Unity学习----【数据持久化】二进制存储(三)--文件夹操作
  • JSP 原理深度解析
  • 血缘元数据采集开放标准:OpenLineage Integrations Compatibility Tests Structure
  • 一句话PHP木马——Web渗透测试中的隐形杀手
  • Java GC 销毁机制 与 Redis 过期策略深度对比
  • 微知-Mellanox OFED编译的一些细节?无法编译怎么办?如何添加自定义编译选项?
  • linux系统学习(14.日志管理)
  • Day 01(02): 精读HDFS概念
  • QML Chart组件之图例
  • leetcode_74 搜索二维矩阵
  • tiktok弹幕 X-Bogus
  • Self-Attention的实现
  • nginx-增加VTS模块
  • [光学原理与应用-357]:ZEMAX - 分析 - 光线迹点
  • Ubuntu 中复制粘贴快捷键
  • 中国 AI 应用出海研究:出海逻辑和注意事项
  • Dreamore AI-解读并描绘你的梦境