数据结构 --链表

article/2025/7/14 20:22:16

前言

  今天把链表重新用c++写了一遍,首先单纯的写一个链表并不困难,无非是定义一个结构体ListNode,设置变量data和下一个指针的地址next,然后完成增删查改的操作,需要注意的是在删除节点的时候记得先保存当前需要删除的节点的下一个节点,然后再删除当前节点并链接节点,否则会导致找不到删除的节点的下一个节点,无法链接。释放链表的时候需要遍历析构,不能直接简单将第一个节点释放而不管剩下的节点了。

  废了我一番时间的是在如何将链表作为一个新容器加入进我前几篇写的通讯录这个项目中。一开始我想的应该不是很困难,应该和顺序表一样只要调用对应的接口就能正确完成任务栏,事与愿违,在调用链表的find的返回值是链表的当前节点指针,并不是数据的地址T*,和一开始Contact类的公共接口有出入:

T* find(const T& x)//查找
{T* pos = cont->Find(x);if (pos != nullptr){cout << "Sucessly Find : " << endl;return pos;}else{cout << "No Find" << endl;return nullptr;}
}

  当然可以直接去特化一个ListNode*参数的find,但是这违背了封装上层的本意,应该修改下层对应上层的接口而不是修改上层,而且此时Contact为了适配不同的容器我加了两个参数模板,此时Contact<K<T>>,而这个Contact是并不知道它需要接受的是什么容器,这正是c++封装的一个大特征,我只需要提供调用上层的接口就能实现下层功能。我思考过用auto推导pos的返回值,但是nullptr和ListNode的类型不相符,所以就放弃了。那么还有什么方法可以自己去推导返回值呢?搜索了一下发现一个叫做traist的操作,即特性萃取。Traits 是一种编译期类型推导技术,用于根据输入类型关联或提取其他类型或值。这里的目标是:给定一个容器类型如(SqList<T>或者List),自动推导出它的查找结果类型,如(T*或者ListNode<T>*),这就能够解决了上诉的问题了。

  使用方法:

  然后将返回值改成FindResult就好了,这里其实也是类模板的实现,只不过这里是用类型实例化出不同的类,然后实例不同的FindResult,没有具体的类其他的操作。

为什么需要 Traits?

  解耦容器与算法:算法(如查找)不需要知道容器的具体实现,只需通过 Traits 获取所需类型。

  通用代码:同一套代码可以适配多种容器类型,只需为每种容器特化 FindResultType

  然后第二个地方是修改这个地方的内容:

