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为首的…

224. 基本计算器

给你一个字符串表达式 s &#xff0c;请你实现一个基本计算器来计算并返回它的值。 注意:不允许使用任何将字符串作为数学表达式计算的内置函数&#xff0c;比如 eval() 。 示例 1&#xff1a; 输入&#xff1a;s "1 1" 输出&#xff1a;2示例 2&#xff1a; 输入…

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…

力扣第224题“基本计算器”

在本篇文章中&#xff0c;我们将详细解读力扣第224题“基本计算器”。通过学习本篇文章&#xff0c;读者将掌握如何使用栈来解析和计算表达式&#xff0c;并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释&#xff0c;以便于理解。 问题描述 力扣第224题“…

单向链表的数据存储(申请堆空间)

函数功能&#xff1a; 0.排序&#xff08;逆置和顺序排序&#xff09; 1.回显 2.头插 3.位插 4.尾插 5.尾删 6.头删 7.位删 8.查找 &#xff08;按值或按位查找&#xff09; 9.修改 &#xff08;按值或按位修改&#xff09; 10.退出 main.c …

Win11系统vscode配置C语言环境

安装Visual Studio Code&#xff1a; 如果你还没有安装VSCode&#xff0c;请从官方网站下载并安装&#xff1a;https://code.visualstudio.com/ 安装C/C扩展&#xff1a; 打开VSCode&#xff0c;进入扩展视图&#xff08;点击侧边栏的扩展图标或使用快捷键CtrlShiftX&#xff…

基于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…

Redis 管道技术

Redis 管道技术 引言 Redis&#xff0c;作为一个高性能的键值存储系统&#xff0c;被广泛应用于各种场景&#xff0c;如缓存、消息队列等。为了进一步提高Redis的处理能力和效率&#xff0c;Redis管道技术应运而生。本文将深入探讨Redis管道技术的原理、应用及其优势。 什么…

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

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

【文件格式转换】Python转换 pdf 到 word (docx)

要使用 Python 将 PDF 转换为 Word 文档&#xff0c;可以使用库pdf2docx。操作方法如下&#xff1a; 安装必要的库&#xff1a; pip install pdf2docx使用以下 Python 代码将 PDF 文件转换为 Word 文档&#xff1a; from pdf2docx import Converterdef pdf_to_word(pdf_file…

调度pass的定制

概述 作为调度pass实现类&#xff0c;MachineScheduler 和 PostMachineScheduler 的优点之一是允许开发者在LLVM 已有调度算法和策略基础上做不同的定制化 PostMachineScheduler 类的定制相对简单&#xff0c;主要是MachineSchedStrategy 接口的定制。 MachineScheduler 类的…

【算法】Dijkstra算法

戴克斯特拉算法(Dijkstras Algorithm),由荷兰杰出计算机科学家艾兹赫尔戴克斯特拉设计,是解决非负权有向图中单源最短路径问题的经典算法。它构建了一个逐步扩展的最短路径树,从指定的源点出发,逐步探索并确定到图中所有其他顶点的最短路径。这一算法在网络路由选择、地理…

C语言旋转动画

目录 开头程序程序的流程图C语言旋转动画(程序的效果)结尾 开头 大家好&#xff0c;我叫这是我58。在这里&#xff0c;我们要来看我用C语言编译成的可以顺时针或者逆时针旋转的动画相关的一些东西和那个动画的顺时针与逆时针的版本。 程序 #define _CRT_SECURE_NO_WARNINGS …

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

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