【数据结构】励志大厂版·初级(二刷复习)双链表

前引:今天学习的双链表属于链表结构中最复杂的一种(带头双向循环链表),按照安排,我们会先进行复习,如何实现双链表,如基本的头插、头删、尾删、尾插,掌握每个细节,随后进行例题练习,帮助我们了解它的实际挑战,前面的实现只是了解它结构的入门,当然只有打好基础才是最重要的,小编会仔细讲解它的各个环节,正文开始~ 

目录

知识点速览

双链表的实现

节点结构

设置哨兵节点

 开辟节点

 尾插

尾删

头插

头删

 在目标节点前面插入

在目标节点后面插入

练习题说明


知识点速览

双链表的实现

今天咱们来复习一下最复杂的链表结构——带头双向循环链表。根据字面意思我们大概可以详细想象出来它的特点,首先有一个哨兵节点来充当头节点,其次是循环双向的,逻辑结构如下图:

它较与单链表的结构里面多了一个指针,指向它的前一个节点,以此达到双向。针对哨兵节点:

哨兵节点一般不存储任何数据, 不仅使用方便,可以简化插入删除的操作(不用传二级指针),所有操作无需去处理哨兵节点,逻辑统一,最后可以减少空指针异常

这里的循环是根据节点的空间结构而来的:头节点->prev=尾节点,尾节点->next=头节点

节点结构

较单链表而言只多加了一个指针,用来指向自身的前一个节点。这里小编用 tpedef 重定义了一下数据类型,方便以后进行维护。

typedef int Plastic;typedef struct DoubleList
{//数据域Plastic data;//前指针域struct DoubleList* prev;//后指针域struct DoubleList* next;
}DoubleList;
设置哨兵节点

哨兵节点的数据域一般不存储数据,但是它之后的链表节点需要给一个数据作为参数开辟节点,所以小编在这里将二者分开,给哨兵节点单独设置一个函数来开辟空间。开始时头指针指向哨兵节点,哨兵节点前后指针应该指向自己,已达到双向循环结构

//设置哨兵节点
DoubleList* pphead = Sentry();
//设置哨兵节点
DoubleList* Sentry()
{//开辟节点DoubleList* newnode = (DoubleList*)malloc(sizeof(DoubleList));//判断空间有效性if (newnode == NULL){printf("哨兵节点开辟失败\n");return NULL;}//初始化newnode->next = newnode;newnode->prev = newnode;return newnode;
}
 开辟节点

开辟节点还是和单链表一样,传一个数据给它就行了

这里需要注意:初始化开辟的节点时应该是前后指向自己的,如下图:

//新增节点
DoubleList* Newnode(Plastic data)
{//开辟节点DoubleList* newnode = (DoubleList*)malloc(sizeof(DoubleList));//判断空间有效性if (newnode == NULL){printf("节点开辟失败\n");return NULL;}//初始化newnode->next = newnode;newnode->prev = newnode;newnode->data = data;return newnode;
}
 尾插

尾插需要先找尾,对于双向循环链表而言,头节点的前一个节点就是它的尾。然后再插入新增的节点,连接  next  与  prev  的关系即可

注意:尾插不用判断链表是否存在啊,因为我们这里有哨兵节点,直接找尾、插入即可

//尾插
void Tail_insert(DoubleList* pphead, Plastic data)
{//找尾DoubleList* tail = pphead->prev;//开辟节点DoubleList* newnode = Newnode(data);//连接pphead->prev = newnode;newnode->next = pphead;tail->next = newnode;newnode->prev = tail;
}

下面我们通过打印函数来看一下尾插的效果如何:

//打印
void List_Print(DoubleList* pphead)
{//如果只有哨兵节点if (pphead->next == pphead){printf("无元素可以打印\n");return;}//因为如果只有哨兵节点,pphead->next就越界了DoubleList* first = pphead->next;while (first != pphead){printf("%d -> ", first->data);first = first->next;}printf("pphead\n");
}
尾删

尾删需要先找尾,然后将头节点与尾的前一个节点进行连接,再释放之前标记的尾,思维上并不难

//尾删
void Tail_deletion(DoubleList* pphead)
{//如果只有头节点无法删除if (pphead->prev == pphead){printf("无法删除\n");return;}//找尾DoubleList* tail = pphead->prev;//找倒数第二个节点DoubleList* cur = pphead->prev->prev;//更新关系,重新连接pphead->prev = cur;cur->next = pphead;free(tail);tail = NULL;
}
头插

先标记头节点的下一个节点,然后在头节点与这个标记的节点中间插入即可,最后更新连接关系

//头插
void Head_insert(DoubleList* pphead, int data)
{//标记头节点的下一个节点DoubleList* first = pphead->next;DoubleList* newnode = Newnode(5);//更新连接关系pphead->next = newnode;newnode->next = first;first->prev = newnode;newnode->prev = pphead;
}
头删

