当前位置: 首页 > article >正文

【C++】——list

文章目录

  • list介绍和使用
    • list注意事项
  • list模拟实现
  • list和vector的不同

list介绍和使用

在C++中,list是一个带头双向链表

在这里插入图片描述

list注意事项

  1. 迭代器失效
    删除元素:当使用迭代器删除一个元素时,指向该元素的迭代器会失效,但是不会影响其他迭代器。
  2. 内部实现:与vector相比,list不支持随机访问,就是说不能通过下标访问元素,但是可以使用迭代器 ,在删除和插入时非常方便
  3. 元素唯一性:list中的元素是不重复的,如果尝试插入已经存在的元素,该元素将被覆盖。
  4. 容量:list没有容量概念,根据动态分配内存
  5. 内存效率:他的插入和删除操作时间复杂度都为O(1)

list模拟实现

  1. 指针可以解引用,迭代器的类中必须重载operator*()
  2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
  3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
    至于operator–()/operator–(int)释放需要重载,
    根据具体的结构来抉择,双向链表可以向前 移动,所以需要重载,如果是forward_list就不需要重载–
  4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
#pragma once
#include <assert.h>
namespace rq
{
	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;

		list_node(const T& x = T())
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};
	template<class T>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator<T> Self;
		Node* _node;

		list_iterator(Node* node)
			:_node(node)
		{}
		T& operator*()
		{
			return _node->_data;
		}
		T* operator&()
		{
			return _node->_data;
		}
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		bool operator!=(const Self& x)
		{
			return _node != x._node;
		}
		bool operator==(const Self& x)
		{
			return _node == x._node;
		}
	};
		
	template<class T>
	struct list_const_iterator
	{
		typedef list_node<T> Node;
		typedef list_const_iterator<T> Self;
		Node* _node;

		list_const_iterator(Node* node)
			:_node(node)
		{}
		
		const T& operator*()
		{
			return _node->_data;
		}
		const T* operator&()
		{
			return _node->_data;
		}
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		bool operator!=(const Self& x)
		{
			return _node != x._node;
		}
		bool operator==(const Self& x)
		{
			return _node == x._node;
		}
	};

	template<class T>
	class list
	{
		typedef list_node<T> Node;

	public:
		typedef list_iterator<T> iterator;
		typedef list_const_iterator<T> const_iterator;


		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);//erase以后会返回下一个位置
			}
		}
		void emtpy_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
		list()
		{
			emtpy_init();
		}
		//拷贝构造
		list(const list<T>& lt)
		{
			emtpy_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

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

		iterator begin()
		{
			/*iterator it(_head->_next);
			return it;*/

		/*	return iterator(_head->_next); 匿名对象*/
			return _head->_next;//接收指针,但是返回会隐式转换成iterator类型
		}
		iterator end()
		{
			return _head;
		}
		const_iterator begin() const 
		{
			/*iterator it(_head->_next);
			return it;*/

			/*	return iterator(_head->_next); 匿名对象*/
			return _head->_next;//接收指针,但是返回会隐式转换成iterator类型
		}
		const_iterator end() const 
		{
			return _head;
		}
		
		size_t size() const
		{
			return _size;
		}
		void swap(list<int>& it)
		{
			std::swap(_head, it._head); 
			std::swap(_size, it._size);
		}
		bool empty() const
		{
			return _size == 0;
		}
		void push_back(const T& x)
		{
			/*Node* newnode = new Node(x);
			Node* tail = _head->_prev;

			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
			++_size;*/
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_front()
		{
			erase(begin());
		}
		void pop_back()
		{
			erase(--end());
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());//不能把头节点删了
			Node* prev = pos._node->_prev;
			Node* next = pos._node->_next;

			prev->_next = next;
			next->_prev = prev;
			--_size;

			return next;
		}
		iterator insert(iterator pos, const T& x)
		{
			//prev newnode cur
			Node* newnode = new Node(x);
			Node* prev = pos._node->_prev;
			Node* cur = pos._node;

			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			++_size;

			return newnode;
		}
	private:
		Node* _head;
		size_t _size;
	};
	template<class Container>
	void print_container(const Container& v)
	{
		list<int>::const_iterator it = v.begin();
		while (it != v.end())
		{
			//*it += 10;
			cout << *it << " ";
			++it;
		}
		cout << endl;
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}
	void test_list01()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		
		print_container(lt);
		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}


