初识《list》及手搓模拟《list》

news2024/11/15 12:40:21

目录

前言:

1. list的介绍及使用

list的介绍:

list的使用:

1、list的构造​编辑

2、list iterator的使用

3、list capacity

4、list element access

5、list modifiers

2.list的模拟实现

1、关于迭代器:

2、迭代器类的封装:

 3、模板为类的时候:

4、关于const迭代器:

一:而额外封装一个const迭代器。const_iterator

二:利用模板

3.vector与list的区别:

总结:


前言:

现阶段我们已经逐渐熟悉了各个STL库中的容器,对于他们的各个接口都大差不差,在我们学习完vector之后我们就可以陆陆续续接触一些算法题。我们的《好题分析》这一专栏也会不断的进行更新!下面我们先来熟悉以下list这个容器。

1. list的介绍及使用

list的介绍:

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向带头链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)


list的使用:

1、list的构造

2、list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

 

注意!!

1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动

3、list capacity

4、list element access

5、list modifiers

 6、list的迭代器失效

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
 

void TestListIterator1()
{
	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);
	}
}

2.list的模拟实现

1、关于迭代器:

我们对list的迭代器的理解与我们之前对于string 和 vector中的iterator的理解有十分大的区别。string和vector的迭代器我们可以替换理解为指针,在我们遍历vector或者string时,仅需要对++运算符进行重载,我们就可以拿到下一个位置的值,而operator++()也很好理解,就是指针+1指向下一个下标的位置。

但是这次我们的list就大有不同,我们都知道list是一个带头的双向链表,而想要获得下一个结点的数据,应当是node = node -> next; 如果我们将运算符++重载为这个代码,那对于其它的代码想要运用++操作符,就纯粹扯淡。

这个时候的唯一解决方法就是————————封装一个类!!!

2、迭代器类的封装:

// 一个结点的结构!!!
template<class T>
struct ListNode
{
	T _data;
	ListNode<T>* _next;
	ListNode<T>* _prev;

	ListNode(const T& x = T())
		:_next(nullptr)
		, _prev(nullptr)
		, _data(x)
	{}
};

// 将迭代器封装成一个类
template<class T>
struct ListIterator
{
	typedef ListIterator<T> Self;
	typedef ListNode<T> Node;

	Node* _node;// iterator

	ListConstIterator(Node* node)
		:_node(node)
	{}

	bool operator != (const Self& it)
	{
		return _node != it._node;
	}

	Self& operator++()
	{
		_node = _node->_next;
		return *this;
	}

	Self operator++(int)
	{
		Self tmp(*this);
		_node = _node->_next;
		return *this;
	}

	Self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}

	Self operator--()
	{
		Self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}

	T& operator*()
	{
		return _node->_data;
	}

	T* operator->()
	{
		return &_node->_data;
	}
};

我们在之后的项目中,如果发现我们目前处理的数据中的内置类型不满足我们的需求,不妨我们可以将其封装成一个类!!!

首先我们先通过画图得方式来理解代码:


 因为这个iterator类我们并不会定义私有成员,所以我们这里用的struct来定义。

而我们在整个链表的总体类中,我们需要先找到两个 头 和 尾 结点的位置,即begin 和 end.

	iterator begin()
	{
		return iterator(_head->_next); // 匿名对象
        // iterator it(_head->_next); // 调用 iterator类 里面的 构造函数
        // return it;
	}


	iterator end()
	{
    	return iterator(_head); // 匿名对象
        // iterator it(_head); // 调用 iterator类 里面的 构造函数
        // return it;
	}

 

 3、模板为类的时候:

想要去到_a1, 若是不进行函数重载, 则代码为:

list<A>::iterator it = ls.begin();
std::cout << it._node->_data._a1 << " " << it._node->_data._a2 << std::endl;
it++;
std::cout << it._node->_data._a1 << " " << it._node->_data._a2 << std::endl;

很明显代码非常的冗长和麻烦, 因此我们可以利用函数重载:

