【c++】STL--List的实现

news2024/11/14 20:37:20

   

     

目录

一.  List的数据结构

二.  List实现的基本框架

1. list的结点结构类      

 2. List的迭代器类

正向迭代器

反向迭代器

3. List操作接口的实现

 1. 默认成员函数

构造函数 和 析构函数   

拷贝构造函数 和 赋值运算符重载

2. 修改相关函数接口 

insert 和 erase

push_back 和 push_front

pop_front 和 pop_back

 3. 迭代器相关接口

begin() 和 end()

rbegin() 和 rend()

  4. 其他相关函数接口

      swap                  

      clear

      Head_init 

三.  完整源代码实现



                      

一.  List的数据结构

     list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代,其底层是带头双向循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素,list实现上就是一个双向循环链表,list节点 有prev 和 next 两个指针。        

     使用链表存储数据,并不会将它们存储到一整块连续的内存空间中。恰恰相反,各元素占用的存储空间(又称为节点)是独立的、分散的,它们之间的线性关系通过指针来维持的

二.  List实现的基本框架

   List的实现需要实现三个类 (类模板形式)    

         list的结点结构类

         List的迭代器类

         List功能的实现类

1. list的结点结构类      

      对节点的定义如下

    //list结点类模板
	template <class T>
	struct listnode
	{
        listnode* prev;
        listnode* next;
		T data;
        
        //结点的构造函数,完成对节点的初始化
		listnode(const T& val = T())
            :prev(nullptr)
			,next(nullptr)
			,data(val)
			
		{}
	};

     可以看到,list 容器定义的每个节点中,都包含 *prev、*next 和 data。其中,prev 指针用于指向前一个节点;next 指针用于指向后一个节点;data用于存储当前元素的值。

 2. List的迭代器类

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

    1. 原生态指针,比如:vector

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

           1. 指针可以解引用,迭代器的类中必须重载operator*()

           2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()

           3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)至于  operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,所以需要重载,如果是forward_list就不需要重载--

           4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()

        和 vector 容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、--、->、==、!= 等运算符,实现迭代器正确的递增、递减、取值等操作,以达到 vector 迭代器的同等效果

      

正向迭代器

   list 容器正向迭代器的实现代码如下:    

    //list正向迭代器类
	template <class T, class Ref, class Ptr>
	struct _list_iterator
	{
		typedef listnode<T> Node; //在迭代器中实例化结点模板
		typedef _list_iterator<T, Ref, Ptr> self; //对迭代器的类型重命名,已达简化

        //构造
		_list_iterator(Node* node = nullptr)
			:_node(node)
		{}


        // 迭代器支持移动
		// 前置++
		self& operator++()
		{
			_node = _node->next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			++_node;
			return tmp;
		}
		//前置--
		self& operator--()
		{
			_node = _node->prev;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self tmp(*this);
			--_node;
			return tmp;
		}

    
        // 具有指针类似行为
		Ref operator*()
		{
			return _node->data;
		}
      
        //当结点数据类型仍然为自定义结构时
		Ptr operator->()
		{
			return &_node->data;
		}

        
        // 迭代器支持比较
		bool operator!=(const self& s) const
		{
			return _node != s._node;
		}

		bool operator==(const self& s) const
		{
			return _node == s._node;
		}

        Node* _node;
	};

反向迭代器

  list 容器反向迭代器的实现代码如下

    //list反向迭代器类
	template <class Iterator, class Ref, class Ptr>
	struct _list_reverse_iterator
	{
		typedef _list_reverse_iterator<Iterator, Ref, Ptr> self;
 
        //构造
		_list_reverse_iterator(Iterator it)
			:_cur(it)
		{}

        // 迭代器支持移动
		//前置++
		self& operator++()
		{
			--_cur;
			return *this;
		}

		//后置++
		self operator++(int)
		{
			self tmp(*this);
			--_cur;
			return tmp;
		}

		//前置--
		self& operator--()
		{
			++_cur;
			return *this;
		}

		//后置--
		self operator--(int)
		{
			self tmp(*this);
			++_cur;
			return tmp;
		}

        // 具有指针类似行为
		Ref operator*()
		{
			Iterator tmp = _cur;
			return *--tmp;
		}

		Ptr operator->()
		{
			return &(*_cur);
		}


        // 迭代器支持比较
		bool operator!=(const self& s) const
		{
			return _cur != s._cur;
		}

		bool operator==(const self& s) const
		{
			return _cur == s._cur;
		}

		Iterator _cur;
	};

