C++相关概念和易错语法(16)(list)

1.list易错点

(1)慎用list的sort,list的排序比vector慢得多,尽管两者时间复杂度一样,甚至不如先把list转为vector,用vector排完序后再转为list

(2)splice是剪切链表,将x的部分剪切到pos后,也可以自己剪自己的

(3)list迭代器不支持[]运算符,但相比较vector多支持了push_front和pop_front,原因在于[]效率不高,push_front和pop_front效率高

2.list模拟实现

list实现中我们会用到多个模板,注意每个template定义的模板参数都只能供当前类或函数使用,不存在一个文件中所有T都是一个意思

(1)结点

为了适应不同数据的存储,我们采用模板的方式来定义结点。为了能够体现封装特性,我们将创建结点并赋值作为一个构造函数写入类ListNode中。因为在接下来的class list中会频繁调用lt->next,即在外部访问LIstNode的成员变量,所以我们的ListNode使用struct,默认访问限定符是public

(2)无参构造和析构

我们的list是带头双向循环链表,所以所有的构造都需要定义一个哨兵位

还有很多带参的构造,虽然这里我们可以去实现,但是它们需要使用到的功能和后面的函数相符合,所以我会先实现后续的函数。

析构只需要遍历所有节点,将它们释放即可

(3)迭代器

list的迭代器和指针就拉开了差距,list的数据是不连续存储的,因此无法使用指针的加减来遍历list。我们需要定义一个iterator的类,在这个类里重载运算符++、*等操作,使它们在使用过程中和指针一致但能够正常访问数据。

我们使用ListNode*作为结点的标识,当++时就调用它的_next,--就调用它的_prev,*就返回对应_data的值。有了这个思路,迭代器的大部分功能我们都可以顺利实现了。

