C++学习笔记(32)

三十四、双链表
示例:
#include <iostream>
using namespace std;
typedef int ElemType; // 自定义链表的数据元素为整数。
struct LNode // 双链表的结点。
{
ElemType data; // 存放结点的数据元素。
struct LNode* prior,*next; // 前驱和后继结点的指针。
};
// 初始化链表,返回值:失败返回 nullptr,成功返回头结点的地址。
LNode* InitList()
{
LNode* head = new (std::nothrow) LNode; // 分配头结点。
if (head == nullptr) return nullptr; // 内存不足,返回失败。
head->prior = head->next = nullptr; // 前驱后继结点都置为空。
return head; // 返回头结点。
}
// 销毁链表。代码和单链表相同。
void DestroyList(LNode* head)
{
// 销毁链表是指释放链表全部的结点,包括头结点。
LNode* tmp;
while (head != nullptr)
{
tmp = head->next; // tmp 保存下一结点的地址。
delete head; // 释放当前结点。
head = tmp; // 指针移动到下一结点。
}
}
// 在链表的头部插入元素(头插法),返回值:false-失败;true-成功。
bool PushFront(LNode* head, const ElemType& ee)
{
if (head == nullptr) { cout << "链表不存在。\n"; return false; }
LNode* tmp = new (std::nothrow) LNode; // 分配一个新结点。
if (tmp == nullptr) return false;
tmp->data = ee; // 把元素的值存入新结点。
// 处理 prior 和 next 指针。
tmp->prior = head; // 当前结点的 prior 指针指向 head。
tmp->next = head->next; // 当前结点的 next 指针指向 head->next。
tmp->prior->next = tmp; // 让前驱结点的 next 指针指向自己。
// head->next = tmp;
// 让后继结点的 prior 指针指向自己。注意:如果当前结点是最后一个结点,tmp->next 根本
不存在。
if (tmp->next != nullptr) tmp->next->prior = tmp;
return true;
}
// 显示链表中全部的元素。代码和单链表相同。
void PrintList(const LNode* head)
{
if (head == nullptr) { cout << "链表不存在。\n"; return; }
LNode* pp = head->next; // 从第 1 个结点开始。
while (pp != nullptr)
{
cout << pp->data << " "; // 如果元素为结构体,这行代码要修改。
pp = pp->next; // 指针往后移动一个结点。
}
cout << endl;
}
// 在链表的尾部插入元素(尾插法),返回值:false-失败;true-成功。
bool PushBack(LNode* head, const ElemType& ee)
{
if (head == nullptr) { cout << "链表不存在。\n"; return false; }
LNode* pp = head; // 从头结点开始。
// 找到最后一个结点,pp 将指向尾结点(最后一个结点)。
while (pp->next != nullptr) pp = pp->next;
LNode* tmp = new (std::nothrow) LNode; // 分配一个新结点。
if (tmp == nullptr) return false;
tmp->data = ee; // 把元素的值存入新结点。
// 处理 prior 和 next 指针。
tmp->prior = pp; // 当前结点的 prior 指针指向 head。
tmp->next = nullptr; // 当前结点的 next 指针指向 head->next。
tmp->prior->next = tmp; // 让前驱结点的 next 指针指向自己。
// 既然是尾插法,那么就肯定没有后继结点,不需要处理它的 prior 指针。
return true;
}
// 求链表的表长,返回值:>=0-表 LL 结点的个数。代码和单链表相同。
size_t ListLength(LNode* head)
{
//if (head == nullptr) { cout << "链表不存在。\n"; return 0; }
//LNode* pp = head->next; // 头结点不算,从第 1 个结点开始。
//size_t length = 0;
//while (pp != nullptr) { pp = pp->next; length++; }
//return length;
// 不使用临时变量,如何计算链表(包括头结点)的长度?
if (head==nullptr) return 0;
return ListLength(head->next)+1;
}
// 删除链表第一个结点。
bool PopFront(LNode* head)
{
if (head == nullptr) { cout << "链表不存在。\n"; return false; }
if (head->next == nullptr) { cout << "链表为空,没有结点。\n"; return false; }
LNode* pp = head->next; // pp 指向第一个节点。
head->next = head->next->next; // 修改头结点的 next 指针。
// 让后继结点的 prior 指针指向头结点。
// 注意:需要特殊处理,如果待删除的结点是最后一个结点,head->next 根本不存在。
if (head->next != nullptr) head->next->prior = head;
delete pp; // 删除第一个节点。
return true;
}
// 删除链表最后一个结点。代码和单链表相同。
bool PopBack(LNode* head)
{
if (head == nullptr) { cout << "链表不存在。\n"; return false; }
// 必须加上这个判断,否则下面的循环 pp->next->next 不成立。
if (head->next == nullptr) { cout << "链表为空,没有结点。\n"; return false; }
LNode* pp = head; // 从头结点开始。
// 找到倒数第二个结点(包括头结点)。
while (pp->next->next != nullptr) pp = pp->next;
delete pp->next; // 释放最后一个结点。
pp->next = nullptr; // 倒数第二个结点成了最后一个结点。
return true;
}
// 清空链表,清空链表是指释放链表全部的结点,但是,不释放头结点。代码和单链表相同。
void ClearList(LNode* head)
{
if (head == nullptr) { cout << "链表不存在。\n"; return; } // 判断链表是否存在。
LNode* tmp1;
LNode* tmp2 = head->next; // 从头结点的下一个结点开始释放。
while (tmp2 != nullptr)
{
tmp1 = tmp2->next;
delete tmp2;
tmp2 = tmp1;
}
head->next = nullptr; // 这行代码一定不能少,否则会留下野指针。
}
// 查找元素 ee 在链表中的结点地址,如果没找到返回 nullptr,否则返回结点的地址。代码和单链
表相同。
LNode* LocateElem(const LNode* head, const ElemType& ee)
{
LNode* pp = head->next; // 从第 1 个存放数据结点开始。
while (pp != nullptr)
{
// 如果数据元素是结构体,以下代码要修改成比较关键字。
if (pp->data == ee) return pp;
pp = pp->next;
}
return pp;
}
// 获取链表中第 n 个结点,成功返回结点的地址,失败返回 nullptr。
// 注意,n 可以取值为 0,表示头结点。代码和单链表相同。
LNode* LocateNode(LNode* head, unsigned int n)
{
if (head == nullptr) { cout << "链表不存在。\n"; return nullptr; }
LNode* pp = head; // 指针 pp 指向头结点,逐步往后移动,如果为空,表示后面没结
点了。
unsigned int ii = 0; // ii 指向的是第几个结点,从头结点 0 开始,pp 每向后移动一次,
ii 就加 1。
while ((pp != nullptr) && (ii < n))
{
pp = pp->next; ii++;
}
if (pp == nullptr) { cout << "位置" << n << "不合法,超过了表长。\n"; return nullptr; }
return pp;
}
// 在指定结点 pp 之后插入元素 ee。
bool InsertNextNode(LNode* pp, const ElemType& ee)
{
if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }
LNode* tmp = new LNode;
tmp->data = ee;
tmp->next = pp->next;
tmp->prior = pp;
tmp->prior->next = tmp; // 让前驱结点的 next 指针指向自己。
// 让后继结点的 prior 指针指向自己。注意当前结点是最后一个结点,tmp->next 根本不存在。
if (tmp->next != nullptr) tmp->next->prior = tmp;
return true;
}
// 在指定结点 pp 之前插入元素 ee。
bool InsertPriorNode(LNode* pp, const ElemType& ee)
{
if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }
LNode* tmp = new LNode;
tmp->data = ee;
tmp->next = pp;
tmp->prior = pp->prior;
tmp->prior->next = tmp; // 让前驱结点的 next 指针指向自己。
tmp->next->prior = tmp; // 让后继结点的 prior 指针指向自己,不需要特别处理。
return true;
}
// 删除指定结点。
bool DeleteNode(LNode* pp)
{
if (pp == nullptr) { cout << "结点 pp 不存在。\n"; return false; }
pp->prior->next = pp->next; // 让前驱结点的 next 指针指向 pp 的后继结点。
// 让后继结点的 prior 指针指向前驱结点。注意,如果当前结点是最后一个结点,pp->next 根
本不存在。
if (pp->next != nullptr) pp->next->prior = pp->prior;
delete pp;
return true; // 可以在函数外面调用 PopBack()函数。
}
int main()
{
LNode* LL = InitList(); // 初始化链表 LL。
cout << "用头插法向链表中插入元素(1、2、3)。\n";
PushFront(LL, 1);
PushFront(LL, 2);
PushFront(LL, 3);
PrintList(LL); // 把链表中全部的元素显示出来。
cout << "用尾插法向链表中插入元素(4、5、6)。\n";
PushBack(LL, 4);
PushBack(LL, 5);
PushBack(LL, 6);
PrintList(LL); // 把链表中全部的元素显示出来。
cout << "链表的表长(包括头结点):" << ListLength(LL) << endl;
//PopFront(LL); PopFront(LL); PopFront(LL); PopFront(LL); PopFront(LL); PopFront(LL);
PopFront(LL);
//PrintList(LL); // 把链表中全部的元素显示出来。
//PopBack(LL); PopBack(LL); PopBack(LL); PopBack(LL); PopBack(LL); PopBack(LL);
PopBack(LL);
//PrintList(LL); // 把链表中全部的元素显示出来。
//ClearList(LL);
//PrintList(LL); // 把链表中全部的元素显示出来。
LNode *p1=LocateElem(LL, 4);
cout << "元素为 4 的结点的地址是:" << p1 << ",值是:" << p1->data << endl;
LNode* p2 = LocateNode(LL, 3);
cout << "位序为 3 的结点的地址是:" << p2 << ",值是:" << p2->data << endl;
cout << "在" << p2->data << "之后插入元素 8。" << endl;
InsertNextNode(p2, 8);
PrintList(LL); // 把链表中全部的元素显示出来。
cout << "在" << p2->data << "之前插入元素 7。" << endl;
InsertPriorNode(p2, 7);
PrintList(LL); // 把链表中全部的元素显示出来。
cout << "删除元素" << p2->data << "。" << endl;
DeleteNode(p2);
PrintList(LL); // 把链表中全部的元素显示出来。
DestroyList(LL); // 销毁链表 LL。
}
 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Unity 高亮插件Highlight Plus快速入门