test.cpp

#define   _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<string>
#include <algorithm>
#include <list>
using namespace std;
void test_list1()
{
	string s("sgfaiug");
	cout << s << endl;
	sort(s.begin(), s.end());
	cout << s << endl;
}
void test_list2()
{
	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
	auto it = lt.begin();
	int k = 3;
	while (k--)
	{
		++it;
	}
	lt.insert(it, 30);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	int x = 0;
	cin >> x;
	it = find(lt.begin(), lt.end(), x);
	if (it != lt.end())//迭代器从前往后找,左闭右开,等于end就是没找到
	{
		lt.erase(it);
	}
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}
#include "list.h"
int main()
{
	//test_list2();
	rq::test_list01();
	return 0;
}

list和vector的不同

  1. 插入和删除:
    vector:是一个动态数组,它在内存中连续存储元素。这意味着vector可以支持快速的随机访问(通过下标),但插入和删除操作(尤其是在容器中间或开始位置)可能需要移动大量元素,因此可能相对较慢。
    list:是一个双向链表,每个元素都包含数据和指向列表中前一个和后一个元素的指针(或链接)。因此,list在插入和删除元素时(尤其是在容器中间或开始位置)非常高效,因为它只需要更改指针,而不需要移动其他元素。但是,list不支持快速的随机访问。
  2. 内存使用:vector使用的是一个连续的空间,空间利用率高,而list使用的是非连续的空间,空间利用率低
  3. 使用场景:
    如果你需要频繁地随机访问元素,且插入和删除操作较少,那么 vector 及其迭代器可能更适合。
    如果你需要频繁地在容器的中间进行插入和删除操作,那么 list 及其迭代器可能更合适。
  4. 性能:vector 的迭代器由于其随机访问特性,在需要快速访问容器中任意元素时非常高效。然而,在涉及大量插入或删除操作(尤其是在容器中间)时,可能会因为元素移动或内存重新分配而导致性能下降。
    list 的迭代器在遍历容器时通常较慢,因为它们不支持随机访问。但是,list 的插入和删除操作(特别是在容器的中间)通常比 vector 更快,因为它们不需要移动大量元素。

http://www.kler.cn/news/307166.html

相关文章:

  • 103.WEB渗透测试-信息收集-FOFA语法(3)
  • k8s中的lables和matchlables的作用
  • 54. 螺旋矩阵(定义四个方向然后遍历类)
  • 腾讯云升级多个云存储解决方案 以智能化存储助力企业增长
  • Java 入门指南:JVM(Java虚拟机)—— Java 类加载器详解
  • 【QGC】把QGroundControl地面站添加到Ubuntu侧边菜单栏启动
  • ElementUI 布局——行与列的灵活运用
  • Vue3 + Echarts 实现中国地图
  • 大数据处理技术:MapReduce综合实训
  • 【SSRF漏洞】——gopherus工具伪造
  • sqli-labs靶场自动化利用工具——第10关
  • 上汽大众:存储成本节约85%,查询性能提升5倍|OceanBase案例
  • 【Multi-UAV】多无人机实现凸多边形区域覆盖--Voronoi分割
  • Mysql树形结构表-查询所有子集数据
  • 【OJ刷题】快慢指针问题
  • ARM驱动学习之基础小知识
  • Windows环境本地部署Oracle 19c及卸载实操手册
  • STL-vector练习题
  • 【2025届华为秋招机考三道编程题之一】华为校招留学生软件开发工程师-真题机考笔试/(200分)- 跳格子3(Java JS Python C)
  • 【C++】模板进阶:深入解析模板特化
  • 【数据库】MySQL内置函数
  • Args4j:Java命令行参数解析的利器
  • Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 死亡对象判断方法
  • uniapp自定义导航栏以及页面加背景
  • CSP-J算法基础 计数排序
  • mis_table.cs 与 csharp_mis_table.h
  • Oracle(121)如何进行数据文件的恢复?
  • Linux中使用Docker构建Nginx容器完整教程
  • k8s使用本地docker私服启动自制的flink集群
  • OKHttp实现原理分享