T* operator->()
{
    return &_node->_data;
}
list<A>::iterator it = ls.begin();
std::cout << it->_a1 << " " << it->_a2 << std::endl;
it++;
std::cout << it->_a1 << " " << it->_a2 << std::endl;

it.operator->()->_a1 
在这里编译器会自动优化代码,将代码的可读性提高。
it->_a1    <==>   it.operator->()->_a1     <==>      it->->_a1
通过公式推导,我们不难发现 it->_a1  <==>    it->->_a1    这两个式子是等价的

4、关于const迭代器:

const迭代器,需要的是迭代器指向的内容不能被修改而const iteratror 作返回值时,代表了迭代器的指向不可被修改。

一:而额外封装一个const迭代器。const_iterator

在我们实施这个方法后,我们会发现仅仅只有Self& operator*() 和 Self* operator->()的返回值是需要加const,其它的都不变

 //对于每一个容器来说,都有存在const的类型接口,因此我们也需要创建一个const迭代器。
 //(运用const主要还是 防止 在进行 拷贝构造 或者 ++ -- 等出现 修改一个 const链表的情况。)
template<class T>
struct List_const_iterator
{
    typedef ListNode<T> Node;
    typedef List_const_iterator<T> Self;

    Node* _node;// 最重要的一行代码,代表在 List_iterator 类中 封装一个 _node 用来指向结点,通过在类里面
    // 控制运算符重载来操纵迭代器

    List_const_iterator(Node* node) // 传值构造
        :_node(node)
    {}

    // ++it
    Self& operator++()
    {
        _node = _node->_next;
        return *this;
    }

    // it++
    Self operator++(int)
    {
        Self tmp(*this); // 无需写拷贝构造函数, 默认的 浅拷贝 即可完成操作
        _node = _node->_next;
        return *this;
    }

    // *it
    const T& operator*()
    {
        return _node->_data;
    }

    // it->_data,此时的 _data 是结构体时才调用
    const T* operator->()
    {
        return &_node->_data;
    }

    bool operator!=(const Self& it)
    {
        return _node != it._node;
    }

};

 

因此我们没必要多此一举。

二:利用模板

// 我们在创建 const_iterator 迭代器时发现在整个类中,仅仅只是对 operator*() 与 operator->() 的返回值进行了修改
// 为了尽可能的减少代码量,利用模板是一个不错的选择。
// Ref == reference    ,    Prt == pointer
//                      T&         T* 
template<class T, class Ref, class Ptr> 
//template<class T>
struct List_iterator
{
    typedef ListNode<T> Node;
    typedef List_iterator<T, Ref, Ptr> Self;

    Node* _node;// 最重要的一行代码,代表在 List_iterator 类中 封装一个 _node 用来指向结点,通过在类里面
                // 控制运算符重载来操纵迭代器
    
    List_iterator(Node* node) // 传值构造
        :_node(node)
    {}

    // ++it
    Self& operator++()
    {
        _node = _node->_next;
        return *this;
    }

    // it++
    Self operator++(int)
    {
        Self tmp(*this); // 无需写拷贝构造函数, 默认的 浅拷贝 即可完成操作
        _node = _node->_next;
        return *this;
    }
    
    // *it 返回类型为T&
    Ref operator*()
    {
        return _node->_data;
    }


    // it->_data,此时的 _data 是结构体时才调用, 返回类型为T*
    Ptr operator->()
    {
        return &_node->_data;
    }

