110.STL中的list
110.STL中的list
1.list的介绍
1.list是序列容器,允许在序列中的任何位置执行固定O(1)时间复杂度的插入和删除操作,并在两个方向进行迭代。
2.list容器使用双链表实现;双链表将每个元素存储在不同的位置,每个节点通过next,prev指针链接成顺序表。
3.list与其他标准序列容器(array,vector和deque)相比,list通常可以在容器内的任何位置插入、提取和移动元素。
4.list与其他标准序列容器(array,vector和deque)相比,list和forward_list(单链表实现)的主要缺点是他们不能通过位置直接访问元素;例如,要访问列表中的第五个元素,必须从已知位置(开始或结束)迭代到该位置,需要线性时间开销。
5.存储密度低,list要使用一些额外的内容空间(next,prev)来保持与每个元素相关联(前后续的线性)的链接信息,从而导致存储小元素类型(如char,short,int等)的列表的存储密度低。
2.list函数说明
2.1成员类型
成员类型 | 定义 |
---|---|
value_type | T |
allocator_type | Allocator |
size_type | 无符号整数类型(通常是 std::size_t ) |
difference_type | 有符号整数类型(通常是 std::ptrdiff_t ) |
reference | value_type& |
const_reference | const value_type& |
pointer | value_type* |
const_pointer | const value_type* |
iterator | 指向 value_type 的双向迭代器 |
const_iterator | 指向 const value_type 的双向迭代器 |
reverse_iterator | std::reverse_iterator |
const_reverse_iterator | std::reverse_iterator<const_iterator> |
2.2构造函数
函数 | 功能 |
---|---|
list(); | 默认构造函数。构造拥有默认构造的分配器的空容器 |
list( size_type count,const T& value); | 构造拥有 count 个有值 value 的元素的容器。 |
explicit list( size_type count ); | 构造拥有个 count 默认插入的 T 实例的容器。 |
list( InputIt first, InputIt last, const Allocator& alloc = Allocator() ); | 构造拥有范围 [first, last) 内容的容器。 |
list( const list& other ); | 复制构造函数。构造拥有 other 内容的容器。 |
list& operator=( const list& other ); | 复制赋值运算符。以 other 的副本替换内容。 |
list& operator=( std::initializer_list ilist ); | 以 initializer_list ilist 所标识者替换内容。 |
void assign( size_type count, const T& value ); | 以 count 份 value 的副本替换内容。 |
template< class InputIt > | |
void assign( InputIt first, InputIt last ); | 以范围 [first, last) 中元素的副本替换内容。 |
void assign( std::initializer_list ilist ); | 以来自 initializer_list ilist 的元素替换内容 |
例子:
#include <iostream>
#include <list>
using namespace std;
template<class T>
void Print(const list<T>& my)
{
class list<T>::const_iterator it = my.begin();
for (; it != my.end(); it++)
{
cout << *it << "\t";
}
cout << endl;
}
int main()
{
list<int> list1 = { 12,23,34 };
list<int> list2(3, 11);
list<int> list3(list2);
list<string> list4 = { "This","is","windows" };
list<string> list5;
list<string> list6;
list5 = list4;
list6.assign(3, "This");
Print(list1);
Print(list2);
Print(list3);
Print(list4);
Print(list5);
Print(list6);
return 0;
}
输出:
12 23 34
11 11 11
11 11 11
This is windows
This is windows
This This This
2.3元素访问
元素访问 | 功能 |
---|---|
reference front(); | 返回到容器首元素的引用 |
const_reference front() const; | 返回到容器首元素的引用 |
reference back(); | 返回到容器中最后一个元素的引用 |
const_reference back() const; | 返回到容器中最后一个元素的引用 |
例子:
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> list1 = { 12,23,34 };
cout << "list1.front():" << list1.front() << endl;
cout << "list1.back():" << list1.back() << endl;
return 0;
}
输出:
list1.front():12
list1.back():34
2.4迭代器
迭代器 | 功能 |
---|---|
iterator begin(); | 返回指向 list 首元素的迭代器 |
若 list 为空,则返回的迭代器将等于 end() | |
const_iterator begin() const; | 返回指向 list 首元素的迭代器 |
若 list 为空,则返回的迭代器将等于 end() | |
iterator end(); | 返回指向 list 末元素后一元素的迭代器 |
const_iterator end() const; | 返回指向 list 末元素后一元素的迭代器 |
reverse_iterator rbegin(); | 返回指向逆向 list 首元素的逆向迭代器 |
const_reverse_iterator rbegin() const; | 返回指向逆向 list 首元素的逆向迭代器 |
reverse_iterator rend(); | 返回指向逆向 list 末元素后一元素的逆向迭代器 |
const_reverse_iterator rend() const; | 返回指向逆向 list 末元素后一元素的逆向迭代器 |
例子:
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> list1 = { 12,23,34 };
list<int>::const_iterator it1 = list1.begin();
for (; it1 != list1.end(); it1++)
{
cout << *it1 << "\t";
}
cout << endl;
list<int>::reverse_iterator it2 = list1.rbegin() ;
for (; it2 != list1.rend(); it2++)
{
cout << *it2 << "\t";
}
cout << endl;
return 0;
}
输出:
12 23 34
34 23 12
2.5容量
容量 | 功能 |
---|---|
bool empty() const; | 检查容器是否无元素,即是否 begin() == end() |
size_type size() const; | 返回容器中的元素数,即 std::distance(begin(), end()) |
size_type max_size() const; | 返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器的 std::distance(begin(), end()) |
例子:
#include <iostream>
#include <list>
using namespace std;
int main()
{
list<int> list1 = { 12,23,34 };
cout << "list1.empty():" << list1.empty() << endl;
cout << "list1.size():" << list1.size() << endl;
cout << "list1.max_size():" << list1.max_size() << endl;
return 0;
}
输出:
list1.empty():0
list1.size():3
list1.max_size():768614336404564650
2.6修改器
修改器 | 功能 |
---|---|
void clear(); | 从容器擦除所有元素。此调用后 size() 返回零 |
iterator insert( iterator pos, const T& value ); | 在 pos 前插入 value |
void insert( iterator pos, size_type count, const T& value ); | 在 pos 前插入 value 的 count 个副本 |
template< class InputIt > | |
void insert( iterator pos, InputIt first, InputIt last); | 在 pos 前插入来自范围 [first, last) 的元素 |
iterator insert( const_iterator pos, std::initializer_list ilist ); | 在 pos 前插入来自 initializer_list ilist 的元素 |
iterator erase( iterator pos ); | 移除位于 pos 的元素 |
iterator erase( iterator first, iterator last ); | 移除范围 [first; last) 中的元素 |
void pop_back(); | 移除容器的末元素 |
void push_front( const T& value ); | 前附给定元素 value 到容器起始 |
void push_back( const T& value ); | 后附给定元素 value 到容器尾 |
void pop_front(); | 移除容器首元素 |
void resize( size_type count ); | 重设容器大小以容纳 count 个元素 |
void resize( size_type count, T value = T() ); | count - 容器的大小,value - 用以初始化新元素的值 |
void swap( list& other ); | 将内容与 other 的交换 |
例子:
#include <iostream>
#include <list>
using namespace std;
template<class T>
void Print(const list<T>& my)
{
typename list<T>::const_iterator it = my.begin();
for (; it != my.end(); it++)
{
cout << *it << "\t";
}
cout << endl;
}
int main()
{
list<int> list1 = { 12,23,34 };
list<int> list2 = { 1,2,3,4,5 };
Print(list1);
Print(list2);
auto it = list1.begin();
list1.insert(it, 55);
Print(list1);
it++;
list1.insert(it, 3, 55);
Print(list1);
list1.erase(it);
Print(list1);
list1.swap(list2);
Print(list1);
Print(list2);
return 0;
}
输出:
12 23 34
1 2 3 4 5
55 12 23 34
55 12 55 55 55 23 34
55 12 55 55 55 34
1 2 3 4 5
55 12 55 55 55 34
2.7操作
操作 | 功能 |
---|---|
void merge( list& other ); | 归并二个已排序链表为一个。链表应以升序排序 |
void splice( const_iterator pos, list& other ); | 从 other 转移所有元素到 *this 中。元素被插入到 pos 所指向的元素之前。操作后容器 other 变为空 |
void splice( const_iterator pos, list& other, const_iterator it ); | 从 other 转移 it 所指向的元素到 *this 。元素被插入到 pos 所指向的元素之前 |
void splice( const_iterator pos, list& other, const_iterator first, const_iterator last); | 从 other 转移范围 [first, last) 中的元素到 *this 。元素被插入到 pos 所指向的元素之前 |
void remove( const T& value ); | 移除所有满足特定标准的元素。value - 要移除的元素的值 |
void reverse(); | 逆转容器中的元素顺序 |
void unique(); | 从容器移除所有相继的重复元素。只留下相等元素组中的第一个元素 |
void sort(); | 以升序排序元素。保持相等元素的顺序。用 operator< 比较元素 |
template< class Compare > | |
void sort( Compare comp ); | 以升序排序元素。保持相等元素的顺序。用给定的比较函数 comp |
例子:
#include <iostream>
#include <list>
using namespace std;
template<class T>
void Print(const list<T>& my)
{
typename list<T>::const_iterator it = my.begin();
for (; it != my.end(); it++)
{
cout << *it << "\t";
}
cout << endl;
}
int main()
{
list<int> list1 = { 2,1,9,5,3,7 };
list<int> list2 = { 1,8,3,6,0,1,5 };
Print(list1);
Print(list2);
list1.sort();
list2.sort();
Print(list1);
Print(list2);
list2.unique();
Print(list2);
list1.merge(list2);
Print(list1);
Print(list2);
return 0;
}
输出:
2 1 9 5 3 7
1 8 3 6 0 1 5
1 2 3 5 7 9
0 1 1 3 5 6 8
0 1 3 5 6 8
0 1 1 2 3 3 5 5 6 7 8 9
2.8vector和list区别
区别 | vector | list |
---|---|---|
底层实现 | 连续存储的容器,动态数组,在堆上分配空间 | 动态双向链表,在堆上 |
空间利用率 | 连续空间,不易造成内存碎片,空间利用率高 | 节点不连续,易造成内存碎片,小元素使节点密度低,空间利用率低 |
查询元素 | iterator operator[];find O(n);binary_search O(logn) | iterator find O(n) |
插入和删除 | 在最后插入(空间够):push_back(val);O(1)。在最后插入(空间不够):需要内存申请和释放,以及对之前数据进行拷贝。在之间插入(空间够):内存拷贝。在之间插入(空间不够):需要内存申请和释放,以及对之前数据进行拷贝。Insert(it,va) ,O(n)。在最后删除:pop_back(),O(1)。在之间删除:内存拷贝,不需要释放内存。erase(it),O(n)。resize(10):开辟空间,存储数据。reserve(10):只开辟空间,不存储数据。初始化vector空间,提高vector的使用效率。版权声明:本文为CSDN博主「愚蠢的土拨鼠。」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 | 插入:O(1),需要申请内存。push_back(),O(1);erase(it) ,O(n); |
迭代器 | 随机迭代器,迭代器检查越界。支持++,–,+,+=,<,>,!=,== | 双向迭代器,迭代器检查越界。支持++,–,==,!= |
迭代器失效 | 插入和删除元素都会导致迭代器失效 | 插入元素 |
总结
1.vector底层实现是数组;list是双向链表。
2.vector支持随机访问,list不支持。
3.vector是顺序内存,list不是。
4.vector在中间节点进行插入删除会导致内存拷贝,list不会。
5.vector一次性分配好内存,不够时才进行扩容;list每次插入新节点都会进行内存申请。
6.vector随机访问性能好,插入删除性能差;list随机访问性能差,插入删除性能好。
vector和list的使用场景
vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随机存取,而不在乎插入和删除的效率(很少使用插入和删除操作),选用vector
list 拥有一段不连续的内存空间,如果需要大量的插入和删除的操作,随机存取很少使用,选用list。
参考: C++中list详解_c++ list_愚蠢的土拨鼠。的博客-CSDN博客
3.forward_list
forward_list为单向链表的泛化容器,与list双向链表容器一样,实现了线性表数据的链表存储,数据元素不必在物理内存中连续分布。STL中List是双向链表,而Slist是单向链表。它们的区别:Slist的迭代器是单向的Forward Iterator,而list的迭代器是双向的Bidirectional Iterator。Slist所耗用的空间更小,操作更快。它们共同的特点是,插入、移除、接合等操作并不会造成原有的迭代器失效。slist插入时,需要从前遍历,找到插入点的位置。为了更快插入,提供了insert_after,erase_after。slist提供push_front()操作,故其元素次序与元素插入顺序相反。
3.1forward_list技术原理
forward_list内部的链表由头指针、头节点和元素节点组成,每个节点含有指向后继节点的指针,最后一个节点的指针为null,可见forward_list没有形成一个环形回路。
头节点一般不存储数据,为了使各个元素节点都有前驱节点的指针指向它以便能够不加区别的对第一个元素节点和其他元素节点进行统一处理,所以构造头节点。
slist应用基础
list对象的创建和vector一样,不多解释。元素的删除、归并、排序与list相同。
使用时需要包含头文件
// 包含头文件
#include <forward_list>
3.2初始化
通常使用push_front函数进行初始化,由于slist的头节点仅有一个指针域保存首元素地址,而没有存放最后一个元素的地址,因此slist没有提供类似的push_back函数能够在容器尾部添加元素。push_front函数在链表首元素前面,插入一个新元素,使之成为首元素。
forward_list<类型名> | 链表名; 单向链表 |
---|---|
forward_list<int> f1; | 创建一个名为f1的单向链表,f1中的元素类型为int |
forward_list<int> f1(100,7); | f1中的元素为100个7 |
forward_list<int> f1{1,2,3,……}; | f1{1,2,3,……}; 元素为1,2,3,……的f1 |
forward_list<int> f2(f1); | 链表f2中包含和链表f1一样的元素 |
forward_list<int> f2=f1; | 链表f2中包含和链表f1一样的元素 |
3.3forward_list构造
构造函数 | 功能 |
---|---|
forward_list();(1) | 默认构造函数。构造拥有默认构造的分配器的空容器。 |
explicit forward_list(const Allocator& alloc);(2) | 构造拥有给定分配器 alloc 的空容器。 |
forward_list(size_type count, const T& value, const Allocator& alloc = Allocator());(3) (C++11 起) | 构造拥有 count 个有值 value 的元素的容器。 |
explicit forward_list(size_type count);(C++11 起)(4)(C++14 前) | |
explicit forward_list(size_type count, const Allocator& alloc = Allocator());(4)(C++14 起) | 构造拥有 count 个 默认插入的 T 实例的容器。不进行复制。 |
template< class InputIt > forward_list(InputIt first, InputIt last, const Allocator& alloc = Allocator());(5) (C++11 起) |
构造拥有范围 [first, last) 内容的容器。如果 InputIt 是整数类型,那么此构造函数拥有的效果同forward_list(static_cast<size_type>(first), static_cast<value_type>(last), a)。 (C++11 前)此重载只有在 InputIt 满足老式输入迭代器 (LegacyInputIterator) 时才会参与重载决议,以避免和重载 (3) 的歧义。(C++11 起) |
forward_list(const forward_list& other);(6) (C++11 起) | 复制构造函数。构造拥有 other 内容的容器。 如同通过调用 std::allocator_traits<allocator_type>::select_on_container_copy_construction( other.get_allocator()) 获得分配器。 |
forward_list(const forward_list& other, const Allocator& alloc);(7) (C++11 起) | 构造拥有 other 内容的容器,以 alloc 为分配器。 在进行类模板实参推导时,只会从首个实参推导模板形参 Allocator 。(C++23 起) |
forward_list(forward_list&& other);(8) (C++11 起) | 移动构造函数。用移动语义构造拥有 other 内容的容器。分配器通过属于 other 的分配器移动构造获得。 |
forward_list(forward_list&& other, const Allocator& alloc);(9) (C++11 起) | 有分配器扩展的移动构造函数。以 alloc 为新容器的分配器,从 other 移动内容;如果 alloc != other.get_allocator() ,那么它会导致逐元素移动。在进行类模板实参推导时,只会从首个实参推导模板形参 Allocator 。(C++23 起) |
forward_list(std::initializer_list |
构造拥有 initializer_list init 内容的容器。 |
参数
alloc | 用于此容器所有内存分配的分配器 |
---|---|
count | 容器的大小 |
value | 以之初始化容器元素的值 |
first, last | 复制元素的来源范围 |
other | 用作初始化容器元素来源的另一容器 |
init | 用作初始化元素来源的 initializer_list |
构造、复制与析构
函数 | 功能 |
---|---|
forward_list<Elem> c; | 默认构造函数;创建一个空forward_list |
forward_list<Elem> c(c2); | 复制构造函数;创建一个新的forward_list作为c2的副本(所有元素都被复制) |
forward_list<Elem> c = c2; | 复制构造函数;创建一个新的forward_list作为c2的副本(所有元素都被复制) |
forward_list<Elem> c(rv); | 移动构造函数;使用右值对象rv创建一个新forward_list |
forward_list<Elem> c = rv; | 移动构造函数;使用右值对象rv创建一个新forward_list |
forward_list<Elem> c(n) ; | 使用默认构造函数创建含有n个元素的forward_list |
forward_list<Elem> c(n,elem) ; | 创建一个forward_list,并使用n个elem进行初始化 |
forward_list<Elem> c(beg,end) ; | 创建一个forward_list,并使用beg到end范围内的值进行初始化 |
forward_list<Elem> c(initlist); | 创建一个forward_list,并使用初始化列表进行初始化 |
forward_list<Elem> c = initlist; | 创建一个forward_list,并使用初始化列表进行初始化 |
c.~forward_list() ; | 销毁所有元素并释放内存 |
3.4赋值
表达式 | 功能 |
---|---|
c = c2; | 将c2所有元素赋值给c |
c = rv; | 将右值对象rv的所有元素移动赋值给c |
c = initlist; | 使用初始化列表进行赋值 |
c.assign(initlist); | 使用初始化列表进行赋值 |
c.assign(n,elem); | 使用n个elem元素进行赋值 |
c.assign(beg,end); | 使用beg到end范围内的元素进行赋值 |
c1.swap(c2); | 交换c1和c2的数 |
swap(c1,c2) | 交换c1和c2的数 |
3.5非变动性操作
表达式 | 功能 |
---|---|
c.empty() | 判断容器是否为空 |
c.max_size() | 返回可容纳的元素最大数量 |
c1 == c2 | 判断c1与c2是否相等 |
c1 != c2 | 判断c1与c2是否不相等,等同于!(c1==c2) |
c1 < c2 | 判断c1是否小于c2 |
c1 > c2 | 判断c1是否大于c2 |
c1 <= c2 | 判断c1是否小于等于c2 |
c1 >= c2 | 判断c1是否大于等于c2 |
3.6迭代器相关函数
表达式 | 功能 |
---|---|
c.begin() | 返回一个双向迭代器,指向第一个元素 |
c.end() | 返回一个双向迭代器,指向最后一个元素 |
c.cbegin() | 返回一个双向常迭代器,指向第一个元素 |
c.cend() | 返回一个双向常迭代器,指向最后一个元素 |
c.before_begin() | 返回一个前向迭代器,指向第一个元素之前的位置 |
c.cbefore_begin() | 返回一个前向常迭代器,指向第一个元素之前的位置 |
3.7插入和移除元素
表达式 | 功能 |
---|---|
c.push_front(elem) | 在头部添加一个elem副本 |
c.pop_front() | 移除头部元素(但不回传) |
c.insert_after(pos,elem) | 在迭代器位置之后插入一个elem副本,并返回新元素的位置 |
c.insert_after(pos,n,elem) | 在迭代器位置之后插入n个elem副本,并返回第一个新元素的位置;若无新插入值,返回原位置 |
c.insert_after(pos,beg,end) | 在迭代器位置之后插入范围beg到end的所有元素的副本,并返回第一个新元素的位置;若无新插入值,返回原位置 |
c.insert_after(pos,initforward_list) | 在迭代器位置之后插入初始化列表的所有元素的副本,并返回第一个新元素的位置;若无新插入值,返回原位置 |
c.emplace_after(pos,args...) | 在迭代器位置之后插入一个使用args初始化的元素副本,并返回新元素的位置 |
c.emplace_front(args...) | 在头部添加一个使用args初始化的元素副本,无返回值 |
c.erase_after(pos) | 移除迭代器位置的元素,无返回值 |
c.erase_after(beg,end) | 移除beg到end范围内的所有元素,无返回值 |
c.remove(val) | 移除所有值为val的元素 |
c.remove_if(op) | 移除所有满足op条件的元素 |
c.resize(num) | 将元素数量设为num(如果size()增大,多出来的元素使用默认构造函数创建) |
c.resize(num,elem) | 将元素数量设为num(如果size()增大,多出来的元素都是elem的副本) |
c.clear() | 移除所以元素,清空容器 |
3.8特殊修改操作
表达式 | 功能 |
---|---|
c.unique() | 若存在相邻而数值相等的元素,移除重复元素 |
c.unique(op) | 若存在相邻而数值相等的元素,且满足op条件时,移除重复元素 |
c.splice_after(pos,c2) | 将c2内的所有元素转移到c1内pos所指的位置之后 |
c.splice_after(pos,c2,c2pos) | 将c2内c2pos之后的元素转移到c1内pos所指的位置之后 |
c.splice_after(pos,c2,c2beg,c2end) | 将c2内从c2beg到c2end区间内的所有元素转移到c1内pos所指的位置之后 |
c.sort() | 以operator<为准则,对所有元素排序 |
c.sort(op) | 以op为准则,对所有元素排序 |
c.merge(c2) | 假设c1和c2都包含已序元素,将c2的全部元素转移到c1.并保证合并后的forward_list仍为已序 |
c.merge(c2,op) | 假设c1和c2都包含op原则下已序元素,将c2的全部元素转移到c1.并保证合并后的forward_list在op原则下仍为已序 |
c.reverse() | 将所有元素反序 |