首页 > 编程语言 >list(c++)

list(c++)

时间:2024-10-30 10:47:44浏览次数:3  
标签:node const iterator list c++ array prev

list介绍

list是STL容器中的容器,且元素在容器中的位置是分散的并与大小无关。list的底层是双向链表,其优势是在任意位置插入和删除元素的时间复杂度为O(1),但无法通过“下标[ ]”直接访问元素,需要通过从头(尾)遍历元素找到元素,多用于需要大量数据的插入和删除,且对数据的随机访问比较少。

list使用

一、list的构造

构造函数接口说明
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

 

// list的构造
    list<int> l1;                         // 构造空的l1
    list<int> l2(4, 100);                 // l2中放4个值为100的元素
    list<int> l3(l2.begin(), l2.end());  // 用l2的[begin(), end())左闭右开的区间构造l3
    list<int> l4(l3);                    // 用l3拷贝构造l4

    // 以数组为迭代器区间构造l5
    int array[] = { 16,2,77,29 };
    list<int> l5(array, array + sizeof(array) / sizeof(int));

    // 列表格式初始化C++11
    list<int> l6{ 1,2,3,4,5 };

二、list 的iterator的使用

 

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

 

// list迭代器的使用
// 注意:遍历链表只能用迭代器和范围for
void PrintList(const list<int>& l)
{
    // 注意这里调用的是list的 begin() const,返回list的const_iterator对象
    for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
    {
        cout << *it << " ";
        // *it = 10; 编译不通过
    }

    cout << endl;
}

void TestList2()
{
    int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
    list<int> l(array, array + sizeof(array) / sizeof(array[0]));
    // 使用正向迭代器正向list中的元素
    // list<int>::iterator it = l.begin();   // C++98中语法
    auto it = l.begin();                     // C++11之后推荐写法
    while (it != l.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;

    // 使用反向迭代器逆向打印list中的元素
    // list<int>::reverse_iterator rit = l.rbegin();
    auto rit = l.rbegin();
    while (rit != l.rend())
    {
        cout << *rit << " ";
        ++rit;
    }
    cout << endl;
}

三、list capacity

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

 四、list element access

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

五、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 中的有效元素
// list插入和删除
// push_back/pop_back/push_front/pop_front
void TestList3()
{
    int array[] = { 1, 2, 3 };
    list<int> L(array, array + sizeof(array) / sizeof(array[0]));

    // 在list的尾部插入4,头部插入0
    L.push_back(4);
    L.push_front(0);
    PrintList(L);

    // 删除list尾部节点和头部节点
    L.pop_back();
    L.pop_front();
    PrintList(L);
}

// insert /erase 
void TestList4()
{
    int array1[] = { 1, 2, 3 };
    list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));

    // 获取链表中第二个节点
    auto pos = ++L.begin();
    cout << *pos << endl;

    // 在pos前插入值为4的元素
    L.insert(pos, 4);
    PrintList(L);

    // 在pos前插入5个值为5的元素
    L.insert(pos, 5, 5);
    PrintList(L);

    // 在pos前插入[v.begin(), v.end)区间中的元素
    vector<int> v{ 7, 8, 9 };
    L.insert(pos, v.begin(), v.end());
    PrintList(L);

    // 删除pos位置上的元素
    L.erase(pos);
    PrintList(L);

    // 删除list中[begin, end)区间中的元素,即删除list中的所有元素
    L.erase(L.begin(), L.end());
    PrintList(L);

 // 交换l1和l2中的元素
    list<int> l2;
    l1.swap(l2);
    PrintList(l1);
    PrintList(l2);

    // 将l2中的元素清空
    l2.clear();
    cout << l2.size() << endl;
}

 六、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);
    }
}

 

模拟实现

一、节点

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

二、构造

        void empty_init()
		{
			_head = new Node();
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
       //无参构造
		list()
		{
			empty_init();
		}
        //拷贝构造
		// lt2(lt1)
		list(const list<T>& lt)
		{
			empty_init();

			for (auto& e : lt)
			{
				push_back(e);
			}
		}
        //n个val构造
        list(size_t n, const T& val = T())
 		{
			empty_init();
			for (size_t i = 0; i < n; i++)
			{
				push_back(val);
			}
		}

三、迭代器

迭代器:类封装节点指针,重载运算符,模拟指针的行为

	template<class T, class Ref, class Ptr>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator<T, Ref, Ptr> Self;
		Node* _node;

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

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

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

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

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

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

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

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

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



		typedef list_iterator<T, T&, T*> iterator;
		typedef list_iterator<T, const T&, const T*> const_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);
		}

四、insert

        iterator insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* newnode = new Node(val);
			Node* prev = cur->_prev;

			// prev newnode cur
			prev->_next = newnode;
			newnode->_prev = prev;

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

			return iterator(newnode);
		}

