C++ list 的介绍和使用


list 的介绍

  1. list 是一种可以在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代

  2. list 的底层是双向循环链表,每个元素存储在互不相关的独立结点当中(离散存储),所以 list 容器不存在迭代器失效的问题

  3. list 与 forward_list 非常相似,最主要的不同在于 forward_list 是单链表,只能进行单方向迭代

  4. 与其他容器相比,list 通常在任意位置进行插入、删除元素的执行效率更高

  5. list 和 forward_list 最大的缺陷是不支持在任意位置的随机访问,其次,list 还需要一些额外的空间,以保存每个结点之间的关联信息(对于存储的类型较小的元素,这是一个重要影响)


list 的使用

构造函数

//构造一个某类型的空容器
template <class T>
list<T>();

//构造一个含有 n 个 val 值的某类型容器
list<T>(size_t n, const T& val = T());

//构造某类型容器的复制品,区间为 [first, last)
template<typename InputIterator>
list<T>(InputIterator first, InputIterator last);

//拷贝构造函数
list<T>(const list<T>& lst);

list<int> lst1;
list<int> lst2(2, 10);
string str = "hello";
list<char> lst3(str.begin(), str.end());

//数组也可以匹配构造函数中的 InputIterator
int arr[10] = {1, 2, 3, 4};
list<int> lst4(arr, arr + 4);	

list<int> lst5(lst4);

list 的插入和删除

push_front() 和 pop_front()

//头插
void push_front(const T& val);

//头删
void pop_front();

list<int> lst = {1, 3, 5, 7};
lst.push_front(0);	//0 1 3 5 7
lst.pop_front();
lst.pop_front();	//3 5 7

push_back() 和 pop_back()

//尾插和尾删
void push_back(const T& val);
void pop_back();

insert()

//在 pos 位置插入元素 val,返回插入元素的迭代器
list<T>::iterator insert (list<T>iterator pos, const T& val);

//在 pos 位置前插入 n 个 val 元素
void insert (list<T>::iterator pos, size_t n, const T& val);

//在 pos 位置前插入某个容器区间 [first, las) 的元素
template <class InputIterator>    
void insert (iterator pos, InputIterator first, InputIterator last);

list<int> lst;
list<int>::iterator it = lst.insert(lst.end(), 10);
cout << *it;	//10
lst.insert(it, 2, 3);	//3 3 10

vector<int> vtr = {1, 2};
//使用 find() 函数插入容器指定元素前面
it = std::find(lst.begin(), lst.end(), 10);
lst.insert(ret, vtr.begin(). vtr.end());	//3 3 1 2 10

erase()

//删除 pos 位置处的元素
list<T>::iterator erase (list<T>::iterator pos);
//删除容器区间 [first, last) 的元素
list<T>::iterator erase (list<T>::iterator first, list<T>::iterator last);

list<int> lst = {1, 2, 3, 4};
lst.erase(lst.begin());		//2 3 4
list<int>::iterator it = std::find(lst.begin(), lst.end(), 3);
lst.erase(lst.begin(), it);	//3 4

list 迭代器的使用

list<T>::iterator begin();
list<T>::iterator end();
list<T>::const_iterator begin()const;
list<T>::const_iterator end()const;
list<T>::const_iterator cbegin()const;
list<T>::const_iterator cend()const;
list<T>::reverse_iterator rbegin();
list<T>::reverse_iterator rend();
list<T>::const_reverse_iterator rbegin()const;
list<T>::const_reverse_iterator rend()const;
list<T>::const_reverse_iterator crbegin()const;
list<T>::const_reverse_iterator crend()const;

list<int> lst = {1, 2, 3, 4};
//正向迭代器遍历
for(list<int>::iterator it = lst.begin(); i != lst.end(); it++) {
	cout << *it << ' ';
}	//1 2 3 4

//反向迭代器遍历
for(list<int>::reverse_iterator it = lst.rbegin(); lst != rend(); lst++) {
	cout << *it << ' ';
}	//4 3 2 1

list 元素获取

front() 和 back()

//获取容器第一个元素
T& front();
const T& front()const;
//获取容器最后一个元素
T& back();
const T& back()const;

list<int> lst = {1, 2, 3};
cout << lst.front();	//1
cout << lst.back();		//3

❗️注意:list 容器不能通过重载 operator[ ] 和 at() 访问元素,也就是说不能通过下标访问元素


list 的大小控制

size() 和 max_size()

//返回容器有效元素个数
size_t size()const;

//返回容器能存储的元素的最大数量
size_t max_size()const;

list<int> lst = {1, 2};
cout << lst.size();		//2