3. List操作接口的实现

 1. 默认成员函数

     构造函数 和 析构函数   

 以下,创建了空的 list 容器(即无参的构造函数),但它也包含有 1 个节点。

    除此之外,list 模板类中还提供有带参的构造函数,它们的实现过程大致分为以下 2 步:

            1. 调用 Head_init() 函数,构造带有头节点的空 list 容器链表;     

             2. 将各个参数按照次序插入到空的 list 容器链表中。

		//构造函数
		list()
		{
			Head_init(); //初始化头结点
		}
      
        //构造n个值为val的节点链表的构造函数
		list(int n, const T& val = T())
		{
			Head_init(); //对链表头结点初始化
			while (n--)  //依次尾插n各值为val的结点
			{
				push_back(val);
			}
		}

		//迭代器区间构造
		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			Head_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}


         
        //析构函数
		~list()
		{
			clear(); //清空链表结点
			delete _head; //释放头结点
			_head = nullptr; //头结点置空
		}

                       

拷贝构造函数 和 赋值运算符重载
        //拷贝构造
		list(const list<T>& lt)
		{
			Head_init(); //初始化

			for (const auto& e : lt)
			{
				push_back(e);
			}
		}



        //赋值运算符重载
		list<T>& operator=(list<T> lt)
		{
			swap(lt); //交换两者数据,交换两者头结点的指针即可
			return *this;
		}

2. 修改相关函数接口 

     insert 和 erase

   insert : 通过在指定位置的元素之前插入新元素(插入位置是任意的)                       

        //在pos位置插入一个结点
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val); //申请一个新节点
			Node* curnode = pos._node;  //pos位置的结点

			newnode->prev = curnode->prev; //先与当前结点的前一个结点链接上
			curnode->prev->next = newnode;

			newnode->next = curnode; //在与当前结点链接
			curnode->prev = newnode;

			return newnode; //返回新插入结点的迭代器
		}

说明:此在list中进行插入时是不会导致list的迭代 器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

注意:节点的连接顺序,先连后断

erase: 删除所指向的位置的结点删除位置是任意的)

        //删除pos位置的结点
		iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* curnode = pos._node; //保存要删除的结点
			Node* nextnode = curnode->next; //删除结点的下一结点

			curnode->prev->next = nextnode;
			nextnode->prev = curnode->prev;

			delete curnode;

			return nextnode; //返回删除结点的后一个结点的迭代器
		}

说明:list 在删除一个结点时会存在迭代器失效,(即被删除的那个位置的迭代器失效),

push_back 和 push_front

尾插

在end()迭代器所在的位置的前一个结点之后插入新的结点

        //尾插
		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;*/

            //复用insert
			insert(end(), val);

		}

在end()位置之前插入一个结点(即在尾部插入结点)

                         

头插

在begin()迭代器所在的位置之前插入新的结点

        //头插
		void push_front(const T& val)
		{
			insert(begin(), val); //复用insert
		}

pop_front 和 pop_back

头删

删除begin()迭代器所在的位置的结点

        //头删
		void pop_front()
		{
			erase(begin()); //复用erase
		}

尾删

删除end()迭代器所在的位置的前一个结点

        //尾删
		void pop_back()
		{
			erase(--end()); 
		}

 3. 迭代器相关接口

     begin() 和 end()
        iterator begin()
		{
			return iterator(_head->next);//构造匿名对象
            //return _head->next; //隐式类型转换
		}


		iterator end()
		{
			return iterator(_head);
            //return _head; 
		}


        //const的迭代器
		const_iterator begin() const
		{
			return const_iterator(_head->next);
            //return _head->next;
		}

		const_iterator end() const
		{
			return const_iterator(_head);
            //return _head;
		}

