数据结构之链表(双链表)

目录

一、双向带头循环链表

概念

 二、哨兵位的头节点

优点:

头节点的初始化

三、带头双向链表的实现

1.双链表的销毁

2.双链表的打印

3.双链表的尾插和头插

尾插:

头插:

4.双链表的尾删和头删

尾删:

头删:

5.双链表的查找

四、测试代码


一、双向带头循环链表

概念

        名如其实,这个链表结构有哨兵位头节点,双向并且循环,结构最复杂。一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带 来很多优势,实现反而简单了,后面我们代码实现了就知道了这里我们用一张图片就能很好的看清楚双向带头循环链表的结构了。

 二、哨兵位的头节点

        从上一篇文章我们就在说带头/不带头,那么这个头是什么呢?其实它就是哨兵位的头节点。这个节点一般在一个链表的最前方的位置,不用来储存数据。

优点:

1.    简化边界条件处理:
•    在没有哨兵节点的情况下,链表的头插、头删等操作需要特别处理头节点为空的情况。
•    使用哨兵节点后,头节点始终存在,简化了插入和删除操作的逻辑,不需要单独处理头节点为空的情况。

2.    统一操作逻辑:
•    无论是头插、尾插、头删还是尾删,操作逻辑都可以统一处理,不需要区分是否是第一个节点。
•    例如,插入操作总是插入到哨兵节点之后,删除操作总是删除哨兵节点之后的节点。

3.    提高代码可读性和维护性:
•    由于边界条件处理简化,代码逻辑更加清晰,减少了出错的可能性。
•    代码维护起来也更加方便,因为不需要在多个地方处理特殊情况。
4.    便于实现某些算法:
•    在某些算法中,使用哨兵节点可以避免多次检查链表是否为空的情况,提高算法的效率。

头节点的初始化

// 创建返回链表的头结点.
ListNode* ListCreate()
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->data = 0;newnode->next = newnode;newnode->prev = newnode;return newnode;
}

三、带头双向链表的实现

1.双链表的销毁

        与单链表的销毁类似,需要定义一个指针来遍历整个链表,但是注意,如果是从头节点开始遍历,我们会因为无法很好的控制停止条件而导致无限循环,所以我们从头节点的下一个开始遍历,当这个cur指针指向头节点时就停止,这里后面还会反复用到,请务必想清楚。

// 双向链表销毁
void ListDestory(ListNode* plist)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){ListNode* next = cur->next;free(cur);cur = next;}free(plist);plist = NULL;
}

2.双链表的打印

// 双向链表打印
void ListPrint(ListNode* plist)
{ListNode* cur = plist->next;while (cur != plist){printf("%d<=>",cur->data);cur = cur->next;}printf("NULL\n");
}

3.双链表的尾插和头插

尾插:

// 双向链表尾插
void ListPushBack(ListNode* plist, LTDataType x)
{// 双向链表的找尾很简单,只需要指向plist的前一个节点就行ListNode* newnode = buyNewnode(x);ListNode* tail = plist->prev;tail->next = newnode;newnode->prev = tail;newnode->next = plist;plist->prev = newnode;
}

头插:

// 双向链表头插
void ListPushFront(ListNode* plist, LTDataType x)
{assert(plist);ListNode* newnode = buyNewnode(x);ListNode* head = plist->next;plist->next = newnode;newnode->prev = plist;newnode->next = head;head->prev = newnode;
}

4.双链表的尾删和头删

尾删:

// 双向链表尾删
void ListPopBack(ListNode* plist)
{assert(plist);if (plist->next == plist){return;}ListNode* tail = plist->prev;ListNode* prev = tail->prev;free(tail);tail = NULL;prev->next = plist;plist->prev = prev;
}

头删:

// 双向链表头删
void ListPopFront(ListNode* plist)
{assert(plist);if (plist->next = plist){return;}ListNode* head = plist->next;ListNode* newHead = head->next;free(head);head = NULL;plist->next = newHead;newHead->prev = plist;
}

5.双链表的查找

// 双向链表查找
ListNode* ListFind(ListNode* plist, LTDataType x)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}

四、测试代码