void change(const string& name)//修改
{auto pos = find(name);int size = cont->size();if(pos){//cin >> *pos;错误cout << "Sucessly Change" << endl;}else{cout << "Change False, NO Person" << endl;}
}

  由于链表中的Find是返回ListNode*的指针,和顺序表以及内置类型直接返回储存数据的地址不同,对ListNode*指针解引用并不能得到数据。有的人会说了直接重载ListNode的解引用不就好了,确实我也尝试过去重载,然后返回*pos,但是这里不能直接使用*pos返回,因为pos假如是一个ListNode<T>*指针并且重载了解引用操作,需要解引用两次才能找到data数据,但是对于自定义类型来说这个操作是不允许的,而为了保证上层代码的统一性就没有进行特化,这里我使用的是仿函数的方式来解决这个问题。

  所以我将函数模板又增加了一个类型,增加了一个默认的仿函数针对内置类型和直接能够返回T*的容器类型,对于自定义类型增加了特殊的仿函数,例如链表或者map这些有奇效。

template<typename T>
struct KeyOfT
{T& operator()(T* t) const  //当数据内容是int,char等内置类型使用默认的仿函数{return *t;}
};template<typename T>
struct LOT//仿函数获取链表节点数据
{T& operator()(ListNode<T>* pos){return pos->_data;}
};

  所以目前的Contact类被拓展成了这样,并且我PersonInfo作为单独的文件方便管理。

#pragma once
#include<string.h>
#include"traist.h"template<typename T>
struct KeyOfT
{T& operator()(T* t) const  //当数据内容是int,char等内置类型使用默认的仿函数{return *t;}
};template <typename T, template<typename> class K, typename F= KeyOfT<T>>//将储存数据的容器作为模板嵌套
class Contact
{   //traist操作,确保传入不同类型能够返回对应的数据类型using ContainerType = K<T>;using FindResult = typename FindResultType<ContainerType>::type;public:Contact():cont(new K<T>())//指针:不会自动构造,必须用new{}~Contact(){delete cont;cont = nullptr;}void push()//增加{T data;cin >> data;cont->PushBack(data);}void push(T data){cont->PushBack(data);}void print()//打印{cont->Print();}void pop(T x){auto pos = cont->Find(x);int size = cont->size();if (pos != nullptr){cout << "Sucessly Erase" << endl;cont->Erase(pos);}else{cout << "Erase False, NO Person" << endl;}}void pop(const string& name)//删除{auto pos =cont->Find(name);int size = cont->size();if (pos != nullptr){cout << "Sucessly Erase" << endl;cont->Erase(pos);}else{cout << "Erase False, NO Person" << endl;}}FindResult find(const string& name)//查找{FindResult pos = cont->Find(name);if (pos != nullptr){cout << "Sucessly Find : " << endl;return pos;}else{cout << "No Find" << endl;return nullptr;}}T* find(const T& x)//查找{T* pos = cont->Find(x);if (pos != nullptr){cout << "Sucessly Find : " << endl;return pos;}else{cout << "No Find" << endl;return nullptr;}}void change(const string& name)//修改{auto pos = find(name);int size = cont->size();if(pos){cin >> kot(pos);cout << "Sucessly Change" << endl;}else{cout << "Change False, NO Person" << endl;}}void change(const T& x){auto pos = find(x);int size = cont->size();if (pos){/*cin >> kot(pos);*/cin >> kot(pos);cout << "Sucessly Change" << endl;}else{cout << "Change False, NO Person" << endl;}}public:F kot;private:K<T>* cont;
};

以上仅为我自己在学习过程中的一些思路,如果有误或者可以优化的地方欢迎提出,完整的项目地址放在仓库中了,感兴趣可以查看。

study_code: 学习路上写的一些代码


http://www.hkcw.cn/article/yyKXECWtXA.shtml

相关文章

【原理扫描】不安全的crossdomain.xml文件和CORS(跨站资源共享)原始验证失败验证与彻底方案

不安全的crossdomain.xml文件【原理扫描】 CORS(跨站资源共享)原始验证失败【原理扫描】 吐槽一下 该漏扫是通过内网漏扫部署服务进行扫描内网minio端口探测到该minio配置不当造成的威胁。 通过nginx配置无效&#xff0c;且必须从MINIO本身解决&#xff1b;MINIO配置存在兼容…

【Web应用】若依框架:基础篇11功能详解-系统接口

文章目录 ⭐前言⭐一、课程讲解⭐二、自己动手实操⭐总结 标题详情作者JosieBook头衔CSDN博客专家资格、阿里云社区专家博主、软件设计工程师博客内容开源、框架、软件工程、全栈&#xff08;,NET/Java/Python/C&#xff09;、数据库、操作系统、大数据、人工智能、工控、网络、…

每日一题:H指数

继续给大家带来每日一题 题目描述 给你一个整数数组 citations &#xff0c;其中 citations[i] 表示研究者的第 i 篇论文被引用的次数。计算并返回该研究者的 h 指数。 根据维基百科上 h 指数的定义&#xff1a;h 代表“高引用次数” &#xff0c;一名科研人员的 h 指数 是指…

MPU9250_WE库详解

MPU9250_WE库 https://docs.arduino.cc/libraries/mpu9250_we/ https://github.com/wollewald/MPU9250_WE 三款MPU6050、MPU6500、MPU9250陀螺仪 其初始化以及函数应用方法基本一致,创建初始化对象名称有所差异 初始化相关函数 用于初始化传感器。该函数会尝试与传感器建…

onlyoffice docspace 协作空间企业版使用秘籍-1.如何连接外部存储

背景介绍 ONLYOFFICE 协作空间是一个可在自定义房间中协作处理文本文档、电子表格、演示文稿、表格和 PDF 文件的平台。用户可设置灵活的访问权限&#xff0c;与同事、客户及合作伙伴共享房间和文档。最棒的一个功能是支持文档的中文全文检索功能,方便根据内容找到需要的文档。…

openppp2 -- 1.0.0.25225 优化多线接入运营商路由调配

本文涉及到的内容&#xff0c;涉及到上个发行版本相关内容&#xff0c;人们在阅读本文之前&#xff0c;建议应当详细阅读上个版本之中的VBGP技术相关的介绍。 openppp2 -- 1.0.0.25196 版本新增的VBGP技术-CSDN博客 我们知道在现代大型的 Internet 网络服务商&#xff0c;很多…

Predixy的docker化

概述 当前已有一套redis cluster的集群&#xff0c;但是fs中的hiredis只能配置单实例redis。 AI了一下方案&#xff0c;可以使用redis的proxy组件来实现从hiredis到redis cluster的互通。 代码地址&#xff1a;https://github.com/joyieldInc/predixy Predixy特性介绍&…

Fusion引擎赋能:流利说如何用阿里云Serverless Spark实现数仓计算加速

作者&#xff1a;流利说 Ibson&#xff08;大数据负责人&#xff09;/ Bruce&#xff08;数据工程师&#xff09; 背景介绍 行业 流利说是领先的科技驱动的教育公司&#xff0c;公司自主研发了领先的英语口语评测、写作打分引擎和深度自适应学习系统&#xff0c;致力于为用户…

Leetcode 340. 至多包含 K 个不同字符的最长子串

1.题目基本信息 1.1.题目描述 给你一个字符串 s 和一个整数 k &#xff0c;请你找出 至多 包含 k 个 不同 字符的最长子串&#xff0c;并返回该子串的长度。 1.2.题目地址 https://leetcode.cn/problems/longest-substring-with-at-most-k-distinct-characters/description…

历年中南大学计算机保研上机真题

2025中南大学计算机保研上机真题 2024中南大学计算机保研上机真题 2023中南大学计算机保研上机真题 在线测评链接&#xff1a;https://pgcode.cn/school 进制转换 题目描述 请写出一段程序&#xff0c;将十进制数字转为八进制。 输入格式 第一行输入 T T T ( 1 ≤ T ≤…

【js逆向】某某省过验证码逆向

查看响应 查看验证码包 send里面就是载荷的密文 向上跟栈 进入到i里面 找到params的加密处 断点&#xff0c;刷新 提示少扣取 报错&#xff1a;st is not defined 全部扣完后发现不报错但是没输出&#xff1a; 一个难以发觉的错误&#xff0c;大量扣取&#xff0c;不容易被发现…

土耳其总统:支持俄乌代表团的谈判继续推进

当地时间5月30日,土耳其总统埃尔多安与乌克兰总统泽连斯基通电话。双方讨论了两国双边关系以及地区与全球局势。△土耳其总统埃尔多安(资料图)埃尔多安在对话中表示,土耳其将继续努力推动乌克兰与俄罗斯之间实现公正与持久的和平。土耳其支持两国代表团在伊斯坦布尔开启的谈…

下一代液晶显示底层技术与九天画芯的技术突围

一、液晶产业&#xff1a;撑起数字经济的显示脊梁 &#xff08;一&#xff09;全球显示市场的核心支柱 作为电子信息产业的战略基石&#xff0c;液晶显示&#xff08;LCD&#xff09;占据全球平板显示市场超 60% 的份额&#xff0c;2022 年全球市场规模达 782.41 亿元&#xf…

工控机安装lubuntu系统

工控机安装lubuntu系统指南手册 1. 准备 1个8G左右的U盘 下载Rufus&#xff1a; Index of /downloads 下载lubuntu系统镜像&#xff1a; NJU Mirror Downloads – Lubuntu 下载Ventoy工具&#xff1a; Releases ventoy/Ventoy GitHub 下载后&#xff0c;解压&#…

完整解析 Linux Kdump Crash Kernel 工作原理和实操步骤

完整解析 Linux Kdump Crash Kernel 工作原理和实操步骤 一、前言 在使用 Linux 操作系统进行内核开发或者系统维护时&#xff0c;内核 panic 是最常见的系统崩溃环节。如果想要在内核崩溃后立即分析环境和输出内核内存 dump&#xff0c;Kdump crashkernel 是最接近完美的解…

day 25 异常处理

异常处理机制 Python 的异常处理机制赋予程序强大的容错能力。当程序在运行时遇到意外情况&#xff08;即异常&#xff09;&#xff0c;它不会直接崩溃&#xff0c;而是可以被设计成优雅地处理错误&#xff0c;或继续执行后续逻辑&#xff0c;或按可控方式结束。 在异常发生时…

智能流体仿真软件AICFD 2025R1新版本功能介绍

智能流体仿真软件AICFD是天洑软件自主研发的一款通用型智能热流体仿真工具&#xff0c;其核心代码拥有完全自主知识产权。该软件在业界率先引入人工智能技术&#xff0c;高效解决工业级流动、传热、多相流、噪声及燃烧等复杂仿真问题。 图1 AICFD软件界面 一、版本更新介绍 A…

数据结构之队列:原理与应用

一、基本原理 队列是一种特殊的线性表队列是一个有序表(可以用数组或链表实现)遵循“先来先服务”的原则&#xff0c;它只允许在表的前端&#xff08;队头&#xff09;进行删除操作&#xff0c;在表的后端&#xff08;队尾&#xff09;进行插入操作 (一) 核心操作 入队&…

windows下安装docker、dify、ollama

一、docker安装 镜像源配置 {"builder": {"gc": {"defaultKeepStorage": "10GB","enabled": true}},"experimental": true,"registry-mirrors": ["https://docker.m.daocloud.io","ht…

mysql隐式转换会造成索引失效的原因

现在我们看一个例子 比如现在我有一张表叫做test 涉及的字段有id code name age address id 是int数值类型 code 是varchar字符串类型 name 是varchar字符串类型 age是int 数值类型 address是varchar 字符串类型 创建语句&#xff1a; CREATE TABLE test ( id INT …