通常你可能只是想简单的为某个物体高亮显示 我将介绍最简单快速的方法 如果你想了解更多 Unity 高亮插件HighlightPlus介绍-CSDN博客 情景一 如果你只是想某个物体被鼠标覆盖显示高亮效果,鼠标离开高亮效果消失的话,你甚至不需要写一句代码! 为物体添加这两个脚本(添加T…

Mac 上哪个剪切板增强工具比较好用? 好用剪切板工具推荐

在日常文字编辑中&#xff0c;我们经常需要重复使用复制的内容。然而&#xff0c;新内容一旦复制&#xff0c;旧内容就会被覆盖。因此&#xff0c;选择一款易用高效的剪贴板工具成为了许多人的需求。本文整理了一些适用于 macOS 系统的优秀剪贴板增强工具&#xff0c;欢迎大家下…

C++ | Leetcode C++题解之第419题棋盘上的战舰

题目&#xff1a; 题解&#xff1a; class Solution { public:int countBattleships(vector<vector<char>>& board) {int row board.size();int col board[0].size();int ans 0;for (int i 0; i < row; i) {for (int j 0; j < col; j) { if (board…

Cesium Shader 广告牌纹理动画

Cesium Shader 广告牌纹理动画 Cesium 在广告牌, 自定义shader实现播放spritesheet. 图片资源: https://www.codeandweb.com/free-sprite-sheet-packer Cesium Shader 广告牌纹理动画

计算机网路(应用层)

客户/服务方式&#xff08;C/S&#xff09;方式和对等方式&#xff08;P2P方式&#xff09; 客户/服务器方式&#xff08;Client/Server&#xff0c;C/S&#xff09;方式 客户/服务器是指通信中所涉及的两个应用进程。 客户/服务器方式所描述的是进程之间的服务和被服务的关…

基于Android Studio 蜜雪冰城(奶茶饮品点餐)—原创

目录 一、项目演示 二、开发环境 三、项目详情 四、项目完整源码 一、项目演示 本项目素材、数据和布局页面参考均来自《蜜雪冰城》&#xff0c;在此特别声明感谢&#xff01; 基于Android Studio 蜜雪冰城(奶茶饮品)—原创 二、开发环境 三、项目详情 1.启动页 这段代码是…

软件安全最佳实践:首先关注的地方

尽管组织拥有大量可用的工具&#xff0c;但应用程序安全性仍然不足。 最近的数据显示&#xff0c;在过去四到五年中&#xff0c;软件供应链攻击同比增长了 600-700%&#xff0c;超过一半的美国企业在过去 12 个月中遭受过某种形式的软件供应链攻击。 为何应用程序安全工作未…

LeetCode[中等] 142. 环形链表 II

给定一个链表的头节点 head &#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整…

Elasticsearch:一次生产集群 ES Watcher 失效的深度排查与分析 - 全过程剖析与解决方案

作者&#xff1a;尚雷&#xff0c;TechTalk 技术交流社区创办者 一次生产集群 ES Watcher 失效的深度排查与分析 全过程剖析与解决方案​​ 一、Elasticsearch Watcher 介绍 1.1 Watcher 概念概述 Watcher 是 Elasticsearch 提供的一项监控和告警服务&#xff0c;允许用户定义…

末端回路漏电监测仪为何不可或缺?

末端回路漏电监测仪 接地故障保护器 智能电力继电器 智能型剩余电流继电器 智能动作保护器 在2022年8月14日一个寻常的午后&#xff0c;庄南地的一片豆角田边&#xff0c;发生了一场令人痛心的意外。杨某与其父亲杨某某正忙于灌溉作物&#xff0c;却不料&#xff0c;一场本可避…

Vue3.0组合式API:依赖注入provide和inject实现跨层组件的通信

Vue3.0组合式API系列文章&#xff1a; 《Vue3.0组合式API&#xff1a;setup()函数》 《Vue3.0组合式API&#xff1a;使用reactive()、ref()创建响应式代理对象》 《Vue3.0组合式API&#xff1a;computed计算属性、watch监听器、watchEffect高级监听器》 《Vue3.0组合式API&…

TypeScript异常处理

1.异常的概念 程序运行中意外发生的情况就成为异常 例子&#xff1a; //除法运算function chu(num1:number,num2:number){if(num20){//throw 抛出异常throw new Error(除数不能为零)}let num:numbernum1/num2console.log(num) }//程序出现异常后会停止运行// 捕获异常try{ /…

论文《Mixture of Weak Strong Experts on Graphs》笔记

【Mowst 2024 ICLR】论文提出了一种新的图神经网络架构&#xff0c;称为Mixture of weak and strong experts&#xff08;Mowst&#xff09;&#xff0c;通过将轻量级的多层感知机&#xff08;MLP&#xff09;作为弱专家和现成的GNN作为强专家相结合&#xff0c;以处理图中的节…

Linux云计算 |【第四阶段】NOSQL-DAY1

主要内容&#xff1a; NoSQL概述&#xff08;RDBMS、NoSQL&#xff09;、部署Redis服务、Redis数据类型&#xff08;字符串、散列类型、列表类型、集合类型、有序集合类型&#xff09;、Redis其它操作命令、修改Redis服务运行参数、部署支持PHP和Redis的Nginx服务器 一、NoSQL…

4G模组SIM双卡切换是徒增成本,还是未雨绸缪?

初学开发的小伙伴提出疑问&#xff1a;手机双卡可以理解&#xff0c;物联网设备有必要双卡吗&#xff0c;会不会太浪费&#xff1f; 但在实际应用中&#xff0c;双卡是必需的。 在使用4G模组双卡功能的场景下&#xff0c;切换卡槽更是一个关键环节——关乎设备在不同网络环境…

【设计模式-享元】

Flyweight Pattern&#xff08;享元模式&#xff09; 是一种结构型设计模式&#xff0c;旨在通过共享对象来减少内存使用和提高性能。享元模式特别适用于需要大量相似对象的场景&#xff0c;可以有效地减少内存开销。 核心思想 享元模式通过将对象的共享部分&#xff08;共享…

关于单片机的技术原理及应用

成长路上不孤单&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a; 【14后&#x1f60a;///C爱好者&#x1f60a;///持续分享所学&#x1f60a;///如有需要欢迎收藏转发///&#x1f60a;】 今日分享关于单片机的技术原理及应用的相关内容&…

ANSYS Workbench蜂窝板泰森多边形Voronoi结构建模

在ANSYS Workbench内基于Voronoi算法建立泰森多边形蜂窝状结构板模型可采用CAD Voronoi插件建模后将模型导入。 在插件内设置好模型参数后运行&#xff0c;插件会自动在CAD内完成Voronoi图形的绘制。 将长方形与Voronoi晶格分别生成面域并做差集&#xff0c;形成Voronoi框架…

【JAVA开源】基于Vue和SpringBoot的校园美食分享平台

本文项目编号 T 033 &#xff0c;文末自助获取源码 \color{red}{T033&#xff0c;文末自助获取源码} T033&#xff0c;文末自助获取源码 目录 一、系统介绍二、演示录屏三、启动教程四、功能截图五、文案资料5.1 选题背景5.2 国内外研究现状5.3 可行性分析 六、核心代码6.1 查…

多层感知机paddle

多层感知机——paddle部分 本文部分为paddle框架以及部分理论分析&#xff0c;torch框架对应代码可见多层感知机 import paddle print("paddle version:",paddle.__version__)paddle version: 2.6.1多层感知机&#xff08;MLP&#xff0c;也称为神经网络&#xff0…