rbegin() 和 rend()
        reverse_iterator rbegin() 
		{
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}


        //const的反向迭代器
		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(end());
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(begin());
		}

  4. 其他相关函数接口

         swap                  

    用于交换两对象的数据的内容,实际改变头结点的指针的指向           

        void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}
      clear

清空链表结点(头结点除外) 

        void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
      Head_init 

       初始化头结点                    

        //初始化头结点
        void Head_init()
		{
			_head = new Node; //申请一个空白结点(头结点)
			_head->next = _head; 
			_head->prev = _head;
		}

三.  完整源代码实现

    //list结点类
	template <class T>
	struct listnode
	{
		T data;
		listnode* prev;
		listnode* next;

		listnode(const T& val = T())
			:data(val)
			,prev(nullptr)
			,next(nullptr)
		{}
	};

	//list正向迭代器类
	template <class T, class Ref, class Ptr>
	struct _list_iterator
	{
		typedef listnode<T> Node;
		typedef _list_iterator<T, Ref, Ptr> self;

		Node* _node;

		_list_iterator(Node* node)
			:_node(node)
		{}
		//前置++
		self& operator++()
		{
			_node = _node->next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			++_node;
			return tmp;
		}
		//前置--
		self& operator--()
		{
			_node = _node->prev;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self tmp(*this);
			--_node;
			return tmp;
		}

		Ref operator*()
		{
			return _node->data;
		}

		Ptr operator->()
		{
			return &_node->data;
		}

		bool operator!=(const self& s) 
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

	//list反向迭代器类
	template <class Iterator, class Ref, class Ptr>
	struct _list_reverse_iterator
	{
		typedef _list_reverse_iterator<Iterator, Ref, Ptr> self;

		_list_reverse_iterator(Iterator it)
			:_cur(it)
		{}
		//前置++
		self& operator++()
		{
			--_cur;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			--_cur;
			return tmp;
		}
		//前置--
		self& operator--()
		{
			++_cur;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self tmp(*this);
			++_cur;
			return tmp;
		}

		Ref operator*()
		{
			Iterator tmp = _cur;
			return *--tmp;
		}

		Ptr operator->()
		{
			return &(*_cur);
		}

		bool operator!=(const self& s)
		{
			return _cur != s._cur;
		}

		bool operator==(const self& s)
		{
			return _cur == s._cur;
		}

		Iterator _cur;
	};

	//list类
	template <class T>
	class list
	{
		typedef listnode<T> Node;
	public:
		typedef _list_iterator<T, T&, T*>  iterator;
		typedef _list_iterator<T, const T&, const T*>  const_iterator;

		typedef _list_reverse_iterator<iterator, T&, T*>  reverse_iterator;
		typedef _list_reverse_iterator<const_iterator, const T&, const T*>  const_reverse_iterator;

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

		void Head_init()
		{
			_head = new Node;
			_head->next = _head;
			_head->prev = _head;
		}

		//构造函数
		list()
		{
			Head_init();
		}

		list(int n, const T& val = T())
		{
			Head_init();
			while (n--)
			{
				push_back(val);
			}
		}
		//迭代器区间构造
		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			Head_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//拷贝构造
		list(const list<T>& lt)
		{
			Head_init();

			for (const auto& e : lt)
			{
				push_back(e);
			}
		}

		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}

		//赋值运算符重载
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

		//析构
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		//在pos位置插入一个结点
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);
			Node* curnode = pos._node;

			newnode->prev = curnode->prev;
			curnode->prev->next = newnode;
			newnode->next = curnode;
			curnode->prev = newnode;

			return newnode;
		}

		//删除pos位置的结点
		iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* curnode = pos._node;
			Node* nextnode = curnode->next;

			curnode->prev->next = nextnode;
			nextnode->prev = curnode->prev;

			delete curnode;

			return nextnode;
		}

		//尾插
		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;*/

			insert(end(), val);
		}
		//头插
		void push_front(const T& val)
		{
			insert(begin(), val);
		}

		//尾删
		void pop_back()
		{
			erase(--end());
		}
		//头删
		void pop_front()
		{
			erase(begin());
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

	private:
		Node* _head;
	};

                   


                      

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

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