    bool operator!=(const Self& it)
    {
        return _node != it._node;
    }

};

 在list类中:

 template<class T>
 class list
 {
 public:
     typedef ListNode<T> Node;
     typedef List_iterator<T, T&, T*> iterator;
     typedef List_iterator<T, const T&, const T*> const_iterator;
     //typedef List_const_iterator<T> const_iterator;
     // 构造函数创建 哨兵位 的头结点

 利用模板是最高效的方法!!!

3.vector与list的区别:

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

总结:

本文的代码思路与之前大为不同,本次首先接触到了利用类封装一个迭代器,其次是对结点里的类进行分类讨论,从而引出对->运算符的重载,再然后又对const的迭代器进行了扩展,发现利用模板可以有效的解决出现的一系列问题。

代码在我的Gitee:
​​​​​​​my_list_practices_2/my_list_practices_2/list.h · 无双/C_Plus_Plus_Acer - Gitee.com

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

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

相关文章

如何搭建一个vue2组件库(king-ui-pro)

文章引用图片无法查看&#xff0c;直接查看原文 感兴趣的可以关注或订阅下这个系列&#xff0c;后续会陆续将相关的组件与公共方法进行分享 目前已经完成了的组件有 多行省略pro版&#xff0c;不是简单的多行省略效果 公共方法&#xff1a; 1、图片预览&#xff0c;知乎的图…

MOS产品在电池化成分容设备上的应用与型号分析

据市场研究机构预测&#xff0c;全球电池化成分容产线市场规模在未来几年将继续保持增长态势。其中&#xff0c;亚洲市场增长速度最快&#xff0c;尤其是中国市场。中国政府对于新能源汽车和储能领域的支持力度不断加大&#xff0c;推动了电池化成分容产线的市场需求不断增长。…

ZISUOJ 高级语言程序设计实训-基础B(部分题)

说明&#xff1a; 有几个题是不会讲的&#xff0c;我只能保证大家拿保底分。 题目列表&#xff1a; 问题 A: 统计字母个数 思路&#xff1a; 把a到z放map里处理后输出即可。 参考题解&#xff1a; #include <iostream> #include <string> #include <map> …

台灯的功能作用有哪些?分享护眼灯排行榜前十名

说到台灯相信大家都不陌生&#xff0c;基本家家户户都会备上一台&#xff0c;不过也有家长存在疑惑&#xff0c;台灯的功能作用有哪些呢&#xff1f;其实台灯最主要的作用就是补充桌面不足的照明&#xff0c;一般单靠室内灯提供亮度是远远不够的&#xff0c;容易造成桌面亮度不…

“卫星-无人机-地面”遥感数据快速使用及地物含量计算的实现方法

原文链接&#xff1a;“卫星-无人机-地面”遥感数据快速使用及地物含量计算的实现方法https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601940&idx6&sn515e01666037570939aaf0eee56f46d1&chksmfa820ef3cdf587e5276eac181c890026b6ca4bc36ce0e4f80d89d…

Linux开机启动流程

Linux开机启动流程详细步骤如下图&#xff1a; 其中&#xff1a; POST:Power On Self Test --加电自检 BIOS: Basic Input Output System --基础输入输出系统 MBR: Master Boot Record --主引导记录 GRUB: GRand Uni…

【electron3】electron将数据写入本地数据库

安装 yarn add sqlite3 --save连接并调用数据库&#xff0c;创建表 createDB.ts文件内容 const sqlite3 require(sqlite3) const NODE_ENV process.env.NODE_ENV const path require(path) const { app } require(electron) let DB_PATH path.join(app.getAppPath(), /…

CUDA编程【2】-(51-78)

系列文章目录 文章目录 系列文章目录前言51、寄存器溢出51.1 溢出概念51.1 使用控制 52、本地内存和共享内存52.1 本地内存52.2. 共享内存 53. 常量内存53.1 概念53.2 初始化 54. 全局内存54.1 概念54.2 初始化 55. GPU缓存和变量作用域55.1 缓存类型55.2 变量作用域 56. 静态全…

vue基础语法学习

Object.defineProperty方法的使用 // 这是一个普通的对象 let phone {} // 给这个phone新增一个属性 三个参数&#xff1a;新增属性的对象&#xff0c;新增啥属性&#xff0c;属性值&#xff0c;key value对 Object.defineProperty(phone,color,{value:太空灰, //设置属性值wr…

互联网大厂ssp面经,数据结构part2

1. 什么是堆和优先队列&#xff1f;它们的特点和应用场景是什么&#xff1f; a. 堆是一种特殊的树形数据结构&#xff0c;具有以下特点&#xff1a;i. 堆是一个完全二叉树&#xff0c;即除了最后一层外&#xff0c;其他层都是满的&#xff0c;并且最后一层的节点都靠左对齐。i…

【css】select实现placeholder效果

场景&#xff1a;使用select下拉选择框的时候&#xff0c;需要像其他控件一样提示默认信息。 问题&#xff1a;表单控件select没有placeholder属性。 解决方案&#xff1a;通过css实现&#xff0c;不需要js <style>select > option[disabled]{ color:#999;cursor: n…

【数据结构(邓俊辉)学习笔记】向量01——接口与实现

文章目录 0.意图1、概述2 从数组到向量3 向量ADT接口4 Vector 模板类5 构造与析构5.1默认构造方法5.2基于复制的构造方法5.3 析构方法 0.意图 一方面是将工作学习中零星的知识点串起来&#xff0c;另一方面向量是其他数据类型的基础&#xff0c;比如栈队列等&#xff0c;所以基…

【C语言】每日一题,快速提升(10)!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 题目&#xff1a;圣诞树 输入&#xff1a; 1输出&#xff1a; * * * * * **说明&#xff1a; 输入&#xff1a; 2输出&#xff1a; * * * * * * * …

近年数一,数二难度如何,听说24是像张宇那样的题?

直接上分数&#xff01; “估分一百零几&#xff0c;平时李林130-140&#xff0c;张八110-125的样子&#xff0c;超越做的分数也是100出头。” 24学长说&#xff1a; “远离李林张八&#xff01;张四没做不评价。” “李林张八暑假前做完当作打基础即可。超越才是真题难度”…

WordPress social-warfare插件XSS和RCE漏洞【CVE-2019-9978】

WordPress social-warfare插件XSS和RCE漏洞 ~~ 漏洞编号 : CVE-2019-9978 影响版本 : WordPress social-warfare < 3.5.3 漏洞描述 : WordPress是一套使用PHP语言开发的博客平台&#xff0c;该平台支持在PHP和MySQL的服务器上架设个人博客网站。social-warfare plugin是使用…

获取肖博数学全套视频+讲义

肖博数学是一个专业团队&#xff0c;教学方法非常颠覆&#xff0c;具有很多技巧&特殊的解题方法内容&#xff0c;能使得学生在高考时冲刺高分 hello&#xff0c;今天分享一下高中数学资料&#xff0c;肖博数学&#xff0c; 他们的教学方法与传统的教学方式有所不同&#…

使用HTML+css+js+jQuery完成,输入用户信息,转换为数据表格

案例图 案例源码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>User Information Form</ti…

基于vue+node+mysql的视频校对系统

一、登录注册&#xff1a;包括登录&#xff0c;注册&#xff0c;忘记密码&#xff0c;验证码等常用点。 二、用户管理&#xff1a;包括用户的增删改查 三、权限管理&#xff08;请增加这个权限&#xff1a;任务分配——只有管理者才能发布和删除任务&#xff1b;管理员设置。 四…

HTML中的文档声明

前言 什么是<!DOCTYPE>&#xff1f;是否需要在 HTML5 中使用&#xff1f;什么是严格模式与混杂模式&#xff1f; 文档声明概念 HTML 文档通常以文档声明开始&#xff0c;该声明的作用是帮助浏览器确定其尝试解析和显示的 HTML 文档类型。 <!DOCTYPE html>文档声…

轻松搭建llama3Web 交互界面 - Ollama + Open WebUI

Ubuntu下安装&#xff1a;&#xff08;官网&#xff1a;Download Ollama on Linux&#xff09; curl -fsSL https://ollama.com/install.sh | sh 就运行起来ollama了&#xff0c;不放心可以用ollama serve查看一下 ollama run llama3 就可以跑起来了&#xff0c; 那么我们肯…