C++ 复习总结记录九

C++ 复习总结记录九

主要内容

1、list 介绍及使用

2、list 剖析及模拟实现

3、list 与 vector 对比

一 list 介绍及使用

List 相关文档

1、List 在任意位置进行插入和删除的序列式容器 O(1) ,且该容器可前后双向迭代

2、List 底层是带头双向循环链表,每个元素存储在互不相关的独立节点中,通过指针指向其前一个元素和后一个元素

3、List 与 Forward_List 相似,主要不同在于 Forward_List 是单链表,只能正向迭代更简单高效

4、与其他的序列式容器相比 ( array,vector,deque ),List 通常在任意位置进行插入、移除元素的执行效率更好。

但缺陷是不支持任意位置的随机访问,比如:要访问 List 的第 6 个元素,必须从已知位置 ( 比如头部或者尾部 ) 迭代到该位置,在这段位置上迭代需要线性的时间开销;List 还需要一些额外的空间,以保存每个节点的相关联信息 ( 对于存储类型较小元素的大 List 来说这可能是一个重要的因素 )

image-20250123234509727

1.1 list 构造

构造函数(constructor) 				接口说明
list (size_type n, const value_type& val = value_type()) 构造的list中包含n个值为val的元素list() 构造空的list
list (const list& x) 拷贝构造函数
list (InputIterator first, InputIterator last) 用[first, last)区间中的元素构造list

1.2 list iterator 的使用

迭代器其实就是节点的指针,但 list 指针的类型应该是 NODE* 而非 T* 需特别注意(像 string,vector 的迭代器就是 T*)

函数声明 					接口说明
begin + end  返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend 返回第一个元素的 reverse_iterator, 即 end 位置,返回最后一个元素下一个位置的 reverse_iterator, 即 begin 位置

begin 和 end 为正向迭代器,对迭代器执行 ++ 操作,迭代器向后移动

rbegin 与 rend 为反向迭代器,对迭代器执行 ++ 操作,迭代器向前移动

如下图,这里要注意 list 迭代器的 begin + end 和 vector 的区别(list 拥有头节点,所以 begin 返回第一个元素的迭代器,即头节点后一个位置)

image-20250123235347303

1.3 list capacity

函数声明 					接口说明
empty 			检测 list 是否为空, 是返回 true, 否则返回 false
size 			返回 list 中有效节点的个数

1.4 list element access

函数声明 					接口说明
front 			返回 list 的第一个节点中值的引用
back 			返回 list 的最后一个节点中值的引用

1.5 list modifiers

函数声明 				接口说明
push_front 			在 list 首元素前插入值为 val 的元素
pop_front 			删除 list 中第一个元素
push_back 			在 list 尾部插入值为 val 的元素
pop_back 			删除 list 中最后一个元素
insert 				在 list position 位置中插入值为 val 的元素
erase 				删除 list position 位置的元素
swap 				交换两个 list 中的元素
clear 				清空 list 中的有效元素

1.6 list 迭代器失效

迭代器失效即迭代器所指向的节点的无效,list 底层结构为带头结点的双向循环链表,因此在 list 中进行插入时不会导致 list 的迭代器失效,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

void TestListIterator()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase() 函数执行后, it 所指向的节点已被删除, 因此 it 无效, 在下一次使用 it 时, 必须先给其赋值l.erase(it); ++it;}
}// 改正
void TestListIterator()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){l.erase(it++); // it = l.erase(it);}
}

二 list 剖析及模拟实现

2.1 模拟实现

迭代器有两种实现方式,具体应根据容器底层数据结构实现

① 原生态指针,比如 vector

② 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法

  1. 指针可以解引用,迭代器的类中必须重载 operator*()
  2. 指针可以通过 -> 访问其所指空间成员,迭代器类中必须重载 oprator->()
  3. 指针可以 ++ 向后移动,迭代器类中必须重载 operator++() 与 operator++(int),至于 operator–() / operator–(int) 根据具体结构抉择,双向链表可以向前移动,需要重载,forward_list 则不需重载
  4. 迭代器需要进行是否相等的比较,因此还需要重载 operator==() 与 operator!=()
