【c++】序列容器之 list


一. list简介

list是可以在常数范围内任意位置进行插入和删除的序列容器,并且该容器支持前后双向迭代。这是基于list的底层数据结构是带头双向循环链表,因此list容器是不支持随即访问的(也就是不能像数组那样使用 [i]下标来访问元素),list访问元素的时间复杂度是线性时间。

二. list基本使用方法

2.1 list的常见构造函数

在这里插入图片描述

list的构造使用代码演示:

#include<list>
using namespace std;
void Test01()
{
	list<int> l1;//构造空的list
	list<int> l2(5);//l2元素个数为5,都为默认初始值
	list<int> l3(5,100);//l3中是5个值为100的元素
	list<int> l4(l3);//用l3拷贝构造l4

	//以数组为迭代器区间构造l5
	int arr[] = {1,2,3,4,5};
	len = sizeof(arr) / sizeof(arr[0]);
	list<int> l5(arr,arr+len);

	//c++11 的列表格式初始化
	list<char> l6{'h','e','l','l','o','w'};
}

2.2 list的iterator

在这里插入图片描述
在这里插入图片描述
[注意]
begin() + end() 是正向迭代器,对迭代器进行++操作,迭代器往后移动
rbegin() + rend() 是反向迭代器,对迭代器进行++操作,迭代器往前移动

iterator使用的代码演示:

#include<list>
using namespace std;
void Test02()
{
	list<int> l1{1,2,3,4,5};
	list<int>::iterator it = l1.begin();//返回第一个元素的迭代器
	list<int>::iterator end = l1.end();//返回最后一个元素的下一个的迭代器
	while(it != end)
	{
		cout << *it << " ";
		(*it) *= 2;
		++it;
 	}
 	cout << endl;
 	list<int>::reverse_iterator rit = l1.rbegin();//返回最后一个元素的迭代器
 	list<int>::reverse_iterator rend = l1.rend();//返回第一个元素的前一个元素迭代器
 	while(rit != rend)
 	{
 		cout << *rit <<" " ;
 		++rit;
 	}
}

vs2019测试结果如下:
在这里插入图片描述

2.3 list的容量相关

在这里插入图片描述
一点思考
为什么list没有像vector提供capacity和reserve接口??这还是基于list的底层数据结构(带头结点的双向循环链表),它不像vector一样是一段连续的空间,所以没有capacity(能容纳元素的最大个数)的说法;也无需为list容器预留空间,因为不存在因往容器加入元素而导致的空间扩容问题,往list加入元素时,需要申请一个节点,然后将该节点连入链表中即可,不会影响其他节点的物理地址。这与vector是不同的。

代码演示使用:

#include<list>
using namespace std;
void Test03()
{
	list<int> l1{1,2,3,4,5};
	cout << l1.size() << endl;//5
	l1.clear();//清空list
	cout << l1.empty() << endl;//true
}

2.4 list的加入和删除元素接口

在这里插入图片描述
在这里插入图片描述

接口使用演示:

#include<list>
#include<string>
using namespace std;
void Test03()
{
	list<string> l1{"i"," ","love"," ","c++"};
	cout << l1.front() <<endl;
	cout << l1.back() <<endl;
	
	 //尾插尾删  头插头删
    list<char> l2;
    l2.push_back('s');//  -->s
    l2.push_back('m');//s --> sm
    l2.push_front('h');//sm--> hsm
    list<char>::iterator it = l2.begin();
    while (it != l2.end())
    {
        cout << *it++;
    }
    cout << endl;
    l2.pop_front();//hsm --> sm
    l2.pop_back();//sm --> s
    
     //任意位置插入与删除insert and erase
    list<int> l3{ 3,4,5,7 };
    auto pos = find(l3.begin(), l3.end(), 5);   //查找5,返回迭代器
    l3.insert(pos, 3);  //  3, 4, 5, 7 --->  3, 4, [3], 5, 7
    l3.insert(pos, 4);  // 3, 4, 3, 5, 7---> 3, 4, 3,[4], 5, 7
    l3.erase(pos);      //3, 4, 3, 4, 5, 7---> 3, 4, 3, 4, 7
    //l3.erase(pos);//报错,因为迭代器已失效
    l3.erase(l3.begin());//3,4,3,4,7--->4,3,4,7
    
}

迭代器失效问题:

在使用erase()删除pos位置的元素后,pos迭代器已失效,不能再继续使用。如果需继续使用,要给pos重新赋值。

pos = l.erase(pos);//返回删除元素的下一个元素的迭代器
l.erase(pos);

在pos位置插入元素不会使pos失效,插入的元素不会改变原来的其他节点的物理地址。

l.insert(pos,3);
l.insert(pos,4);
l.insert(pos,5);

三、list与vector的对比

vectorlist
底层结构动态顺序表,一段连续空间带头节点双向循环链表
随机访问支持随机访问不支持随机访问,访问元素复杂度为O(n)
插入和删除任意位置插入和删除元素复杂度高,需要挪动大量元素,甚至是重新申请新的空间,复杂度为O(n)任意位置插入和删除都在O(1)时间完成
空间利用率空间利用率高,底层为连续空间,不会产生碎片化空间,缓存利用率高空间利用率低,底层节点动态开辟,容易产生碎片化空间,缓存利用率低
迭代器原生态指针对原生态指针(节点指针)进行封装
迭代器失效在插入元素时,可能会导致空间扩容,所以插入元素迭代器会失效; 在删除元素时,只是将后面的元素往前挪动,理论上迭代器是不会失效的,但是如果恰好删除的是尾元素,迭代器就指向了end(),也就失效了,所以为了避免这种巧合,在删除元素迭代器也会失效哦!在插入元素时,迭代器不会失效;删除元素会导致当前迭代器失效,其他迭代器不受影响
使用常景需要随机访问、需要高效存储、不关心插入删除的效率无需随机访问、插入删除操作频繁

总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值