SLT顺序容器
在C++的STL中,顺序容器是按顺序存储数据的容器类,用于存储和操作数据集合。顺序容器主要包括 vector、deque、list 和 forward_list,它们各自有不同的实现方式和应用场景。下面详细介绍每种顺序容器的特点、常用操作及其应用场景。
vector:动态数组
vector
是一种动态数组,可以动态调整大小,同时支持快速的随机访问。
特点
- 连续存储:
vector
内部数据存储在连续的内存中,因此支持快速的随机访问(O(1))。 - 动态扩展:当添加新元素超过当前容量时,
vector
会自动分配更大的内存块,将数据复制过去。通常,新容量是原来的 1.5 倍或 2 倍,具体取决于实现。 - 自动管理内存:当超出容量时,
vector
会自动分配更多空间;当vector
销毁时,内存会自动释放。 - 迭代器失效:当
vector
扩容或元素被删除时,原有的迭代器可能失效,因为底层内存块可能会更改。
常用操作:
push_back()
:在末尾添加一个元素。
pop_back()
:删除末尾的一个元素。
resize()
:调整 vector 的大小。
at()
:访问指定位置的元素(带边界检查)。
operator[]
:访问指定位置的元素(不带边界检查)。
适用场景:
适用于需要快速随机访问和在末尾插入删除的场景。
不适合频繁在中间位置进行插入或删除的情况,因为这会导致大量元素的移动。
成员函数详情
以下是 std::vector
常用函数的原型说明,包括构造函数、元素访问、容量管理、修改操作等。
- 构造函数
std::vector
提供了多种构造方式,可以根据不同的需求创建 vector
对象。
//默认构造函数
std::vector<T> vec;
//创建一个空的 `vector`,不包含任何元素。
//指定大小的构造函数
std::vector<T> vec(size_t count);
//创建一个包含 count 个默认初始化元素的 vector。
//指定大小和初始值的构造函数
std::vector<T> vec(size_t count, const T& value);
//创建一个包含 count 个 value 值的元素的 vector。
//拷贝构造函数
std::vector<T> vec(const std::vector<T>& other);
//复制构造函数,用 other 中的元素创建一个新的 vector。
//移动构造函数
std::vector<T> vec(std::vector<T>&& other);
//使用 other 的内容创建新 vector,并避免拷贝,提高效率。
//初始化列表构造函数
std::vector<T> vec(std::initializer_list<T> init);
//使用初始化列表创建 vector,如 {1, 2, 3}。
- 元素访问
//operator[]:访问指定位置的元素,不进行边界检查。
T& operator[](size_t pos);
const T& operator[](size_t pos) const;
//at:访问指定位置的元素,带边界检查,超出范围时抛出 std::out_of_range 异常。
T& at(size_t pos);
const T& at(size_t pos) const;
//front:返回 vector 中第一个元素的引用。
T& front();
const T& front() const;
//back:返回 vector 中最后一个元素的引用。
T& back();
const T& back() const;
//data:返回指向 vector 底层数组的指针。
T* data();
const T* data() const;
- 迭代器
//begin:返回指向第一个元素的迭代器。
iterator begin();
const_iterator begin() const;
//end:返回指向最后一个元素之后的位置的迭代器。
iterator end();
const_iterator end() const;
//rbegin 和 rend:反向迭代器,适用于反向遍历。
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
//注意end()返回的迭代器指向容器末尾元素之后的位置(超尾) rend()返回的迭代器指向容器第一个元素之前的位置
- 容量管理
//empty:检查 vector 是否为空。
bool empty() const;
//size:返回 vector 中元素的数量。
size_t size() const;
//max_size:返回 vector 可存储的最大元素数量。
size_t max_size() const;
//reserve:预留存储空间,至少容纳指定数量的元素,以避免频繁的内存分配。
void reserve(size_t new_cap);
//capacity:返回当前分配的存储空间,可以容纳的最大元素数。
size_t capacity() const;
//shrink_to_fit:请求减少内存使用,将多余的容量释放。
void shrink_to_fit();
- 修改操作
//clear:清空 vector 中的所有元素。
void clear();
//insert:在指定位置插入元素或多个元素。
iterator insert(const_iterator pos, const T& value);
iterator insert(const_iterator pos, T&& value);
iterator insert(const_iterator pos, size_t count, const T& value);
template <class InputIt>
iterator insert(const_iterator pos, InputIt first, InputIt last);
iterator insert(const_iterator pos, std::initializer_list<T> ilist);
//emplace:在指定位置直接构造元素,避免拷贝或移动。
iterator emplace(const_iterator pos, Args&&... args);
//push_back:在末尾添加一个元素。
void push_back(const T& value);
void push_back(T&& value);
//emplace_back:在末尾直接构造一个元素。
template <class... Args>
void emplace_back(Args&&... args);
//pop_back:移除末尾的元素。
void pop_back();
//erase:删除指定位置或区间的元素。
iterator erase(const_iterator pos);
iterator erase(const_iterator first, const_iterator last);
//resize:调整 vector 的大小,如果新大小小于当前大小,则删除末尾元素;如果大于当前大小,则添加默认值或指定值的元素。
void resize(size_t count);
void resize(size_t count, const T& value);
//swap:交换两个 vector 的内容。
void swap(std::vector<T>& other);
- 赋值操作
//operator=:将 vector 赋值为另一个 vector 或初始化列表。
vector& operator=(const vector& other);
vector& operator=(vector&& other) noexcept;
vector& operator=(std::initializer_list<T> ilist);
//assign:将 vector 的内容替换为指定数量的元素、来自迭代器区间的元素,或来自初始化列表的元素。
void assign(size_t count, const T& value);
template <class InputIt>
void assign(InputIt first, InputIt last);
void assign(std::initializer_list<T> ilist);
deque:双端队列
deque
是一种双端队列,支持在两端进行高效的插入和删除操作。
特点
- 双端队列:
deque
支持在头部和尾部进行高效的插入和删除操作,适合实现双向队列、双向缓存等结构。 - 分块存储:
deque
将数据分块存储在不同的内存块中,方便在头尾插入数据而不移动整个容器。 - 快速访问:虽然不像
vector
那样提供严格连续的内存,但deque
仍支持常数时间(O(1))的随机访问,效率接近vector
。 - 迭代器的稳定性:
deque
的迭代器在头部和尾部插入和删除操作时不会失效(除非扩容),但在中间插入和删除时会导致迭代器失效。
常用操作
push_front()
:在头部插入一个元素。
pop_front()
:删除头部的元素。
push_back()
:在尾部插入一个元素。
pop_back()
:删除尾部的元素。
at()
和 operator[]
:访问指定位置的元素。
适用场景
适合需要在两端频繁插入和删除的场景,如双向队列、滑动窗口等应用。
适用于需要在头尾两端进行操作并支持随机访问的情况。
成员函数详情
- 构造函数
//std::deque 提供了多种构造方式,用于创建不同初始化状态的 deque 对象。
//默认构造函数
//创建一个空的 deque。
std::deque<T> deq;
//指定大小的构造函数
//创建一个包含 count 个默认初始化元素的 deque。
std::deque<T> deq(size_t count);
//指定大小和初始值的构造函数
//创建一个包含 count 个 value 值的元素的 deque。
std::deque<T> deq(size_t count, const T& value);
//拷贝构造函数
//复制构造函数,用 other 中的元素创建一个新的 deque。
std::deque<T> deq(const std::deque<T>& other);
//移动构造函数
//使用 other 的内容创建新 deque,并避免拷贝,提高效率。
std::deque<T> deq(std::deque<T>&& other);
//初始化列表构造函数
//使用初始化列表创建 deque,如 {1, 2, 3}。
std::deque<T> deq(std::initializer_list<T> init);
- 元素访问
//operator[]:访问指定位置的元素,不进行边界检查。
T& operator[](size_t pos);
const T& operator[](size_t pos) const;
//at:访问指定位置的元素,带边界检查,超出范围时抛出 std::out_of_range 异常。
T& at(size_t pos);
const T& at(size_t pos) const;
//front:返回 deque 中第一个元素的引用。
T& front();
const T& front() const;
//back:返回 deque 中最后一个元素的引用。
T& back();
const T& back() const;
- 迭代器
//begin:返回指向第一个元素的迭代器。
iterator begin();
const_iterator begin() const;
//end:返回指向最后一个元素之后的位置的迭代器。
iterator end();
const_iterator end() const;
//rbegin 和 rend:反向迭代器,适用于反向遍历。
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
- 容量管理
//empty:检查 deque 是否为空。
bool empty() const;
//size:返回 deque 中元素的数量。
size_t size() const;
//max_size:返回 deque 可存储的最大元素数量。
size_t max_size() const;
//shrink_to_fit:请求减少内存使用,将多余的容量释放。
void shrink_to_fit();
- 修改操作
//clear:清空 deque 中的所有元素。
void clear();
//insert:在指定位置插入元素或多个元素。
iterator insert(const_iterator pos, const T& value);
iterator insert(const_iterator pos, T&& value);
iterator insert(const_iterator pos, size_t count, const T& value);
template <class InputIt>
iterator insert(const_iterator pos, InputIt first, InputIt last);
iterator insert(const_iterator pos, std::initializer_list<T> ilist);
//emplace:在指定位置直接构造元素,避免拷贝或移动。
iterator emplace(const_iterator pos, Args&&... args);
//push_back:在末尾添加一个元素。
void push_back(const T& value);
void push_back(T&& value);
//emplace_back:在末尾直接构造一个元素。
template <class... Args>
void emplace_back(Args&&... args);
//push_front:在头部插入一个元素。
void push_front(const T& value);
void push_front(T&& value);
//emplace_front:在头部直接构造一个元素。
template <class... Args>
void emplace_front(Args&&... args);
//pop_back:删除末尾的元素。
void pop_back();
//pop_front:删除头部的元素。
void pop_front();
//erase:删除指定位置或区间的元素。
iterator erase(const_iterator pos);
iterator erase(const_iterator first, const_iterator last);
//resize:调整 deque 的大小,如果新大小小于当前大小,则删除末尾元素;如果大于当前大小,则添加默认值或指定值的元素。
void resize(size_t count);
void resize(size_t count, const T& value);
//swap:交换两个 deque 的内容。
void swap(std::deque<T>& other);
- 赋值操作
//operator=:将 deque 赋值为另一个 deque 或初始化列表。
deque& operator=(const deque& other);
deque& operator=(deque&& other) noexcept;
deque& operator=(std::initializer_list<T> ilist);
//assign:将 deque 的内容替换为指定数量的元素、来自迭代器区间的元素,或来自初始化列表的元素。
void assign(size_t count, const T& value);
template <class InputIt>
void assign(InputIt first, InputIt last);
void assign(std::initializer_list<T> ilist);
list:双向链表
list 是一种双向链表,元素不连续存储,支持高效的插入和删除操作。
特点
- 双向链表:
std::list
是一个双向链表,每个节点包含指向前后节点的指针。 - 高效插入和删除:在任意位置进行插入或删除的时间复杂度为常数时间 O(1),适合需要频繁插入、删除的场景。
- 迭代器稳定性:由于不使用连续内存,
list
中的插入或删除操作不会使其他迭代器失效,只有删除操作会使相关迭代器失效。 - 不支持随机访问:
list
不支持使用 [] 或 at 访问特定位置的元素,访问指定位置的元素需要顺序遍历,时间复杂度为 O(n)。
常用操作
push_front()
和 push_back()
:在头部或尾部插入元素。
pop_front()
和 pop_back()
:删除头部或尾部的元素。
insert()
:在指定位置插入元素。
erase()
:删除指定位置的元素。
适用场景
适合频繁在任意位置插入和删除元素,但不需要随机访问的场景。
适用于实现队列、栈、双向链表等数据结构。
成员函数详情
- 构造函数
//默认构造函数
//创建一个空的 list。
std::list<T> lst;
//指定大小的构造函数
//创建一个包含 count 个默认初始化元素的 list。
std::list<T> lst(size_t count);
//指定大小和初始值的构造函数
//创建一个包含 count 个 value 值的元素的 list。
std::list<T> lst(size_t count, const T& value);
//拷贝构造函数
//复制构造函数,用 other 中的元素创建一个新的 list。
std::list<T> lst(const std::list<T>& other);
//移动构造函数
//使用 other 的内容创建新 list,并避免拷贝,提高效率。
std::list<T> lst(std::list<T>&& other);
//初始化列表构造函数
//使用初始化列表创建 list,如 {1, 2, 3}。
std::list<T> lst(std::initializer_list<T> init);
- 元素访问
//front:返回 list 中第一个元素的引用。
T& front();
const T& front() const;
//back:返回 list 中最后一个元素的引用。
T& back();
const T& back() const;
- 迭代器
//begin:返回指向第一个元素的迭代器。
iterator begin();
const_iterator begin() const;
//end:返回指向最后一个元素之后的位置的迭代器。
iterator end();
const_iterator end() const;
//rbegin 和 rend:反向迭代器,适用于反向遍历。
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
- 容量管理
//empty:检查 list 是否为空。
bool empty() const;
//size:返回 list 中元素的数量。
size_t size() const;
//max_size:返回 list 可存储的最大元素数量。
size_t max_size() const;
- 修改操作
//clear:清空 list 中的所有元素。
void clear();
//insert:在指定位置插入元素或多个元素。
iterator insert(const_iterator pos, const T& value);
iterator insert(const_iterator pos, T&& value);
iterator insert(const_iterator pos, size_t count, const T& value);
template <class InputIt>
iterator insert(const_iterator pos, InputIt first, InputIt last);
iterator insert(const_iterator pos, std::initializer_list<T> ilist);
//emplace:在指定位置直接构造元素,避免拷贝或移动。
iterator emplace(const_iterator pos, Args&&... args);
//push_back:在末尾添加一个元素。
void push_back(const T& value);
void push_back(T&& value);
//emplace_back:在末尾直接构造一个元素。
template <class... Args>
void emplace_back(Args&&... args);
//push_front:在头部插入一个元素。
void push_front(const T& value);
void push_front(T&& value);
//emplace_front:在头部直接构造一个元素。
template <class... Args>
void emplace_front(Args&&... args);
//pop_back:删除末尾的元素。
void pop_back();
//pop_front:删除头部的元素。
void pop_front();
//erase:删除指定位置或区间的元素。
iterator erase(const_iterator pos);
iterator erase(const_iterator first, const_iterator last);
//resize:调整 list 的大小,如果新大小小于当前大小,则删除末尾元素;如果大于当前大小,则添加默认值或指定值的元素。
void resize(size_t count);
void resize(size_t count, const T& value);
//swap:交换两个 list 的内容。
void swap(std::list<T>& other);
- 赋值操作
//operator=:将 list 赋值为另一个 list 或初始化列表。
list& operator=(const list& other);
list& operator=(list&& other) noexcept;
list& operator=(std::initializer_list<T> ilist);
//assign:将 list 的内容替换为指定数量的元素、来自迭代器区间的元素,或来自初始化列表的元素。
void assign(size_t count, const T& value);
template <class InputIt>
void assign(InputIt first, InputIt last);
void assign(std::initializer_list<T> ilist);
- 专有操作
std::list 提供了一些特有的操作,主要用于链表的合并、拆分和排序:
//merge:将另一个已排序的 list 合并到当前 list,并保持顺序。
void merge(list& other);
template <class Compare>
void merge(list& other, Compare comp);
//splice:将一个 list 的元素插入到另一个 list 中的指定位置。
void splice(const_iterator pos, list& other);
void splice(const_iterator pos, list& other, const_iterator it);
void splice(const_iterator pos, list& other, const_iterator first, const_iterator last);
//remove:移除所有等于指定值的元素。
void remove(const T& value);
//remove_if:移除满足特定条件的所有元素。
template <class UnaryPredicate>
void remove_if(UnaryPredicate p);
//reverse:反转 list 中所有元素的顺序。
void reverse();
//unique:移除连续的重复元素,仅保留一个。
void unique();
template <class BinaryPredicate>
void unique(BinaryPredicate p);
//sort:对 list 中的元素进行排序。
void sort();
template <class Compare>
void sort(Compare comp);
forward_list:单向链表
forward_list
是一种单向链表,与 list
类似,但只支持单向遍历,只包含指向下一个节点的指针。其设计目标是提供比 std::list
更小的内存开销和更高的性能,适用于需要简单链表结构且只需从头到尾单向遍历的场景。
特点
- 单向链表:每个节点只包含一个指向下一个节点的指针,不包含指向前一个节点的指针。
- 轻量级:因为只有一个方向的指针,相比
std::list
,内存开销更小。 - 高效的头部插入和删除:在链表头部插入或删除操作的时间复杂度为 O(1),适合实现简单的队列或栈。
- 不支持双向遍历和随机访问:只能从头到尾单向遍历链表,不支持 [] 或 at 进行随机访问。
常用操作
push_front()
:在头部插入元素。
pop_front()
:删除头部的元素。
insert_after()
:在指定位置后插入元素。
erase_after()
:删除指定位置后的元素。
适用场景
适合内存较为紧张、对链表大小没有严格需求的场景。
适用于实现单链表,或只需要单向遍历的数据结构,如简单的队列。
成员函数详情
- 构造函数
//默认构造函数
//创建一个空的 forward_list。
std::forward_list<T> flst;
//指定大小的构造函数
//创建一个包含 count 个默认初始化元素的 forward_list。
std::forward_list<T> flst(size_t count);
//指定大小和初始值的构造函数
//创建一个包含 count 个 value 值的元素的 forward_list。
std::forward_list<T> flst(size_t count, const T& value);
//拷贝构造函数
//用 other 中的元素创建一个新的 forward_list。
std::forward_list<T> flst(const std::forward_list<T>& other);
//移动构造函数
//使用 other 的内容创建新 forward_list,避免不必要的拷贝。
std::forward_list<T> flst(std::forward_list<T>&& other);
//初始化列表构造函数
//使用初始化列表创建 forward_list,如 {1, 2, 3}。
std::forward_list<T> flst(std::initializer_list<T> init);
- 元素访问
std::forward_list 不提供随机访问,因此只有一个头部访问函数。
//front:返回 forward_list 中第一个元素的引用。
T& front();
const T& front() const;
- 迭代器
//begin:返回指向第一个元素的迭代器。
iterator begin();
const_iterator begin() const;
//end:返回指向最后一个元素之后的位置的迭代器。
iterator end();
const_iterator end() const;
- 容量管理
//empty:检查 forward_list 是否为空。
bool empty() const;
- 修改操作
std::forward_list
的修改操作比 std::list
简单,因为它是单向链表,没有提供像 emplace_back 或 push_back 的操作。
//clear:清空 forward_list 中的所有元素。
void clear();
//insert_after:在指定位置后插入元素或多个元素。
iterator insert_after(const_iterator pos, const T& value);
iterator insert_after(const_iterator pos, T&& value);
iterator insert_after(const_iterator pos, size_t count, const T& value);
template <class InputIt>
iterator insert_after(const_iterator pos, InputIt first, InputIt last);
iterator insert_after(const_iterator pos, std::initializer_list<T> ilist);
//emplace_after:在指定位置后直接构造元素。
template <class... Args>
iterator emplace_after(const_iterator pos, Args&&... args);
//push_front:在头部插入一个元素。
void push_front(const T& value);
void push_front(T&& value);
//emplace_front:在头部直接构造一个元素。
template <class... Args>
void emplace_front(Args&&... args);
//pop_front:删除头部的元素
void pop_front();
//erase_after:删除指定位置后的元素或区间。
iterator erase_after(const_iterator pos);
iterator erase_after(const_iterator first, const_iterator last);
//resize:调整 forward_list 的大小,如果新大小小于当前大小,则删除多余的元素;如果大于当前大小,则添加默认值或指定值的元素。
void resize(size_t count);
void resize(size_t count, const T& value);
//swap:交换两个 forward_list 的内容。
void swap(forward_list<T>& other);
- 赋值操作
//operator=:将 forward_list 赋值为另一个 forward_list 或初始化列表。
forward_list& operator=(const forward_list& other);
forward_list& operator=(forward_list&& other) noexcept;
forward_list& operator=(std::initializer_list<T> ilist);
//assign:将 forward_list 的内容替换为指定数量的元素、来自迭代器区间的元素,或来自初始化列表的元素。
void assign(size_t count, const T& value);
template <class InputIt>
void assign(InputIt first, InputIt last);
void assign(std::initializer_list<T> ilist);
- 专有操作
由于 std::forward_list 的单向链表特性,它也支持一些常用的链表操作:
//merge:将另一个已排序的 forward_list 合并到当前 forward_list,并保持顺序。
void merge(forward_list& other);
template <class Compare>
void merge(forward_list& other, Compare comp);
//splice_after:将一个 forward_list 的元素插入到另一个 forward_list 中的指定位置之后。
void splice_after(const_iterator pos, forward_list& other);
void splice_after(const_iterator pos, forward_list& other, const_iterator it);
void splice_after(const_iterator pos, forward_list& other, const_iterator first, const_iterator last);
//remove:移除所有等于指定值的元素。
void remove(const T& value);
//remove_if:移除满足特定条件的所有元素。
template <class UnaryPredicate>
void remove_if(UnaryPredicate p);
//reverse:反转 forward_list 中所有元素的顺序。
void reverse();
//unique:移除连续的重复元素,仅保留一个。
void unique();
template <class BinaryPredicate>
void unique(BinaryPredicate p);
//sort:对 forward_list 中的元素进行排序。
void sort();
template <class Compare>
void sort(Compare comp);
专有函数补充说明
看到这边可能有些朋友会有疑惑,怎么list和forward_list有专有操作,vector和deque没有呢?
std::vector
和 std::deque
的设计目标是提供快速的随机访问和高效的序列操作,因此它们的接口更关注基础的顺序操作(如插入、删除、访问等),而没有添加复杂的专有操作。相比之下,std::list 和 std::forward_list 基于链表结构,这使得它们天然支持一些高级操作,如合并、排序、去重等。这些操作在链表结构上可以在不移动大量数据的情况下高效实现,因此它们被设计为专有操作。
以下是 std::list 和 std::forward_list 具有这些专有操作的原因,以及 std::vector 和 std::deque 不需要这些操作的原因:
- std::list 和 std::forward_list 的专有操作
merge:将两个已排序的链表合并成一个,且链表的合并操作只需调整指针,而无需移动大量数据,因此链表合并非常高效。这在链表结构上可以实现 O(1) 的操作复杂度。
splice:将一个链表中的元素移动到另一个链表中,只需改变指针指向,而不必复制或移动数据,这对于链表来说是一项很自然的操作,能以 O(1) 的复杂度完成。
remove 和 remove_if:删除符合条件的节点只需调整链表指针,因此链表结构的删除操作可以高效完成,不会涉及到大量元素的移动。
unique:去除连续重复的元素,在链表结构中通过遍历实现,不需要进行大量的元素重排。
sort:链表可以通过交换指针高效排序,而无需像数组那样移动大量数据。
这些操作利用了链表节点的指针特性,使得复杂操作可以高效地实现。
- std::vector 和 std::deque 的设计侧重点
快速随机访问:std::vector 和 std::deque 设计上重视连续内存的快速访问特性。由于其内存是连续的,直接支持 O(1) 的随机访问,这是链表无法做到的。
简单顺序操作:std::vector 和 std::deque 的主要操作集中在顺序插入和删除,特别是 std::vector 优化了尾部操作,而 std::deque 额外支持高效的头尾操作。
高级操作不适用:由于内存是连续存储的,执行类似 merge、splice 的操作需要移动大量数据,这样的操作会非常低效;而 sort、remove 等操作,std::vector 和 std::deque 可以通过算法库中的通用函数(如 std::sort、std::remove)来完成。这样的算法函数在这些连续容器上效率很高,因为它们能利用直接访问的优势。
总结
链表容器(list、forward_list):使用指针操作节点,高效支持高级操作,因此提供了专有的成员函数。
连续内存容器(vector、deque):基于连续内存,更适合直接访问和基础顺序操作,专有操作反而会影响效率。因此这些容器专注于基础的增删查操作,不提供链表专有的高级操作。
可以通过标准算法来对 vector 和 deque 执行类似的操作。例如:
排序:
std::vector<int> vec = {4, 2, 3, 1};
std::sort(vec.begin(), vec.end());
去重:
std::vector<int> vec = {1, 2, 2, 3, 4, 4};
std::sort(vec.begin(), vec.end()); // 先排序
auto last = std::unique(vec.begin(), vec.end());
vec.erase(last, vec.end()); // 删除重复元素
合并:
std::vector<int> vec1 = {1, 3, 5};
std::vector<int> vec2 = {2, 4, 6};
std::vector<int> result;
std::merge(vec1.begin(), vec1.end(), vec2.begin(), vec2.end(), std::back_inserter(result));
std::vector
和 std::deque
的功能更偏向于高效的内存管理和随机访问,使用标准算法库可以很好地完成类似 list 的操作,但不需要专门的成员函数来实现。
list和forward_list为什么不推荐使用标准算法?
标准算法库中的大部分算法(如 std::sort、std::merge 等)假设数据结构支持 随机访问迭代器。这在连续存储的容器(如 std::vector 和 std::deque)上可以实现高效的操作,但 std::list 和 std::forward_list 只提供双向迭代器或单向迭代器,无法满足这些算法的随机访问要求。因此,尝试在链表上使用标准算法可能会导致编译错误,或效率极低。
顺序容器的选择和比较
选择顺序容器时,可以根据需求来选择:
std::vector
:适合需要随机访问和尾部操作较多的场景,如动态数组。
std::deque
:适合双端操作和适度随机访问的场景,如双端队列。
std::list
:适合在任意位置频繁插入、删除的场景,典型的双向链表。
std::forward_list
:适合单向遍历、头部插入删除频繁、资源受限的场景。
容器类型 | 底层结构 | 内存分配 | 随机访问 | 插入/删除效率 | 适用场景 |
---|---|---|---|---|---|
std::vector | 动态数组 | 连续内存 | 快速 O(1) | 插尾部 O(1),头/中部 O(n) | 随机访问频繁、尾部插入删除 |
std::deque | 分段连续的双端队列 | 分块内存 | 较快 O(1) | 头尾 O(1) | 双端插入删除,双端队列 |
std::list | 双向链表 | 非连续分散存储 | 慢 O(n) | 任意位置 O(1) | 插入删除频繁,无需随机访问 |
std::forward_list | 单向链表 | 非连续分散存储 | 慢 O(n) | 头部 O(1),尾部 O(n) | 头部操作频繁,单向遍历 |