#pragma once#include <iostream>
using namespace std;
#include <assert.h>namespace lucky
{// List 的节点类template<class T>struct ListNode{ListNode(const T& val = T()): _prev(nullptr), _next(nullptr), _val(val){}ListNode<T>* _prev;ListNode<T>* _next;T _val;};template<class T, class Ref, class Ptr>class ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;// Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到public:typedef Ref Ref;typedef Ptr Ptr;public:// 构造ListIterator(Node* node = nullptr): _node(node){}// 具有指针类似行为Ref operator*() { return _node->_val;}Ptr operator->() { return &(operator*()); }// 迭代器支持移动Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self temp(*this);_node = _node->_next;return temp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self temp(*this);_node = _node->_prev;return temp;}// 迭代器支持比较bool operator!=(const Self& l)const{ return _node != l._node;}bool operator==(const Self& l)const{ return _node == l._node;}Node* _node;};template<class Iterator>class ReverseListIterator{// 注意:此处 typename 的作用是明确告诉编译器, Ref 是 Iterator 类中的一个类型,而不是静态成员变量// 否则编译器编译时就不知道 Ref 是 Iterator 中的类型还是静态成员变量// 因为静态成员变量也是按照 类名::静态成员变量名的方式访问的public:typedef typename Iterator::Ref Ref;typedef typename Iterator::Ptr Ptr;typedef ReverseListIterator<Iterator> Self;public:// 构造ReverseListIterator(Iterator it): _it(it){}// 具有指针类似行为Ref operator*(){Iterator temp(_it);--temp;return *temp;}Ptr operator->(){return &(operator*());}// 迭代器支持移动Self& operator++(){--_it;return *this;}Self operator++(int){Self temp(*this);--_it;return temp;}Self& operator--(){++_it;return *this;}Self operator--(int){Self temp(*this);++_it;return temp;}// 迭代器支持比较bool operator!=(const Self& l)const{return _it != l._it;}bool operator==(const Self& l)const{return _it == l._it;}Iterator _it;};template<class T>class list{typedef ListNode<T> Node;public:// 正向迭代器typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;// 反向迭代器typedef ReverseListIterator<iterator> reverse_iterator;typedef ReverseListIterator<const_iterator> const_reverse_iterator;public:// List 的构造list(){CreateHead();}list(int n, const T& value = T()){CreateHead();for (int i = 0; i < n; ++i)push_back(value);}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*first);++first;}}list(const list<T>& l){CreateHead();// 用 l 中的元素构造临时的 temp, 然后与当前对象交换list<T> temp(l.begin(), l.end());this->swap(temp);}list<T>& operator=(list<T> l){this->swap(l);return *this;}~list(){clear();delete _head;_head = nullptr;}// List 的迭代器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(end());}reverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}// List 容量相关size_t size() const{Node* cur = _head->_next;size_t count = 0;while (cur != _head){count++;cur = cur->_next;}return count;}bool empty() const{return _head->_next == _head;}void resize(size_t newsize, const T& data = T()){size_t oldsize = size();if (newsize <= oldsize){// 有效元素个数减少到newsizewhile (newsize < oldsize){pop_back();oldsize--;}}else{while (oldsize < newsize){push_back(data);oldsize++;}}}// List 元素访问操作// 注意: List不支持 operator[]T& front(){return _head->_next->_val;}const T& front() const{return _head->_next->_val;}T& back(){return _head->_prev->_val;}const T& back() const{return _head->_prev->_val;}// List 插入和删除void push_back(const T& val) { insert(end(), val); }void pop_back() { erase(--end()); }void push_front(const T& val) { insert(begin(), val); }void pop_front() { erase(begin()); }// 在 pos 位置前插入值为 val 的节点iterator insert(iterator pos, const T& val){Node* pNewNode = new Node(val);Node* pCur = pos._node;// 先将新节点插入pNewNode->_prev = pCur->_prev;pNewNode->_next = pCur;pNewNode->_prev->_next = pNewNode;pCur->_prev = pNewNode;return iterator(pNewNode);}// 删除 pos 位置的节点, 返回该节点的下一个位置iterator erase(iterator pos){// 找到待删除的节点Node* pDel = pos._node;Node* pRet = pDel->_next;// 将该节点从链表中拆下来并删除pDel->_prev->_next = pDel->_next;pDel->_next->_prev = pDel->_prev;delete pDel;return iterator(pRet);}void clear(){Node* cur = _head->_next;// 采用头删除删除while (cur != _head){_head->_next = cur->_next;delete cur;cur = _head->_next;}_head->_next = _head->_prev = _head;}void swap(lucky::list<T>& l){std::swap(_head, l._head);}private:void CreateHead(){_head = new Node;_head->_prev = _head;_head->_next = _head;}private:Node* _head;};
}// 对模拟实现的 list 进行测试
// 正向打印链表
template<class T>
void PrintList(const lucky::list<T>& l)
{auto it = l.begin();while (it != l.end()){cout << *it << " ";++it;}cout << endl;
}// 测试 List 的构造
void TestList1()
{lucky::list<int> l1;lucky::list<int> l2(10, 5);PrintList(l2);int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };lucky::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));PrintList(l3);lucky::list<int> l4(l3);PrintList(l4);l1 = l4;PrintList(l1);
}// PushBack() / PopBack() / PushFront() / PopFront()
void TestList2()
{// 测试 PushBack 与 PopBacklucky::list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);PrintList(l);l.pop_back();l.pop_back();PrintList(l);l.pop_back();cout << l.size() << endl;// 测试 PushFront 与 PopFrontl.push_front(1);l.push_front(2);l.push_front(3);PrintList(l);l.pop_front();l.pop_front();PrintList(l);l.pop_front();cout << l.size() << endl;
}// 测试 insert 和 erase
void TestList3()
{int array[] = { 1, 2, 3, 4, 5 };lucky::list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto pos = l.begin();l.insert(l.begin(), 0);PrintList(l);++pos;l.insert(pos, 2);PrintList(l);l.erase(l.begin());l.erase(pos);PrintList(l);// pos 指向的节点已经被删除,pos迭代器失效cout << *pos << endl;auto it = l.begin();while (it != l.end()){it = l.erase(it);}cout << l.size() << endl;
}// 测试反向迭代器
void TestList4()
{int array[] = { 1, 2, 3, 4, 5 };lucky::list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto rit = l.rbegin();while (rit != l.rend()){cout << *rit << " ";++rit;}cout << endl;const lucky::list<int> cl(l);auto crit = l.rbegin();while (crit != l.rend()){cout << *crit << " ";++crit;}cout << endl;
}