五、erase

        iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* del = pos._node;
			Node* prev = del->_prev;
			Node* next = del->_next;

			prev->_next = next;
			next->_prev = prev;
			delete del;

			--_size;

			return iterator(next);
		}

六、头(尾)插(删)

        void push_back(const T& x)
		{
			/*Node* new_node = new Node(x);
			Node* tail = _head->_prev;

			tail->_next = new_node;
			new_node->_prev = tail;

			new_node->_next = _head;
			_head->_prev = new_node;*/

			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_front()
		{
			erase(begin());
		}

		void pop_back()
		{
			erase(--end());
		}

七、析构

        ~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}

八、赋值运算符重载

        // lt2 = lt3
		//list& operator=(list lt)
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

九、clear

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

标签:node,const,iterator,list,c++,array,prev
From: https://blog.csdn.net/qq_75271671/article/details/143217803

相关文章

  • 基于 C++ 的 UG/NX 二次开发环境配置
    基于C++的UG/NX二次开发环境配置参考教程:UG/NX二次开发环境配置方法(nx1980+vs2019)-CSDN博客NX二次开发VS环境搭建-怡宁塑胶模具设计-博客园(cnblogs.com)NX/UG二次开发环境配置方法—史上最详细版(以NX11.0和VisualStudio2017为例)_nx二次开发-CSDN博客在Windows......
  • C++:日期类的实现
      目录一.日期类1.类的构造函数和拷贝构造2.日期的比较3.日期加减一个天数1.日期加天数2.日期减天数4.日期的++与--1.日期的++2.日期的--5.日期减日期6.日期的输入和输出二.整体代码1.Date.h2.Date.cpp      在前面几节的内容中,我们学习了类的基本......
  • 【C/C++】5.并发控制
    并发控制(ConcurrencyControl)是指在多线程或多进程环境中,确保多个操作在共享资源上的访问不会发生冲突或产生不一致的情况。并发控制的核心目标是在允许并发操作的同时,保证系统的正确性、数据的一致性和完整性。在并发环境下,不同的线程或进程可能会同时访问共享资源(例如变量、文......
  • 每日OJ题_牛客_AB20走迷宫_BFS_C++_Java
    目录牛客_AB20走迷宫_BFS题目解析C++代码Java代码牛客_AB20走迷宫_BFS走迷宫_牛客题霸_牛客网(nowcoder.com)描述:        给定一个n×m的网格,在网格中每次在不超过边界的情况下可以选择向上、向下、向左、向右移动一格。网格中的一些格子上放置有障碍物,放有......
  • qt的c++环境配置和c++基础【正点原子】嵌入式Qt5 C++开发视频
    QTc++环境配置和c++基础c++环境配置和工程创建  1.配置步骤  2.新建qt工程目录和工程  3.重启qt后打开最近的qt项目c++基础-类和对象  1.什么是类和对象    A.类的定义    B.类的结构表示    C.类的访问权限    D.对象的定义    E.类和......
  • vue2基础组件通信案例练习:把案例Todo-list改写成本地缓存
    @目录概述前端代码本人其他相关文章链接概述前面文章案例已经练习了父子组件之间的通信,这一节讲述如何把todos数组放进本地缓存中,因为实际开发场景中频繁查询的数据很有可能会用到本地缓存技术。思考:如何改成使用本地缓存,是写一堆按钮每次触发就是往本地缓存种get和set?答案......
  • 什么时候用C而不用C++
    在选择编程语言时,我们可能会在C和C++之间犹豫。C语言通常用于低级别的系统编程、嵌入式系统开发、操作系统组件、与硬件密切相关的软件、对性能要求极高的应用以及早期使用C语言编写且维护成本较低的项目。而C++以其面向对象特性、灵活的抽象能力、类和模板等特性而广泛应用于软......
  • C++中结构体是使用实例还是指针
    在C++中,结构体(struct)可以通过指针或直接实例来定义。选择使用指针或直接实例化结构体取决于几个因素,包括内存管理、性能、语义和使用场景。以下是一些常见的考虑因素:1. 内存管理:指针:使用指针时,结构体的实例通常在堆上分配。这允许动态管理内存,可以在运行时决定结构体的......
  • 【OJ题解】C++ 把字符串转换成整数
    ......
  • Android实现ListView嵌套Checkbox真正的多选、全选、反选 (附完整源码)
    Android实现ListView嵌套Checkbox真正的多选、全选、反选1.创建项目2.添加布局文件3.创建数据模型4.创建自定义Adapter5.实现MainActivity6.运行项目在Android中实现一个包含复选框的ListView,并支持多选、全选和反选的功能,可以按照以下步骤进行。我们将......