template<class T, class T1 = T>struct List_iterator{typedef ListNode<T> Node;typedef List_iterator<T, T1> iterator;List_iterator(Node* node):_curnode(node){}iterator operator++(){_curnode = _curnode->_next;return _curnode;}iterator operator--(){_curnode = _curnode->_prev;return _curnode;}		iterator operator++(int){_curnode = _curnode->_next;return _curnode->_prev;}		iterator operator--(int){_curnode = _curnode->_prev;return _curnode->_next;}T1& operator*()//对于const T无法进行修改{return _curnode->_data;}T1* operator->()//对于const T无法进行修改{return &(_curnode->_data);}bool operator!=(const List_iterator lt) const{return _curnode != lt._curnode;}bool operator==(const List_iterator lt) const{return _curnode == lt._curnode;}Node* _curnode;};

但是这里面还有一些代码需要解释

a.operator->()

要理解这里,我们要思考迭代器本身想要模仿的是指向数据的指针,如果存储类型是int,对应iterator和int*的使用方式一模一样,类似地,像char、double、int*等内置类型是这样,那么自定义类型呢?operator->()就是为了模仿自定义类型的指针而专门设计的。

我们先将T1就看成T,T是一个结构体,里面有自己的成员变量,当我们使用结构体指针想要访问它们时,我们首先要拿到这个结构体的地址,再用->访问,这里的iterator也是如此,_data是结构体类型,我们直接先指向这个结构体内容本身,即_curnode->_data,这个表达式的返回值是一个结构体,也就是我们想要访问的结构体,但是我们是要模拟指针的操作,要使用->而不是.,所以我们要再对它取地址,即&(_curnode->_data),这样返回的就是一个指向_data的指针了,当我们调用it.operator->()->(成员变量)时就能访问结构体内部的成员变量了,简化为it->(成员变量),省略了一个->为了易读性。

b.T1和T的区分

在我们想要创建一个const_iterator时,T1的出现就很重要了。

当不传第二个模板参数时,默认和第一个一样,如果传了const T,那就使用传的参数

对于这两个要返回指针或引用的函数,加上const修饰T可以防止T被修改。那为什么不直接用const T一个参数呢?要注意const int和int是两个类型,对于一些自定义类型来说有很大区别,所以要分成两块来写。

有了上面的基础,反向迭代器也可以很快的完成,思路就是复用,用刚刚实现的正向迭代器稍加修改得到。

template<class T, class T1 = T>struct List_reverse_iterator{typedef ListNode<T> Node;typedef List_iterator<T, T1> iterator;typedef List_reverse_iterator<T, T1> reverse_iterator;List_reverse_iterator(Node* node):_it(node){}List_reverse_iterator(iterator it):_it(it){}reverse_iterator operator++(){return --_it;}reverse_iterator operator--(){return ++_it;}reverse_iterator operator++(int){return _it--;}reverse_iterator operator--(int){return _it++;}T1& operator*()//对于const T无法进行修改{return *_it;}T1* operator->()//对于const T无法进行修改{return &(*_it);}bool operator!=(const List_reverse_iterator lt) const{return _it != lt._it;}bool operator==(const List_reverse_iterator lt) const{return _it == lt._it;}iterator _it;};

(4)push_back

push_back是非常关键的一个函数,在构造函数中,我们经常用到它

先用val创建一个newnode,找到尾节点并修改尾节点和哨兵位的_prev和_next使得newnode插入链表中

(5)insert

我们实现insert的方式是先实现一个可复用性最强的函数(迭代器版本的insert),再用它实现其它函数

后续的函数只需要调整参数复用即可

iterator insert(iterator pos, int n, int val)//特殊处理,防止定位错误
{return insert(pos, (size_t)n, (T)val);
}iterator insert(iterator pos, size_t n, const T& val)
{list<T> tmp;for (size_t count = 0; count < n; count++){tmp.push_back(val);}return insert(pos, tmp.begin(), tmp.end());
}		iterator insert(iterator pos, const T& val)
{return insert(pos, 1, val);
}

其中有一个函数需要解释

当我们调用insert(lt.begin(), 1, 3)时,是想在首位置插入1个3,但是编译器会将1,3识别成迭代器(注意看我们之前已经实现了模板函数,1和3同类型是会匹配的),size_t和const int&虽然都能作为1和3的类型,但它们都不叫完美匹配,不完美匹配就会去找模板函数。

注意各大整型家族的区别,10u是unsigned int类型。

因此我们只有针对这一种情况进行单独处理,将它们强转后就可以匹配正确的函数了。

(6)erase

我们的思路也是先实现一个可复用性最强的,再对其它函数复用。这些函数逻辑都很简单,不做过多讲解。

(7)带参构造

这里我们就可以发现,只要有了push_back,insert,我们实现这些带参构造就很快了,我们要学会复用,即实现最关键的几个函数,其它的函数用这几个核心的函数套用就可以了,这不仅节省了我们的时间,还使得代码易于维护,失误率降低。

list(size_t n, const T& val)
{_head = new Node;_head->_next = _head->_prev = _head;for (size_t count = 0; count < n; count++){push_back(val);}
}list(const list<T>& lt)
{_head = new Node;_head->_next = _head->_prev = _head;for (const auto& e : lt){push_back(e);}
}template<class Init>
list(Init first, Init last)
{_head = new Node;_head->_next = _head->_prev = _head;insert(begin(), first, last);
}

(8)全部代码(包含一些边缘性的函数)

注意模板函数是按需实例化,只检查调用的实例化的函数,不调用就不实例化

#pragma once#include <iostream>
#include <algorithm>
#include <assert.h>using namespace std;namespace my_list
{template<class T>struct ListNode{ListNode(const T& data = T()):_data(data), _next(nullptr), _prev(nullptr){}T _data;ListNode<T>* _next;ListNode<T>* _prev;};template<class T, class T1 = T>struct List_iterator{typedef ListNode<T> Node;typedef List_iterator<T, T1> iterator;List_iterator(Node* node):_curnode(node){}iterator operator++(){_curnode = _curnode->_next;return _curnode;}iterator operator--(){_curnode = _curnode->_prev;return _curnode;}		iterator operator++(int){_curnode = _curnode->_next;return _curnode->_prev;}		iterator operator--(int){_curnode = _curnode->_prev;return _curnode->_next;}T1& operator*()//对于const T无法进行修改{return _curnode->_data;}T1* operator->()//对于const T无法进行修改{return &(_curnode->_data);}bool operator!=(const List_iterator lt) const{return _curnode != lt._curnode;}bool operator==(const List_iterator lt) const{return _curnode == lt._curnode;}Node* _curnode;};template<class T, class T1 = T>struct List_reverse_iterator{typedef ListNode<T> Node;typedef List_iterator<T, T1> iterator;typedef List_reverse_iterator<T, T1> reverse_iterator;List_reverse_iterator(Node* node):_it(node){}List_reverse_iterator(iterator it):_it(it){}reverse_iterator operator++(){return --_it;}reverse_iterator operator--(){return ++_it;}reverse_iterator operator++(int){return _it--;}reverse_iterator operator--(int){return _it++;}T1& operator*()//对于const T无法进行修改{return *_it;}T1* operator->()//对于const T无法进行修改{return &(*_it);}bool operator!=(const List_reverse_iterator lt) const{return _it != lt._it;}bool operator==(const List_reverse_iterator lt) const{return _it == lt._it;}iterator _it;};template<class T>class list{public:typedef ListNode<T> Node;typedef List_iterator<T> iterator;typedef List_iterator<T, const T> const_iterator;typedef List_reverse_iterator<T> reverse_iterator;typedef List_reverse_iterator<T, const T> const_reverse_iterator;list(){_head = new Node;_head->_next = _head->_prev = _head;}list(size_t n, const T& val){_head = new Node;_head->_next = _head->_prev = _head;for (size_t count = 0; count < n; count++){push_back(val);}}list(const list<T>& lt){_head = new Node;_head->_next = _head->_prev = _head;for (const auto& e : lt){push_back(e);}}template<class Init>list(Init first, Init last){_head = new Node;_head->_next = _head->_prev = _head;insert(begin(), first, last);}~list(){Node* cur = _head->_next, * next = _head->_next->_next;while (cur != _head){delete cur;cur = next, next = next->_next;}delete _head;_head = nullptr;}size_t size(){size_t count = 0;for (const auto& e : *this){count++;}return count;}bool empty(){return _head->_next == _head;}list<T>& operator=(const list<T>& lt){if (lt._head != _head){clear();for (const auto& e : lt){push_back(e);}}return *this;}T& front(){return *begin();}		T& back(){return *(--end());}		const T& front() const{return *begin();}		const T& back() const{return *(--end());}void swap(const list<T>& lt){std::swap(_head, lt._head);}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}		const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin(){return reverse_iterator(_head->_prev);}reverse_iterator rend(){return reverse_iterator(_head);}const_reverse_iterator rbegin() const{return const_reverse_iterator(_head->_prev);}const_reverse_iterator rend() const{return const_reverse_iterator(_head);}void push_back(const T& val){Node* newnode = new Node(val);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}		void push_front(const T& val){insert(begin(), 1, val);}void pop_front(){erase(begin());}void pop_back(){erase(--end());}template<typename Input>iterator insert(iterator pos, Input first, Input last){Node* cur = pos._curnode->_prev, * next = cur->_next;Node* ret = cur;while (first != last){Node* newnode = new Node((T)(*first));cur->_next = newnode, newnode->_prev = cur;newnode->_next = next, next->_prev = newnode;cur = cur->_next;first++;}return ret->_next;//新插入的第一个结点对应的迭代器,有隐式类型转换}iterator insert(iterator pos, int n, int val)//特殊处理,防止定位错误{return insert(pos, (size_t)n, (T)val);}iterator insert(iterator pos, size_t n, const T& val){list<T> tmp;for (size_t count = 0; count < n; count++){tmp.push_back(val);}return insert(pos, tmp.begin(), tmp.end());}		iterator insert(iterator pos, const T& val){return insert(pos, 1, val);}iterator erase(iterator first, iterator last){Node* cur = first._curnode->_prev, * next = last._curnode;iterator nextit = first;while (first != last){nextit++;delete first._curnode, first = nextit;}cur->_next = next, next->_prev = cur;return cur->_next;}iterator erase(iterator pos){iterator cur = pos;return erase(pos, ++cur);}void clear(){erase(begin(), end());}private:Node* _head;};}

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

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

相关文章

指数增长远大于nlgn

在学习算法导论的时候&#xff0c;遇到了这么一行字把我难住了。我不理解为什么叶节点代价总和就为Ω(nlgn)了&#xff0c;后来经过学习之后了解了&#xff0c;因为n的指数严格大于1&#xff0c;只要指数函数的指数大于1就是指数增长&#xff0c;那么就远大于nlgn。

C++ | Leetcode C++题解之第22题完全二叉树的节点个数

题目&#xff1a; 题解&#xff1a; class Solution { public:int countNodes(TreeNode* root) {if (root nullptr) {return 0;}int level 0;TreeNode* node root;while (node->left ! nullptr) {level;node node->left;}int low 1 << level, high (1 <&…

【笔记】finalshell中使用nano编辑器GNU

ctrl O 保存 enter 确定 ctrl X 退出 nano编辑 能不用就不用吧 因为我真用不习惯 nano编辑的文件也可以用vim编辑的

Social to Sales全链路,数说故事专享会开启出海新视角

————瞎出海&#xff0c;必出局 TikTok&#xff0c;这个充满活力的短视频平台&#xff0c;已经成为全球范围内不可忽视的电商巨头。就在6月8日&#xff0c;TikTok美区带货直播诞生了首个“百万大场”。在此之前&#xff0c;百万GMV被视为一道难以逾越的高墙。以TikTok为首的…

CentOS 7遗忘了root密码怎么办?

正文共&#xff1a;666 字 12 图&#xff0c;预估阅读时间&#xff1a;1 分钟 说来也巧&#xff0c;突然发现使用KVM在部署CentOS时&#xff08;笔记本电脑安装CentOS系统&#xff09;&#xff0c;会有一个神奇的现象&#xff0c;还不是偶然出现的&#xff0c;在最近的三四次部…

4种叶轮平衡技巧 提高精度,降低故障率

在风机运作时&#xff0c;叶轮的动平衡是关键因素之一&#xff0c;不平衡的叶轮会产生振动和噪音&#xff0c;影响风机性能&#xff0c;甚至可能导致故障。 因此&#xff0c;掌握合适的平衡技术对提高设备稳定性和延长使用寿命至关重要。 本文将探讨几种有效的叶轮平衡方法及…

java中Request和Response的详细介绍

1.Request和Response的概述 # 重点 1. service方法的两个参数request和response是由tomcat创建的void service(ServletRequest var1, ServletResponse var2) 2. request 表示请求数据, tomcat将浏览器发送过来的请求数据解析并封装到request对象中servlet开发者可以通过reques…

基于AWS Billing Conductor自定义账单计算进行【linker账单】RI/SP还原以及账单菜单栏选择性精细化限制策略设置

文章目录 一、客户需求需求① 设置策略屏蔽billing菜单选项查看需求② 账单RI和SP还原及SP和RI的共享 二、AWS Billing Conductor介绍三、IAM 精细操作映射参考四、详细步骤操作演示4.1 AWS Organization策略设置4.2 账单和成本管理设置4.3 AWS Billing Conductor设置4.3.1 创建…

allWebPlugin中间件实现ActiveX插件在谷歌、火狐、Edge浏览器使用

下载并安装allWebPlugin中间件 1、请从下面地址下载allWebPlugin中间件产品&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1xUyQDzOabh7mU7J7TYhtig?pwdz3q0 提取码&#xff1a;z3q0 如下图所示&#xff0c;下载最新allWebPlugin_x86_v2.0.0.14_stable_20240707…

LiveNVR监控流媒体Onvif/RTSP用户手册-录像计划:批量配置、单个配置、录像保存(天)、配置时间段录像

TOC 1、录像计划 支持单个通道 或是 通道范围内配置支持快速滑选支持录像时间段配置 1.1、录像存储位置如何配置&#xff1f; 2、RTSP/HLS/FLV/RTMP拉流Onvif流媒体服务 支持 Windows Linux 及其它CPU架构&#xff08;国产、嵌入式…&#xff09;操作系统安装包下载 、 安装…

Hospital Management System v4.0 SQL 注入漏洞(CVE-2022-24263)

前言 CVE-2022-24263 是一个影响 Hospital Management System (HMS) v4.0 的 SQL 注入漏洞。这个漏洞允许攻击者通过注入恶意 SQL 代码来获取数据库的敏感信息&#xff0c;甚至可能控制整个数据库。以下是对这个漏洞的详细介绍&#xff1a; 漏洞描述 在 Hospital Management…

【MindSpore学习打卡】应用实践-自然语言处理-深入理解LSTM+CRF在序列标注中的应用

在自然语言处理(NLP)领域&#xff0c;序列标注是一项重要的任务。其目标是为给定的输入序列中的每个Token分配一个标签。序列标注的应用范围广泛&#xff0c;包括分词、词性标注、命名实体识别(NER)等。在本文中&#xff0c;我们将探讨如何利用LSTM和CRF模型进行序列标注&#…

无人机之穿越机知识篇

一、定义和类型 穿越机&#xff0c;即FPV Drone或Racing Drone&#xff0c;是一种主要通过第一人称视角&#xff08;FPV&#xff09;进行操作的无人机。这种无人机通常配备有四个电机和相应的飞控系统&#xff0c;使其具有极高的飞行自由度和速度。穿越机主要分为竞速型和花飞…

electron在VSCode和IDEA及webStrom等编辑器控制台打印日志乱码

window10环境下设置 1.打开Windows设置 2.打开时间和语言&#xff0c;选择语言菜单、如何点击管理语言设置 3.打开之后选择管理&#xff0c;选择更改系统区域设置&#xff0c;把Beta版&#xff1a;使用Unicode UTF-8提供全球语言支持 勾上&#xff0c;点击确定&#xff0c;…

甄选范文“论区块链技术及应用”,软考高级论文,系统架构设计师论文

论文真题 区块链作为一种分布式记账技术,目前已经被应用到了资产管理、物联网、医疗管理、政务监管等多个领域。从网络层面来讲,区块链是一个对等网络(Peer to Peer, P2P),网络中的节点地位对等,每个节点都保存完整的账本数据,系统的运行不依赖中心化节点,因此避免了中…

MySQL 9.0 新功能概览

官方文档 https://dev.mysql.com/doc/refman/9.0/en/mysql-nutshell.html 时隔 6 年多&#xff0c;上周 Oracle 发布了 MySQL 最新的大版本 9.0。我们一起来看看新版本有哪些东西。 用 JavaScript 写存储过程 半年前已经单独介绍过 「虽迟但到&#xff01;MySQL 可以用 Java…

方圆资源网,方圆资源官网

在当今这个信息化高速发展的时代&#xff0c;方圆资源网络已成为推动社会进步、促进经济发展的重要力量。方圆资源网不仅汇聚了海量的信息资源&#xff0c;更为我们提供了一个高效、便捷的信息交流平台。本文旨在详细介绍资源网的概念、特点、功能以及其在现代社会中的重要意义…

DeepMind的JEST技术:AI训练速度提升13倍,能效增强10倍,引领绿色AI革命

谷歌旗下的人工智能研究实验室DeepMind发布了一项关于人工智能模型训练的新研究成果&#xff0c;声称其新提出的“联合示例选择”&#xff08;Joint Example Selection&#xff0c;简称JEST&#xff09;技术能够极大地提高训练速度和能源效率&#xff0c;相比其他方法&#xff…

华为乾崑智驾加持:深蓝S07首次亮相

最近&#xff0c;特斯拉FSD即将入华的消息&#xff0c;让智能驾驶成为了汽车行业热议的焦点&#xff0c;而当新能源汽车的代表企业深蓝汽车&#xff0c;与全球领先的华为乾崑智驾强强联手&#xff0c;一场颠覆性的智能出行变革也已蓄势待发。 7月8日&#xff0c;深蓝汽车携其最…

[数仓]四、离线数仓(Hive数仓系统-续)

第8章 数仓搭建-DWT层 8.1 访客主题 1)建表语句 DROP TABLE IF EXISTS dwt_visitor_topic; CREATE EXTERNAL TABLE dwt_visitor_topic (`mid_id` STRING COMMENT 设备id,`brand` STRING COMMENT 手机品牌,`model` STRING COMMENT 手机型号,`channel` ARRAY<STRING> C…