相关文章

人大金仓KingbaseES:windows安装

人大金仓KingbaseES&#xff1a;windows安装 产品简介 金仓数据库管理系统[简称:KingbaseES]是北京人大金仓信息技术股份有限公司&#xff08;简称人大金仓&#xff09;自主研发的、具有自主知识产权的商用关系型数据库管理系统&#xff08;DBMS&#xff09;。该产品面向事务…

春日特惠,爱基百客限时放送,开启您的学术新篇章!

春回大地&#xff0c;万物复苏&#xff0c; 正是探索未知、启发新思的最佳时节。 在这个充满生机的季节里&#xff0c; 我们推出了春季大促活动&#xff0c; 旨在助力每一位科研工作者在新的一年里实现更多突破。 让我们一起迎接科研人的春天&#xff0c; 开启智慧的花朵…

初阶数据结构:二叉树(补充扩展)

目录 1. 堆排序1.1补充&#xff1a;建堆的时间复杂度1.2 堆排序&#xff1a;升序与降序 2. TopK问题3. 二叉树的链式结构及其遍历方式3.1 二叉树的链式结构3.2 二叉树的前序遍历2.2 二叉树的中序遍历2.3 后序遍历2.4 层序遍历 4. 二叉树OJ练习4.1 单值二叉树4.2 判断两棵二叉树…

“零碳未来”:引领全球向低碳经济转型

全球环境基金(GEF),这个由183个国家和地区组成的国际合作组织,是世界银行1990年创建的实验项目,一直致力于支持环境友好型项目,推动全球环境改善。而“零碳未来”不仅是一个由全球环境基金(GEF)创建的跨越国界的全新交易平台,更是一个致力于推动全球向低碳经济转型的零碳排放生…

代码随想录day12(2)字符串:重复的子字符串(leetcode459)

题目要求&#xff1a;给定一个非空的字符串&#xff0c;判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母&#xff0c;并且长度不超过10000。 思路&#xff1a; 一、首先对于暴力解法&#xff0c;可以枚举所有的字串进行判断。但是枚举时实际上只需…

【Pytorch 第四讲】图像分类的Tricks

1. 标签平滑 在分类问题中&#xff0c;最后一层一般是全连接层&#xff0c;然后对应标签的one-hot编码&#xff0c;即把对应类别的值编码为1&#xff0c;其他为0。这种编码方式和通过降低交叉熵损失来调整参数的方式结合起来&#xff0c;会有一些问题。这种方式会鼓励模型对不同…

【机器学习】生成对抗网络GAN

概述 生成对抗网络&#xff08;Generative Adversarial Network&#xff0c;GAN&#xff09;是一种深度学习模型架构&#xff0c;由生成器&#xff08;Generator&#xff09;和判别器&#xff08;Discriminator&#xff09;两部分组成&#xff0c;旨在通过对抗训练的方式生成逼…

在全志V853平台上成功部署深度学习步态识别算法

北理工通信课题组辛喆同学在本科毕业设计《基于嵌入式系统的步态识别的研究》中&#xff0c;成功将深度步态识别算法GaitSet移植到全志V853开发板上。本研究在CASIA-B数据集上进行测试&#xff0c;正常行走状态下该系统的步态识别准确率达到了94.9%&#xff0c;背包行走和穿外套…

Java I/O流

一、常用文件操作 1.创建文件对象 2.获取文件相关信息 3.目录的操作和文件删除 二、IO 流原理及流的分类 1.IO流原理 2.流的分类 2.1 字节流和字符流 2.2 节点流和处理流 3.IO流体系图 三、常用的类介绍 1.FileInputStream 和 FileOutputStream&#xff08;字节流、节点流&…

2核4g服务器能支持多少人访问?并发数性能测评

2核4g服务器能支持多少人访问&#xff1f;支持80人同时访问&#xff0c;阿腾云使用阿里云2核4G5M带宽服务器&#xff0c;可以支撑80个左右并发用户。阿腾云以Web网站应用为例&#xff0c;如果视频图片媒体文件存储到对象存储OSS上&#xff0c;网站接入CDN&#xff0c;还可以支持…