2.2 list 反向迭代器

反向迭代器的 ++ 就是正向迭代器的 --,因此反向迭代器的实现可以借助正向迭代器,即反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可

template<class Iterator>class ReverseListIterator{public:typedef typename Iterator::Ref Ref;typedef typename Iterator::Ptr Ptr;typedef ReverseListIterator<Iterator> Self;public:// 构造ReverseListIterator(Iterator it): _it(it){}// 具有指针类似行为Ref operator*(){Iterator temp(_it);--temp;return *temp;}Ptr operator->(){ return &(operator*());}// 迭代器支持移动Self& operator++(){--_it;return *this;}Self operator++(int){Self temp(*this);--_it;return temp;}Self& operator--(){++_it;return *this;}Self operator--(int){Self temp(*this);++_it;return temp;}// 迭代器支持比较bool operator!=(const Self& l)const{ return _it != l._it;}bool operator==(const Self& l)const{ return _it ==l._it;}Iterator _it;};

三 list 与 vector 对比

vector 与 list 都是 STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下

image-20250124000408977

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

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

相关文章

速通JS中的函数作用域与全局污染

函数作用域与全局污染 在 JavaScript 编程实践中&#xff0c;函数作用域和全局污染是两个核心概念&#xff0c;对代码的健壮性、可维护性及协作开发的有效性具有深远影响。函数作用域通过限制变量的访问范围来减少干扰&#xff0c;而全局污染则可能导致命名冲突和难以排查的错…

从音频到 PDF:AI 全流程打造完美英文绘本教案

今天把英文绘本的自学教案自动生成流程完成了&#xff0c;我分享一下整个实现思路&#xff0c;让你也轻松搞定英文绘本教案的产出&#xff0c;让孩子的学习之路更加顺畅。  从音频到 PDF&#xff1a;AI 全流程打造完美英文绘本教案 一、音频转文本&#xff1a;AI 助力第一步 …

(5)STM32 USB设备开发-USB键盘

讲解视频&#xff1a;2、USB键盘-下_哔哩哔哩_bilibili 例程&#xff1a;STM32USBdevice: 基于STM32的USB设备例子程序 - Gitee.com 本篇为使用使用STM32模拟USB键盘的例程&#xff0c;没有知识&#xff0c;全是实操&#xff0c;按照步骤就能获得一个STM32的USB键盘。本例子是…

初步认识操作系统(Operator System)

目录 一、概念二、设计OS的目的三、定位四、操作系统上下的分级五、如何理解 "管理"六、总结 一、概念 任何计算机系统都包含一个基本的程序集合&#xff0c;称为操作系统(OS)。操作系统包括&#xff1a; 内核&#xff08;进程管理&#xff0c;内存管理&#xff0c…

LINUX 平台最快子网路由转发,内核使能选项配置

阅读本文之间&#xff0c;可线性参考以下文献。 Linux 命令行配置为单臂旁路由。_linux单臂路由-CSDN博客 Linux 软路由命令行配置&#xff08;参考&#xff09;_linux软路由-CSDN博客 VGW在 Windows 平台上局域网就绪的旁路由器程序_windows旁路由-CSDN博客 本文介绍 LINUX…

vue中onclick如何调用methods中的方法

文章目录 前言一、代码一开始效果二、解决方案 前言 今天在开发vue项目中使用的第三方地图&#xff0c;地图上绘制的marker内容需要自定义&#xff0c;因为绘制的内容是原生HTML&#xff0c;所以遇到点击事件的时候就用了onclick来定义&#xff0c;此时想要调用methods中的方法…

python创建一个httpServer网页上传文件到httpServer

一、代码 1.server.py import os from http.server import SimpleHTTPRequestHandler, HTTPServer import cgi # 自定义请求处理类 class MyRequestHandler(SimpleHTTPRequestHandler):# 处理GET请求def do_GET(self):if self.path /:# 响应200状态码self.send_response(2…

一文讲解Java中的重载、重写及里氏替换原则

提到重载和重写&#xff0c;Java小白应该都不陌生&#xff0c;接下来就通过这篇文章来一起回顾复习下吧&#xff01; 重载和重写有什么区别呢&#xff1f; 如果一个类有多个名字相同但参数不同的方法&#xff0c;我们通常称这些方法为方法重载Overload。如果方法的功能是一样…

Java Swing 基础组件详解 [论文投稿-第四届智能系统、通信与计算机网络]

大会官网&#xff1a;www.icisccn.net Java Swing 是一个功能强大的 GUI 工具包&#xff0c;提供了丰富的组件库用于构建跨平台的桌面应用程序。本文将详细讲解 Swing 的基础组件&#xff0c;包括其作用、使用方法以及示例代码&#xff0c;帮助你快速掌握 Swing 的核心知识。 一…

前端小案例——520表白信封

前言&#xff1a;我们在学习完了HTML和CSS之后&#xff0c;就会想着使用这两个东西去做一些小案例&#xff0c;不过又没有什么好的案例让我们去练手&#xff0c;本篇文章就提供里一个案例——520表白信封 ✨✨✨这里是秋刀鱼不做梦的BLOG ✨✨✨想要了解更多内容可以访问我的主…

PyTorch广告点击率预测(CTR)利用深度学习提升广告效果

目录 广告点击率预测问题数据集结构广告点击率预测模型的构建1. 数据集准备2. 构建数据加载器3. 构建深度学习模型4. 训练与评估 总结 广告点击率预测&#xff08;CTR&#xff0c;Click-Through Rate Prediction&#xff09;是在线广告领域中的重要任务&#xff0c;它帮助广告平…

【信息系统项目管理师-选择真题】2017上半年综合知识答案和详解

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 【第1题】【第2题】【第3题】【第4题】【第5题】【第6题】【第7~8题】【第9题】【第10题】【第11题】【第12题】【第13~14题】【第15题】【第16题】【第17题】【第18题】【第19题】【第20题】【第21题】【第22题…

Github 2025-01-25Rust开源项目日报Top10

根据Github Trendings的统计,今日(2025-01-25统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目1Vue项目1JavaScript项目1Deno: 现代JavaScript和TypeScript运行时 创建周期:2118 天开发语言:Rust, JavaScript协议类型…

每日一题--合并二叉树

合并二叉树 问题描述 已知两颗二叉树&#xff0c;将它们合并成一颗二叉树。合并规则是&#xff1a;如果节点存在于两棵树中&#xff0c;则将节点值相加&#xff1b;如果某个节点在一棵树中不存在&#xff0c;则直接使用另一棵树的节点值。例如&#xff1a; 两颗二叉树如下&a…

云计算的概念与特点:开启数字化时代的新篇章

在当今数字化时代,云计算(Cloud Computing)已经成为推动技术创新和业务转型的核心力量。无论是大型企业、中小型企业,还是个人用户,云计算都为其提供了高效、灵活和经济的解决方案。本文将深入探讨云计算的概念及其核心特点,帮助读者全面了解这一革命性技术。 © ivw…

Arcgis国产化替代:Bigemap Pro正式发布

在数字化时代&#xff0c;数据如同新时代的石油&#xff0c;蕴含着巨大的价值。从商业决策到科研探索&#xff0c;从城市规划到环境监测&#xff0c;海量数据的高效处理、精准分析与直观可视化&#xff0c;已成为各行业突破发展瓶颈、实现转型升级的关键所在。历经十年精心打磨…

分布式机器学习中【拓扑】与【通信】的区别和联系

在分布式机器学习中&#xff0c;拓扑和通信是两个重要的概念&#xff0c;它们虽然有一定的关联&#xff0c;但侧重点不同。下面我会逐一解释它们的定义、区别及其联系&#xff1a; 1. 拓扑&#xff08;Topology&#xff09;&#xff1a; 拓扑指的是系统中不同节点&#xff08…

【fly-iot飞凡物联】(20):2025年总体规划,把物联网整套技术方案和实现并落地,完成项目开发和课程录制。

前言 fly-iot飞凡物联专栏&#xff1a; https://blog.csdn.net/freewebsys/category_12219758.html 1&#xff0c;开源项目地址进行项目开发 https://gitee.com/fly-iot/fly-iot-platform 完成项目开发&#xff0c;接口开发。 把相关内容总结成文档&#xff0c;并录制课程。…

MFC结构体数据文件读写实例

程序功能将结构体内数组数据写入文件和读出 2Dlg.h中代码: typedef struct Student {int nNum[1000];float fScore;CString sss;}stu; class CMy2Dlg : public CDialog { // Construction public:CMy2Dlg(CWnd* pParent NULL); // standard constructorstu stu1; ... } 2Dl…

rust 自定义错误(十二)

错误定义&#xff1a; let file_content parse_file("test.txt");if let Err(e) file_content {println!("Error: {:?}", e);}let file_content parse_file2("test.txt");if let Err(e) file_content {match e {ParseFileError::File > …