对于只有哨兵节点的双链表是无法头删的,因此需要先进行判断。其次是标记链表的第一个节点、第二个节点,重新确立头节点和第二个节点的关系,再释放掉第一个节点

//头删
void Head_deletion(DoubleList* pphead)
{//判断是否只有哨兵节点if (pphead->prev == pphead){printf("无法删除\n");return;}//标记第一个节点DoubleList* first = pphead->next;//标记第二个节点DoubleList* second = first->next;//重新确立头节点和第二个节点的关系pphead->next = second;second->prev = first;//释放第一个节点free(first);first = NULL;
}
 在目标节点前面插入

我们先找到目标节点,然后再标记目标节点前面的一个节点,再确立三者之间的 next 与 prev 的关系,如下图:

//在目标节点前面插入
void Before_target(DoubleList* pphead, int  data)
{//找目标节点DoubleList* cur = pphead->next;while (cur->data != data){if (cur == pphead){printf("没有找到\n");return;}cur = cur->next;}//标记cur前面的节点DoubleList* prev = cur->prev;DoubleList* newnode = Newnode(6);//将三者进行连接prev->next = newnode;newnode->prev = prev;newnode->next = cur;cur->prev = newnode;
}
在目标节点后面插入

先找到目标节点,然后标记目标节点后面的一个节点,再确立新增节点、目标节点、标记节点的 next  prev 的关系,与“在目标节点前面插入”很类似

//在目标节点后面插入
void Behind_target(DoubleList* pphead, int data)
{//找目标节点DoubleList* cur = pphead->next;while (cur->data != data){if (cur == pphead){printf("没有找到\n");return;}cur = cur->next;}//标记cur后面的节点DoubleList* next = cur->next;DoubleList* newnode = Newnode(7);//将三者进行连接cur->next = newnode;newnode->prev = cur;newnode->next = next;next->prev = newnode;
}

练习题说明

一般链表题,比如考研、面试、校招会以单链表居多,大家可以看我的上一篇文章来学习链表题目

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/79995.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS `display` 属性详解(完整版)

CSS display 属性详解(完整版) 1. 属性值及特性详解 display 属性控制元素的布局类型和生成的框类型,以下是 所有有效值 及其特性: 1.1 基础类型 值描述布局行为是否生成块级框典型用途block元素独占一行,宽度自动撑…

【数据结构 · 初阶】- 堆的实现

目录 一.初始化 二.插入 三.删除(堆顶、根) 四.整体代码 Heap.h Test.c Heap.c 我们使用顺序结构实现完全二叉树,也就是堆的实现 以前学的数据结构只是单纯的存储数据。堆除了存储数据,还有其他的价值——排序。是一个功能…

qt.tlsbackend.ossl: Failed to load libssl/libcrypto.

我的环境是windows,QT6.3.2(msvc2019_64/mingw_64) 出错原因 QT没有正确加载OpenSSL。 解决过程 1、确保安装的有openssl。 文章结尾有个注意,是其他方式安装过openssl,环境变量有,但是QT找不到的问题。…

【Linux】用户权限

shell命令 1. Linux本质上是一个操作系统,但是一般的用户不能直接使用它,而是需要通过外壳程序shell,来与Linux内核进行沟通。 2. shell的简单定义:命令行解释器。主要包含以下作用: 将使用者的命令翻译给核心处理。将…

赛灵思 XC7K325T-2FFG900I FPGA Xilinx Kintex‑7

XC7K325T-2FFG900I 是 Xilinx Kintex‑7 系列中一款工业级 (I) 高性能 FPGA,基于 28 nm HKMG HPL 工艺制程,核心电压标称 1.0 V,I/O 电压可在 0.97 V–1.03 V 之间灵活配置,并可在 –40 C 至 100 C 温度范围内稳定运行。该器件提供…

【题解-Acwing】847. 图中点的层次

题目:847. 图中点的层次 题目描述 给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环。 所有边的长度都是 1,点的编号为 1∼n。 请你求出 1 号点到 n 号点的最短距离,如果从 1 号点无法走到 n 号点,输出 −1 。 输入 第一行包含两个整数 n 和 m。 接下来 m 行…

css图片设为灰色

使用filter方式将图片设置为灰色 普通图片使用&#xff1a;filter: saturate(0); 纯白图片使用&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"width…

【Luogu】动态规划一

P5414 [YNOI2019] 排序 - 洛谷 思路&#xff1a; 可以想到对于任意一个需要换位置的数字&#xff0c;我们不可能换两次及以上&#xff0c;那么这题就可以转化为求一个最大和的最长不递减子序列&#xff0c;最后的答案就是众和减去这个最大和 代码&#xff1a; #include <…