// 2、带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType data;struct ListNode* next; struct ListNode* prev;
}ListNode;
// 创建返回链表的头结点.
ListNode* buyNewnode(LTDataType x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->data = x;newnode->next = NULL;newnode->prev = NULL;return newnode;
}
ListNode* ListCreate()
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc failed");exit(-1);}newnode->data = 0;newnode->next = newnode;newnode->prev = newnode;return newnode;
}
// 双向链表销毁
void ListDestory(ListNode* plist)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){ListNode* next = cur->next;free(cur);cur = next;}free(plist);plist = NULL;
}
// 双向链表打印
void ListPrint(ListNode* plist)
{ListNode* cur = plist->next;while (cur != plist){printf("%d<=>",cur->data);cur = cur->next;}printf("NULL\n");
}
// 双向链表尾插
void ListPushBack(ListNode* plist, LTDataType x)
{// 双向链表的找尾很简单,只需要指向plist的前一个节点就行ListNode* newnode = buyNewnode(x);ListNode* tail = plist->prev;tail->next = newnode;newnode->prev = tail;newnode->next = plist;plist->prev = newnode;
}
// 双向链表尾删
void ListPopBack(ListNode* plist)
{assert(plist);if (plist->next == plist){return;}ListNode* tail = plist->prev;ListNode* prev = tail->prev;free(tail);tail = NULL;prev->next = plist;plist->prev = prev;
}
// 双向链表头插
void ListPushFront(ListNode* plist, LTDataType x)
{assert(plist);ListNode* newnode = buyNewnode(x);ListNode* head = plist->next;plist->next = newnode;newnode->prev = plist;newnode->next = head;head->prev = newnode;
}
// 双向链表头删
void ListPopFront(ListNode* plist)
{assert(plist);if (plist->next = plist){return;}ListNode* head = plist->next;ListNode* newHead = head->next;free(head);head = NULL;plist->next = newHead;newHead->prev = plist;
}
// 双向链表查找
ListNode* ListFind(ListNode* plist, LTDataType x)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
int main()
{ListNode* plist = ListCreate();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPrint(plist);ListPopBack(plist);ListPrint(plist);ListPushFront(plist, 0);ListPrint(plist);ListPopFront(plist);ListPrint(plist);return 0;
}

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

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

相关文章

内存取证之windows-Volatility 3

一&#xff0c;Volatility 3下载 1.安装Volatility 3。 要求&#xff1a;python3.7以上的版本&#xff0c;我的是3,11&#xff0c;这里不说python的安装方法 使用 pip 安装 Volatility 3&#xff1a; pip install volatility3 安装完成后&#xff0c;验证安装&#xff1a; v…

Unity的JSON工具类+LitJson的引入及使用

C#使用JSON数据 数据存储&#xff08;序列化&#xff09;&#xff1a;将C#的数据格式&#xff0c;转化为JSON字符串&#xff0c;存储或传输 数据使用&#xff08;反序列化&#xff09;&#xff1a;将JSON字符串中存储的数据&#xff0c;转化为C#可用的数据格式&#xff0c;实现…

WX小程序

下载 package com.sky.utils;import com.alibaba.fastjson.JSONObject; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.Cl…

MyBatis 中 #{} 和 ${} 的区别详解

目录 1. #{} 和 ${} 的基本概念 1.1 #{} 1.2 ${} 2. #{} 和 ${} 的工作原理 2.1 #{} 的工作原理 2.2 ${} 的工作原理 3.共同点&#xff1a;动态 SQL 查询 4. 区别&#xff1a;处理方式和适用场景 4.1 处理方式 4.2 适用场景 &#xff08;1&#xff09;#{} 的适用场景…

【蓝桥杯速成】| 10.回溯切割

前面两篇内容我们都是在做有关回溯问题的组合应用 今天的题目主题是&#xff1a;回溯法在切割问题的应用 题目一&#xff1a;分割回文串 问题描述 131. 分割回文串 - 力扣&#xff08;LeetCode&#xff09; 给你一个字符串 s&#xff0c;请你将 s 分割成一些 子串&#xff…

数据结构之双向链表-初始化链表-头插法-遍历链表-获取尾部结点-尾插法-指定位置插入-删除节点-释放链表——完整代码

数据结构之双向链表-初始化链表-头插法-遍历链表-获取尾部结点-尾插法-指定位置插入-删除节点-释放链表——完整代码 #include <stdio.h> #include <stdlib.h>typedef int ElemType;typedef struct node{ElemType data;struct node *next, *prev; }Node;//初化链表…

开源视频剪辑工具,无损编辑更高效

LosslessCut 是一款基于 FFmpeg 开发的跨平台开源视频剪辑工具&#xff0c;致力于无损处理音视频文件。它无需重新编码即可完成剪切、合并、轨道编辑等操作&#xff0c;极大地保留了原始文件的质量&#xff0c;特别适合处理大体积视频&#xff0c;如无人机拍摄素材或长时录制内…

Java:Apache HttpClient中HttpRoute用法的介绍

当使用Apache HttpClient组件时&#xff0c;经常会用到它的连接池组件。典型的代码如下&#xff1a; PoolingHttpClientConnectionManager connectionManager new PoolingHttpClientConnectionManager();connectionManager.setMaxTotal(httpConfig.getMaxPoolTotal());connect…

EasyRTC嵌入式音视频通信SDK:WebRTC技术下的硬件与软件协同演进,开启通信新时代

