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

   

     

目录

一.  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;
	};

                   


                      

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch统计属性

目录 1.normal2. mean, sum, min, max, prod3.argmin, argmax4. topk kthvalue5. compare6.norm 1.normal torch.normal(mean, std, *, generatorNone, outNone) → Tensor返回一个张量&#xff0c;其中的每个元素随机来自独立的标准正态分布。这些分布具有给定的均值和标准差…

12-Linux部署Zookeeper集群

Linux部署Zookeeper集群 简介 ZooKeeper是一个分布式的&#xff0c;开放源码的分布式应用程序协调服务&#xff0c;是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件&#xff0c;提供的功能包括&#xff1a;配置维护、域名服务、分布式同步、组服务等。…

入门LLMs开发 — LangChain

像OpenAI的GPT-4这样的大型语言模型&#xff08;LLMs&#xff09;已经风靡全球。它们可以自动执行各种任务&#xff0c;如回答问题、翻译语言、分析文本等。LLMs是第一种真正感觉像“人工智能”的机器学习类型。 然而&#xff0c;在将LLMs应用于实际产品时仍然存在挑战。特别是…

特氟龙塑料试剂瓶对比普通塑料和玻璃试剂瓶的优势

试剂瓶作为实验室中常备器皿耗材之一&#xff0c;主要用来盛放和储存样品、试剂&#xff0c;根据使用条件不同&#xff0c;也可叫取样瓶、样品瓶、储样瓶、广口瓶等。 根据瓶口口径不同&#xff0c;可分为广口瓶和窄口瓶&#xff0c;广口瓶口径较大&#xff0c;主要用于储存固…

Vue.js 实用技巧:深入理解 Vue.mixin

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

JProfiler详解 JVM性能监测内存泄露分析工具

JProfiler详解 JProfiler简介主要功能特点使用场景注意事项使用案例使用步骤Could not verify ssh-ed25519 host key with fingerprint 问题解决内存泄露分析 JProfiler简介 JProfiler是一款业界领先的Java性能分析工具&#xff0c;由ej-technologies公司开发&#xff0c;专门…

【亲测】注册Claude3教程,解决Claude3被封号无法发送手机验证码

【亲测】注册Claude3教程&#xff1a;解决无法发送手机验证码的问题 Anthropic 今日宣布推出其最新大型语言模型&#xff08;LLM&#xff09;系列——Claude 3&#xff0c;这一系列模型在各种认知任务上树立了新的性能标准。Claude 3 系列包括三个子模型&#xff1a;Claude 3 …

微服务架构SpringCloud(2)

热点参数限流 注&#xff1a;热点参数限流默认是对Springmvc资源无效&#xff1b; 隔离和降级 1.开启feign.sentinel.enabletrue 2.FeignClient(fallbackFactory) 3.创建一个类并实现FallbackFactory接口 4.加入依赖 <!--添加Sentienl依赖--><dependency><gro…

Linux开发工具使用

一、Linux软件包管理器 yum 软件包和软件包管理器, 就好比 "App" 和 "应用商店" &#xff0c;我们现在要安装的yum就是相当于在我们的Linux终端安装一个"应用商店"。 但使用yum时&#xff0c;我们一定要保证主机(虚拟机)网络畅通!这点也非常好理…

【单调栈】Leetcode 739.每日温度

【单调栈】Leetcode 739.每日温度 解法&#xff1a;维护单调栈栈中存的是数组的索引 解法&#xff1a;维护单调栈栈中存的是数组的索引 栈中存的是数组的索引 当新的值比当前栈顶的大&#xff0c;那么就执行出栈-更新result数组-判断当新的值比当前栈顶的大&#xff1f;的循环…

C语言回顾学习

一、数据类型 1.常量 2.float浮点表示 3.字符型 4.char&#xff08;大小写&#xff09; #include <stdio.h> //根据数字输出字符--int值可以直接输出为char int main() {int value;while (1){scanf("%d",&value);if(value<65||value>122){printf(&…

Python的http模块requests

模块简介&#xff1a; requests 库是一个 python中比较有名的 http请求的库&#xff0c;能处理 get,post,put,delete 等 restful请求&#xff0c;能设置 header&#xff0c;cookie,session 等操作&#xff0c;也是作为爬虫的基础库&#xff0c;它目前还不能异步请求,如果要支持…

C语言 BMP图片的旋转与缩放

目录 一、bmp文件头、文件信息头、位图实际数据的数据结构定义 二、源BMP文件信息的读取 三、实际位图数据的旋转、缩放操作 四、生成转换过后的新位图文件 #include <stdlib.h> #ifndef PHOTODEAL_H #define PHOTODEAL_H #pragma pack(1) typedef struct tagBm…

力扣经典题目解析--删除链表的倒数第 N 个结点

原题地址:. - 力扣&#xff08;LeetCode&#xff09; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], n 2 输出&#xff1a;[1,2,3,5]示例 2&#xff1a; 输入&#xff1a;h…

【韩顺平零基础学java】第12章课后题

练习题1 如果用户输入的不是一个整数&#xff0c;就提示他反复输入&#xff0c;直到输入的是一个整数为止 import java.util.Scanner;/*如果用户输入的不是一个整数&#xff0c;就反复输入&#xff0c;直到输入的是一个整数为止*/ public class TryCatchExercise04 {public s…

代码随想录算法训练营Day37 | LeetCode738.单调递增的数字、LeetCode968.监控二叉树、贪心算法总结

LeetCode738.单调递增的数字 思路&#xff1a;与分糖果的题目同理&#xff0c;因为需要与前一位数比较&#xff0c;并且修改这两个数&#xff0c;因此需要从后往前遍历&#xff0c;当前一位数比当前数大时&#xff0c;则前一个数-1&#xff0c;后一个数变为9。 代码细节&…

金三银四,程序员如何备战面试季

金三银四&#xff0c;程序员如何备战面试季 一个人简介二前言三面试技巧分享3.1 自我介绍 四技术问题回答4.1 团队协作经验展示 五职业规划建议5.1 短期目标5.2 中长期目标 六后记 一个人简介 &#x1f3d8;️&#x1f3d8;️个人主页&#xff1a;以山河作礼。 &#x1f396;️…

【数据存储】大端存储||小端存储(超详细解析,小白一看就懂!!!)

目录 一、前言 二、什么是低地址、高地址 &#xff1f; 三、什么是数据的高位和低位 &#xff1f; 四、什么是大小端存储&#xff1f; &#x1f349; 小端存储详解 &#x1f352; 大端存储详解 五、为什么会有大小端存储&#xff1f; &#x1f34d;大端存储的优点 &#…

跨境电商趋势解析:社交电商携手私域流量运营,精准触达与转化

随着全球化的深入发展&#xff0c;跨境电商逐渐成为全球贸易的重要组成部分。在这一背景下&#xff0c;社交电商作为一种新兴的商业模式&#xff0c;正逐渐在跨境电商领域崭露头角&#xff0c;并对私域流量的运营产生了深远的影响。本文Nox聚星将和大家分析社交电商在跨境电商中…

数据结构(一)综述

一、常见的数据结构 数据结构优点缺点数组查找快增删慢链表增删快查找慢哈希表增删、查找都快数据散列&#xff0c;对存储空间有浪费栈顶部元素插入和取出快除顶部元素外&#xff0c;存取其他元素都很慢队列顶部元素取出和尾部元素插入快存取其他元素都很慢二叉树增删、查找都快…