什么是管理思维?

管理思维是指在管理活动中形成的系统性、战略性和创造性的思考方式&#xff0c;帮助个人或团队更高效地达成目标。它不仅适用于企业管理&#xff0c;也适用于个人成长、项目执行和复杂问题解决。以下是关于管理思维的核心内容&#xff1a; 一、管理思维的核心特征 1. 系统性思…

利用TCP+多进程技术实现私聊信息

服务器&#xff1a; import socket from multiprocessing import Process from threading import Threaduser_dic {}def send_recv(client_conn, client_addr):while 1:# 接收客户端发送的消息res client_conn.recv(1024).decode("utf-8")print("客户端发送…

Hbuilder 上的水印相机实现方案 (vue3 + vite + hbuilder)

效果 思路 通过 live-pusher 这个视频推流的组件来获取摄像头拿到视频的一帧图片之后&#xff0c;跳转到正常的 vue 页面&#xff0c;通过 canvas 来处理图片水印 源码 live-pusher 这个组件必须是 nvue 的 至于什么是 nvue&#xff0c;看这个官方文档吧 https://uniapp.dcl…

Spark,IDEA编写Maven项目

IDEA中编写Maven项目 1.打开IDEA新建项目2.选择java语言&#xff0c;构建系统选择Maven 3.IDEA中配置Maven 注&#xff1a;这些文件都是我们老师帮我们在网上找了改动后给我们的&#xff0c;大家可自行在网上查找 编写代码测试HDFS连接 1.在之前创建的pom.xml文件中添加下…

初识Redis · C++客户端set和zset

目录 前言&#xff1a; set sadd sismember smembers spop scard sinter sinterstore zset zadd zrange zcard zrem zrank zscore 前言&#xff1a; 前文我们已经介绍了string list hash在Redis-plus-plus的使用&#xff0c;本文我们开始介绍set和zset在redis-plus-pl…

sed命令笔记250419

sed命令笔记250419 sed&#xff08;Stream Editor&#xff09;是 Linux/Unix 系统中强大的流编辑器&#xff0c;主要用于对文本进行过滤和转换&#xff08;按行处理&#xff09;。它支持正则表达式&#xff0c;适合处理文本替换、删除、插入等操作。以下是 sed 的详细解析&…

ubuntu-24.04.2-live-server-arm64基于cloud-init实现分区自动扩容(LVM分区模式)

1. 环境 虚拟机镜像ISO&#xff1a;ubuntu-24.04.2-live-server-arm64.iso 2. 定制cloud-init镜像 2.1 安装OS 基于ubuntu-24.04.2-live-server-arm64.iso&#xff0c;通过virt-manager安装操作系统&#xff0c;语言建议选择英文&#xff0c;分区选择基于LVM的自动分区&…

vue3专题1------父组件中更改子组件的属性

理解 Vue 3 中父组件如何引用子组件的属性是一个很重要的概念。 这里涉及到 defineExpose 和 ref 这两个关键点。 方法&#xff1a;使用 defineExpose 在子组件中暴露属性&#xff0c;然后在父组件中使用 ref 获取子组件实例并访问暴露的属性。 下面我将详细解释这个过程&…

数据仓库分层架构解析:从理论到实战的完整指南​​

数据仓库分层是构建高效数据体系的核心方法论。本文系统阐述ODS、DWD、DWS、ADS四层架构的设计原理&#xff0c;结合电商用户行为分析场景&#xff0c;详解各层功能及协作流程&#xff0c;并给出分层设计的原则与避坑指南&#xff0c;帮助读者掌握分层架构的落地方法。 一、为什…

从零搭建一套前端开发环境

一、基础环境搭建 1.NVM(Node Version Manager)安装 简介 nvm&#xff08;Node Version Manager&#xff09; 是一个用于管理多个 Node.js 版本的工具&#xff0c;允许开发者在同一台机器上轻松安装、切换和使用不同版本的 Node.js。它特别适合需要同时维护多个项目&#xff…

计算机组成原理笔记(十六)——4.1基本算术运算的实现

计算机中最基本的算术运算是加法运算&#xff0c;加、减、乘、除运算最终都可以归结为加法运算。 4.1.1加法器 一、加法器的基本单元 加法器的核心单元是 全加器&#xff08;Full Adder, FA&#xff09;&#xff0c;而所有加法器都由 半加器&#xff08;Half Adder, HA&…

利用Qt创建一个模拟问答系统

界面&#xff1a; 添加了聊天显示区域&#xff08;QTextEdit&#xff09; 添加了发送按钮和清空对话按钮 优化了布局和窗口大小添加了时间戳显示 2、功能&#xff1a; 支持实时对话可以清空对话历史 支持按回车发送消息 添加了简单的关键词匹配响应系统 交互体验&#x…