在当今数字化时代&#xff0c;智能设备的普及和人们对实时通信需求的不断增长&#xff0c;推动了嵌入式音视频通信技术的快速发。EasyRTC嵌入式音视频通信SDK凭借其独特的技术特点和应用优势&#xff0c;在嵌入式设备和多平台实时通信领域脱颖而出。 1、轻量级设计与高性能 Ea…

Uthana,AI 3D角色动画生成平台

Uthana是什么 Uthana 是专注于3D角色动画生成的AI平台。平台基于简单的文字描述、参考视频或动作库搜索&#xff0c;快速为用户生成逼真的动画&#xff0c;支持适配任何骨骼结构的模型。Uthana 提供风格迁移、API集成和定制模型训练等功能&#xff0c;满足不同用户需求。平台提…

Python:多线程创建的语法及步骤

线程模块&#xff1a;import threading 线程类Thread参数&#xff1a;group(线程组) target&#xff1a;执行的目标的任务名 args&#xff1a;以元组的方式给执行任务进行传参 *args可以传任意多个参数 kwargs以字典方式给执行任务传参 name&#xff1a;线程名 步骤&…

Jupyter Notebook 常用命令(自用)

最近有点忘记了一些常见命令&#xff0c;这里就记录一下&#xff0c;懒得找了。 文章目录 一、文件操作命令1. %cd 工作目录2. %pwd 显示路径3. !ls 列出文件4. !cp 复制文件5. !mv 移动或重命名6. !rm 删除 二、代码调试1. %time 时间2. %timeit 平均时长3. %debug 调试4. %ru…

快速入手-基于Django的Form和ModelForm操作(七)

1、Form组件 2、ModelForm操作 3、给前端表单里在django里添加class相关属性值 4、前端 5、后端form 新增数据处理 6、更新数据处理

【Linux系统】Linux权限讲解!!!超详细!!!

目录 Linux文件类型 区分方法 文件类型 Linux用户 用户创建与删除 用户之间的转换 su指令 普通用户->超级用户(root) 超级用户(root) ->普通用户 普通账户->普通账户 普通用户的权限提高 sudo指令 注&#xff1a; Linux权限 定义 权限操作 1、修改文…

剑指小米特斯拉:秦L EV上市11.98万起

3月23日&#xff0c;比亚迪王朝网推出全新中级纯电轿车秦L EV&#xff0c;价格区间为11.98万-13.98万元&#xff0c;瞬间火爆市场。 依托e平台3.0 Evo技术赋能&#xff0c;秦L EV以“国潮设计、智能座舱、越级空间、高效安全、高阶智驾”五大核心优势&#xff0c;直击年轻用户痛…

嵌入式学习(31)-Lora模块A39C-T400A30D1a

一、概述 A39C-T400A30D1a是一款410~490MHz&#xff0c;1W&#xff0c;具有高稳定性&#xff0c;工业级的无线串口模块。LORA扩频调制&#xff0c;实测传输距离最远可达10K米。该模块具备数据广播、数据监听、定点传输、主从模式、自动中继、定点唤醒等传输方式&#xff0c;支…

使用__attribute__((at(addr))) 固定变量到指定 Flash 地址

文章目录 一、代码示例&#xff1a;将变量固定到 Flash 0x08001000二、__attribute__((at(addr))) 的作用三、__attribute__((at(addr))) 可能导致的问题四、运行时修改 Flash 存储的变量五、在 GCC&#xff08;STM32CubeIDE&#xff09;中实现同样功能 在嵌入式开发中&#xf…

vmware虚拟机快照、克隆、迁移区别说明

一、快照 1.1 快照概念 记录了虚拟机在某个特定时间点的状态(软件部署、网络配置、照片备份、游戏存档等) 1.2快照用途 可以在需要时轻松地恢复虚拟机到快照创建时的状态。 备份和恢复:快速备份虚拟机状态的方法可以在数据丢失或损坏时快速恢复虚拟机到先前的状态。测试和…

面试常问系列(一)-神经网络参数初始化

一、背景 说到参数初始化&#xff0c;先提一下大家常见的两个概念梯度消失和梯度爆炸。 &#xff08;一&#xff09;、梯度消失&#xff1a;深层网络的“静默杀手” 定义&#xff1a; 在反向传播过程中&#xff0c;梯度值随着网络层数增加呈指数级衰减&#xff0c;最终趋近…

使用CSS3实现炫酷的3D翻转卡片效果

使用CSS3实现炫酷的3D翻转卡片效果 这里写目录标题 使用CSS3实现炫酷的3D翻转卡片效果项目介绍技术要点分析1. 3D空间设置2. 核心CSS属性3. 布局和定位 实现难点和解决方案1. 3D效果的流畅性2. 卡片内容布局3. 响应式设计 性能优化建议浏览器兼容性总结 项目介绍 在这个项目中…