❗️注意:list 没有 capacity() 和 reserve() 成员函数,因为对于链表来说,只有在插入元素时,才会为其分配一个空间,并不会预留空间


resize

🌸resize 的两种情况:

  1. 当所给值大于当前的 size 时,将 size 扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值

  2. 当所给值小于当前的 size 时,删除容器尾部的元素,将 size 缩小到该值

void resize(size_t n, const T& val = T());

list<int> lst = {1, 2};
lst.resize(5, 0);	//1 2 0 0 0
lst.resize(1, 3);		//1

empty() 和 clear()

//判断容器是否为空
bool empty()const;

//清空容器中的所有元素
void clear();

list 操作函数

sort()

🌸将容器中的数据进行排序,默认为升序

//默认的升序排序函数
void sort();

//传入一个比较函数(模板类型实例化为函数指针),可以实现降序排序
template <typename Compare>
void sort(Cmopare com);

list<int> lst = {1, 5, 9, 7, 3};
lst.sort();
for(auto e : lst) {
	cout << e << ' ';	
}	//1 3 5 7 9

🌸下面介绍如何实现降序排序(当然也可以用下面的方向实现升序排序):

template <typename Compare>
void sort(Cmopare com);

//先实现一个 compare 比较函数
template <class T>
bool compare(const T& val1, const T& val2) {
	return val1 > val2;		//如果为真,则排序时 val1 会在 val2 前面
}
list<int> lst = {1, 2, 3, 4};
//传入该比较函数,此时 sort 的模板参数 Compare 实例化为 bool (const T&, const T&)
lst.sort(compare<int>);	
for(auto e : lst) {
	cout << e << ' ';	
}	//4 3 2 1

splice()

🌸用于两个 list 容器之间的拼接,一共有三种拼接方式:

//将整个容器 lst 拼接到当前容器的 pos 位置前
void splice (list<T>::iterator pos, list<T>& lst);

//将 lst 容器中 it 位置的元素拼接到当前容器 pos 位置前
void splice (list<T>::iterator pos, list<T>& lst, list<T>::iterator it);

//将 lst 容器区间 [first, last) 的元素拼接到当前容器的 pos 位置前
void splice (list<T>::iterator pos, list<T>& lst, list<T>::iterator first, list<T>::iterator last);

❗️注意:容器当中被拼接到另一个容器的数据在原容器当中就不存在了


remove()

🌸删除容器中特定值的元素

void remove (const T& val);

list<int> lst = {1, 2, 3, 4, 2};
lst.remove(2);
for(auto e : lst) {
	cout << e << ' ';
}	//1 3 4

remove_if()

🌸删除容器中满足条件的元素

template <class Predicate>  
void remove_if (Predicate pred);

//Predicate 需要实例化为一个函数指针类型
bool less_than_10(const T& val) {
	return val < 10;
}
list<int> lst = {1, 2, 3, 19, 17};
//remove_if 函数模板参数实例化为 bool (const T&)
lst.remove_if(less_than_10);
for(auto e : lst) {
	cout << e << ' ';
}	//19 17

unique()

🌸删除容器当中连续的重复元素

❗️注意:该函数只删除连续的重复元素,如果要实现容器中所有元素唯一,则需要先将容器进行排序

void unique();
list<int> lst1 = {1, 1, 2, 3, 2, 4, 4, 3};
lst1.unique();
for(auto e : lst1) {
	cout << e << ' ';
}	//1 2 3 2 4 3
//由上我们发现,我们只删除了容器中连续的重复元素,但是并没有做到容器所有元素唯一

//配合 sort() 函数实现容器元素唯一
list<int> lst2 = {1, 1, 2, 3, 2, 4, 4, 3};
lst2.sort();	//升序排序
lst2.unique();
for(auto e : lst2) {
	cout << e << ' ';
}	//1 2 3 4

提问:对于上面第一个例子,我们删除连续的相同元素,那删除的元素是前者还是后者?

答案是后者,具体原因看下面介绍的另一个 unique 重载函数

//判断相邻的两个元素是否满足参数 com 的条件,如果满足则删除
//com 是一个比较函数,也就是说,unique 函数模板参数实例化为 bool (const T&, const T&)
template <class Compare>  
void unique (Compare com);

bool compare1(const T& val1, const T& val2) {
	return (int)val1 == (int)val2;
}
bool compare2(const T& val1, const T& val2) {
	return abs(val1 - val2) < 3;
}

double arr[] = {1.1, 1.23, 3.1, 3.5, 3.7, 6.7, 9.0};
list<double> lst(arr, arr + sizeof(arr) / sizeof(arr[0]));
//删除相邻元素整数类型相等的情况
lst.unique(compare1);	//1.1 3.1 6.7 9.0