力扣经典题目解析--反转链表

原题地址: . - 力扣&#xff08;LeetCode&#xff09; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 题目解析 链表&#xff08;Linked List&…

交叉编译qt5.14.2

qt源码下载地址&#xff1a;qt-everywhere-src-5.14.2.tar.xz 1.修改qt-everywhere-src-5.14.2/qtbase/mkspecs/linux-arm-gnueabi-g/qmake.conf文件&#xff1a; # # qmake configuration for building with arm-linux-gnueabi-g #MAKEFILE_GENERATOR UNIX CONFIG …

贪心算法练习题(最小化战斗力差距、谈判、纪念品分组、分糖果)

目录 一、贪心算法的介绍 二、贪心算法的实现步骤 三、最小化战斗力差距 四、谈判 五、纪念品分组 六、分糖果 一、贪心算法的介绍 贪心的基本原理:每一步都选择局部最优解&#xff0c;而尽量不考虑对后续的影响&#xff0c;最终达到全局最优解。 贪心的局限性:贪心算法…

深度学习-Pytorch实现经典AlexNet网络:山高我为峰

深度学习-Pytorch实现经典AlexNet网络之山高我为峰 深度学习中&#xff0c;经典网络引领一波又一波的技术革命&#xff0c;从LetNet到当前最火的GPT所用的Transformer&#xff0c;它们把AI技术不断推向高潮。2012年AlexNet大放异彩&#xff0c;它把深度学习技术引领第一个高峰…

智慧楼宇的心脏:E6000物联网主机

智慧楼宇是指通过全面覆盖的感知设备和互联网技术&#xff0c;为建筑提供高效、舒适、安全、环保、可持续的智能化服务。 在科技快速发展的今天&#xff0c;智慧楼宇已经不再是遥不可及的梦想。而在这个梦想成真的过程中&#xff0c;物联网主机扮演着至关重要的角色。它如同智慧…

JS逆向进阶篇【去哪儿旅行登录】【下篇-逆向Bella参数JS加密逻辑Python生成】

目录&#xff1a; 每篇前言&#xff1a;引子——本篇目的1、 代码混淆和还原&#xff08;1&#xff09;单独替换&#xff1a;&#xff08;2&#xff09;整个js文件替换&#xff1a; 2、算法入口分析3、 深入分析&#xff08;0&#xff09;整体分析&#xff1a;&#xff08;1&am…

软件应用,财务收支系统试用版操作教程,佳易王记录账单的软件系统

软件应用&#xff0c;财务收支系统试用版操作教程&#xff0c;佳易王记录账单的软件系统 一、前言 以下软件操作教程以 佳易王账单记账统计管理系统V17.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 如上图&#xff0c;统计报表包含 收支汇…

StarRocks实战——松果出行实时数仓实践

目录 一、背景 二、松果出行实时OLAP的演进 2.1 实时数仓1.0的架构 2.2 实时数仓2.0的架构 2.3 实时数仓3.0的架构 三、StarRocks 的引入 四、StarRocks在松果出行的应用 4.1 在订单业务中的应用 4.2 在车辆方向的应用 4.3 StarRocks “极速统一” 落地 4.4 StarRoc…

Day25-进程管理核心知识1

Day25-进程管理核心知识1 1. CentOS7 软件包安装方式1.1 什么是源码包&#xff1f;1.2 为什么要源码包安装1.3 源码包如何获取1.4 编译安装源码包软件 2. 源码编译示例 下面通过编译Nginx来深入了解下源码包编译的过程。3. Linux 系统进程介绍3.1 什么是进程?3.2 进程/守护进程…

【深度学习】脑部MRI图像分割

案例4&#xff1a;脑部MRI图像分割 相关知识点&#xff1a;语义分割、医学图像处理&#xff08;skimage, medpy&#xff09;、可视化&#xff08;matplotlib&#xff09; 1 任务目标 1.1 任务简介 本次案例将使用深度学习技术来完成脑部MRI(磁共振)图像分割任务&#xff0c…