文章目录
list 的介绍
-
list 是一种可以在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代
-
list 的底层是双向循环链表,每个元素存储在互不相关的独立结点当中(离散存储),所以 list 容器不存在迭代器失效的问题
-
list 与 forward_list 非常相似,最主要的不同在于 forward_list 是单链表,只能进行单方向迭代
-
与其他容器相比,list 通常在任意位置进行插入、删除元素的执行效率更高
-
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 的两种情况:
-
当所给值大于当前的 size 时,将 size 扩大到该值,扩大的数据为第二个所给值,若未给出,则默认为容器所存储类型的默认构造函数所构造出来的值
-
当所给值小于当前的 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
❗️注意:
- 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
-
如果传入的参数为自己本身(&lst == this),则什么也不做
-
合并会使得另外一个容器的所有元素删除
-
升序排序配合升序合并,降序排序配合降序合并,不能混乱在一起
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 的区别:
-
insert 插入的元素是现有的元素(即已经构造好了),但是 emplace 插入的元素是通过传入的参数临时构造的
-
因为 emplace 传入的元素是临时构造的,所以该元素构造所需要的参数个数我们不清楚,所以 emplace 是一个变参数函数
-
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);
本篇文章到这里就结束啦,欢迎批评指正!