//删除相邻元素差的绝对值小于3的情况
lst.unique(compare2);	//1.1 6.7 9.0

🌸由上可知,如果满足条件,我们删除的是位置相对后面的元素。事实上,list 调用函数时,传入的参数为:

bool compare(*it, *(it - 1));		//其中 it 为迭代器

当满足函数返回为真时,就会删除 it 所在位置的元素


merge()

🌸用于和并两个有序的 list 容器,并保证合并的 list 容器仍然有序(类似于归并排序)

void merge (list<T>& lst);

list<int> lst1 = {2, 1, 5, 6};
list<int> lst2 = {6, 7, 9, 8};
//先进行升序排序
lst1.sort();
lst2.sort();
//升序合并
lst1.merge(lst2);
for(auto e : lst1){
	cout << e << ' ';	
}	//1 2 5 6 6 7 8 9
cout << lst2.size();	//0,说明被合并的容器元素被删除

//根据参数 comp 进行排序(可以实现弱排序)
template <class Compare>  
void merge (list<T>& lst, Compare comp);

//对浮点数的整数位进行排序
bool compare(const T& val1. const T& val2) {
	return (int)val1 < (int)val2;
}
list<double> lst1 = {1.1, 3.3, 3.7. 4.4};
list<double> lst2 = {1.2, 3.8, 3.1};
lst1.sort();
lst2.sort();
lst2.sort(lst1, compare);
for(auto e : lst1) {
	cout << e << ' ';
}	//1.1 1.2 3.3 3.7 3.1 3.8 4.4

❗️注意:

  1. merge 函数默认实现升序合并,如果要实现降序合并,可以通过参数 comp 进行
//表示如果 val1 > val2,则 val1 应该在 val2 前面
bool compare(const T& val1, const T& val2) {
	return val1 > val2;
}
list<int> lst1 = {2, 1, 5, 6};
list<int> lst2 = {6, 7, 9, 8};
//先进行降序排序
lst1.sort(compare);
lst2.sort(compare);
//升序合并
lst1.merge(lst2, compare);
for(auto e : lst1){
	cout << e << ' ';	
}	//9 8 7 6 6 5 2 1
  1. 如果传入的参数为自己本身(&lst == this),则什么也不做

  2. 合并会使得另外一个容器的所有元素删除

  3. 升序排序配合升序合并,降序排序配合降序合并,不能混乱在一起


reverse()

🌸用于反转整个容器

void reverse();

list<int> lst = {1, 2, 3};
lst1.reverse();
for(auto e : lst) {
	cout << e << ' ';	
}	//3 2 1

assign()

🌸删除容器原来的所有元素,并设置新的元素

//将 n 个值为 val 的元素分配给容器
void assign (size_t n, const T& val);

//将区间 [first, last) 的元素分配给容器
template <class InputIterator>  
void assign (InputIterator first, InputIterator last);

list<int> lst = {8, 8, 8};
lst.assign(3, 6);	//6 6 6
vector<int> vtr = {6, 8, 8};
lst.assign(vtr,begin(), vtr.end());		//6 8 8

swap()

🌸交换两个容器中的元素

void swap(list<T>& lst);

其他函数

operator=()

list<T>& operator=(const list<T>& lst);		//不多介绍

emplace()

🌸与 insert 类似,在指定位置插入一个元素,返回插入的新元素的迭代器

//可变参数模板(c++11,后面会详细说明)
template <class... Args>  
list<T>::iterator emplace (const_iterator po, Args&&... args);

list<vector<int>> lst;
lst.emplace(lst.begin(), 1, 2, 3, 4);
vector<int> vtr = lst.begin();
for(auto e : vtr) {
	cout << e << ' ';	
}	// 1 2 3 4
//上面说明,emplace 插入的元素是通过其传入的参数构造出来的

//可变参数模板传入的参数类型可以不同
list<pair<string, int>> lst;
lst.emplace(lst.begin(), "int", 1);
vector<int> vtr = lst.begin();
for(auto e : vtr) {
	cout << e.first << ' ' << e.second;	
}	//int 1

❗️insert 与 emplace 的区别:

  1. insert 插入的元素是现有的元素(即已经构造好了),但是 emplace 插入的元素是通过传入的参数临时构造的

  2. 因为 emplace 传入的元素是临时构造的,所以该元素构造所需要的参数个数我们不清楚,所以 emplace 是一个变参数函数

  3. emplace 还有两个特殊形式:emplace_back() 和 emplace_front(),分别对应 push_back() 和 push_front(),这里不多赘述

template <class... Args>
void emplace_front(Args&&... args);

template <class... Args>
void emplace_back(Args&&... args);

本篇文章到这里就结束啦,欢迎批评指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值