LIST容器
- list概述
- 列表
- 容器属性
- 例子
- list函数
- 构造函数
- 默认构造函数:
- 带有元素个数和元素初值的构造函数:
- 范围构造函数:
- 拷贝构造函数:
- 移动构造函数:
- 示例
- 赋值运算符重载
- 拷贝赋值操作符 (1):
- 移动赋值操作符 (2):
- 初始化列表赋值操作符 (3):
- 示例
- 注意事项
- 迭代器函数
- 插入和删除函数
- 清空和删除函数
- 自己实现的list容器案例
list概述
std::list
template < class T, class Alloc = allocator<T> > class list;
列表
列表是序列容器,允许在序列中的任何位置执行恒定时间插入和擦除操作,并在两个方向上进行迭代。
列表容器以双向链表的形式实现;双向链表可以将它们包含的每个元素存储在不同且不相关的存储位置。排序是通过与每个元素的关联在内部保持的,该关联链接到它前面的元素,以及与它后面的元素的链接。
它们与 forward_list 非常相似:主要区别在于forward_list对象是单链表,因此它们只能向前迭代
,以换取更小、更高效的性能。
与其他基本标准序列容器(数组、向量和 deque)相比,列表在插入、提取和移动已获得迭代器的容器内的任何位置的元素方面通常表现更好
,因此在大量使用这些元素的算法(如排序算法)中也是如此。
与这些其他序列容器相比,列表s 和 forward_lists 的主要缺点是它们无法通过其位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(如开始或结束)迭代到该位置,这需要它们之间的线性时间。它们还会消耗一些额外的内存,以保持与每个元素关联的链接信息(这可能是小型元素的大型列表的一个重要因素)。
容器属性
序列
序列容器中的元素按严格的线性顺序排序。各个元素通过它们在此序列中的位置进行访问。
双向链表
每个元素都保留有关如何定位下一个和上一个元素的信息,允许在特定元素(甚至整个范围)之前或之后进行恒定时间插入和擦除操作,但不能直接随机访问。
分配器感知
容器使用分配器对象来动态处理其存储需求。
模板参数
T
元素的类型
别名为成员类型 list::value_type。
分配
用于定义存储分配模型的分配器对象的类型。默认情况下,使用分配器类模板,该模板定义了最简单的内存分配模型,并且与值无关。
别名为成员类型 list::allocator_type。
例子
std::list 是 C++ 标准库中的双向链表(doubly linked list)容器,它提供了高效的插入和删除操作,但不支持随机访问元素。下面简要介绍一下 std::list 的底层实现机制和一个简单的示例。
底层实现机制
std::list 使用双向链表作为其底层数据结构,每个节点(node)包含两个指针:一个指向前一个节点,一个指向后一个节点。这种结构使得在任意位置插入和删除操作都很高效,因为只需要调整相邻节点的指针即可,不需要像数组那样移动大量元素。
双向链表的结构如下所示:
nullptr <-> Node1 <-> Node2 <-> … <-> NodeN <-> nullptr
其中 nullptr 表示空指针,用来表示链表的头尾。每个节点中会存储实际的数据,比如 int、char、struct 等。
示例
下面是一个简单的示例展示如何使用 std::list 容器:
#include <iostream>
#include <list>
int main() {
// 创建一个空的 std::list 容器
std::list<int> mylist;
// 向 list 中插入元素
mylist.push_back(1); // {1}
mylist.push_back(2); // {1, 2}
mylist.push_front(3); // {3, 1, 2}
mylist.push_back(4); // {3, 1, 2, 4}
// 使用迭代器遍历输出 list 中的元素
std::cout << "List elements: ";
for (auto it = mylist.begin(); it != mylist.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 在指定位置插入元素
auto it = ++mylist.begin(); // 获取第二个元素的迭代器
mylist.insert(it, 5); // 在第二个元素后面插入 5,{3, 5, 1, 2, 4}
// 删除元素
mylist.pop_front(); // 删除第一个元素,{5, 1, 2, 4}
mylist.pop_back(); // 删除最后一个元素,{5, 1, 2}
// 输出修改后的 list 元素
std::cout << "Modified List elements: ";
for (auto it = mylist.begin(); it != mylist.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
示例解释
1,首先,我们创建了一个 std::list 类型的空列表 mylist。
2,使用 push_back() 和 push_front() 方法向列表中添加元素。
3,使用迭代器 begin() 和 end() 遍历输出列表中的元素。
4,使用 insert() 在指定位置插入元素。
5,使用 pop_front() 和 pop_back() 方法删除第一个和最后一个元素。
6,最后,再次使用迭代器遍历输出修改后的列表元素。
这个例子展示了 std::list 容器的基本操作,包括插入、删除和遍历元素等,利用了其底层双向链表的特性,使得这些操作都能够高效地执行。
list函数
构造函数
std::list 的构造函数可以分为以下几类:
默认构造函数:
std::list();
创建一个空的双向链表。链表中不包含任何元素。
带有元素个数和元素初值的构造函数:
explicit std::list(size_type count, const T& value = T());
创建一个包含 count 个元素的链表,每个元素的值都是 value。例如:
std::list<int> mylist(5, 10); // 创建一个包含5个值为10的元素的链表
范围构造函数:
template<class InputIterator>
std::list(InputIterator first, InputIterator last);
从迭代器范围 [first, last) 中构造链表,将范围内的元素复制到新建的链表中。例如:
int arr[] = {1, 2, 3, 4, 5};
std::list<int> mylist(arr, arr + 5); // 从数组中复制元素到链表
拷贝构造函数:
std::list(const std::list& other);
使用另一个 std::list 对象 other 中的元素创建新的链表。例如:
std::list<int> original = {1, 2, 3};
std::list<int> copy = original; // 使用拷贝构造函数创建副本
移动构造函数:
std::list(std::list&& other) noexcept;
使用另一个 std::list 对象 other 中的元素创建新的链表,并接管 other 的资源。移动构造函数通常比拷贝构造函数更高效。例如:
std::list<int> source = {1, 2, 3};
std::list<int> dest = std::move(source); // 使用移动构造函数
示例
下面是一个综合示例,展示了 std::list 的不同构造函数的使用方式:
#include <iostream>
#include <list>
int main() {
// 默认构造函数创建空链表
std::list<int> empty_list;
// 使用带有元素个数和初值的构造函数
std::list<int> filled_list(5, 10); // 包含5个值为10的元素的链表
// 使用范围构造函数
int arr[] = {1, 2, 3, 4, 5};
std::list<int> range_list(arr, arr + 5); // 从数组中复制元素到链表
// 使用拷贝构造函数
std::list<int> original = {1, 2, 3};
std::list<int> copy = original; // 创建副本
// 使用移动构造函数
std::list<int> source = {4, 5, 6};
std::list<int> dest = std::move(source); // 移动构造函数
// 输出链表中的元素
std::cout << "Filled list elements:";
for (auto& elem : filled_list) {
std::cout << " " << elem;
}
std::cout << std::endl;
std::cout << "Range list elements:";
for (auto& elem : range_list) {
std::cout << " " << elem;
}
std::cout << std::endl;
std::cout << "Copy list elements:";
for (auto& elem : copy) {
std::cout << " " << elem;
}
std::cout << std::endl;
std::cout << "Moved list elements:";
for (auto& elem : dest) {
std::cout << " " << elem;
}
std::cout << std::endl;
return 0;
赋值运算符重载
拷贝赋值操作符 (1):
list& operator= (const list& x);
将另一个 std::list 对象 x 中的元素拷贝赋值给当前链表对象。例如:
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5};
list2 = list1; // 使用拷贝赋值操作符
// 现在 list2 的元素为 {1, 2, 3}
移动赋值操作符 (2):
list& operator= (list&& x);
将另一个 std::list 对象 x 中的元素移动赋值给当前链表对象。移动赋值操作符通常用于提高效率。例如:
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5};
list2 = std::move(list1); // 使用移动赋值操作符
// 现在 list2 的元素为 {1, 2, 3},list1 变成空链表
初始化列表赋值操作符 (3):
list& operator= (initializer_list<value_type> il);
使用初始化列表 il 中的元素来赋值给当前链表对象。例如:
std::list<int> list1 = {1, 2, 3};
list1 = {4, 5, 6}; // 使用初始化列表赋值操作符
// 现在 list1 的元素为 {4, 5, 6}
示例
下面是一个综合示例,演示了 std::list 中各种赋值操作符的使用:
#include <iostream>
#include <list>
int main() {
// 拷贝赋值操作符示例
std::list<int> list1 = {1, 2, 3};
std::list<int> list2 = {4, 5};
list2 = list1; // 拷贝赋值操作符
std::cout << "List2 after copy assignment:";
for (auto& elem : list2) {
std::cout << " " << elem;
}
std::cout << std::endl;
// 移动赋值操作符示例
std::list<int> list3 = {7, 8};
std::list<int> list4 = {9, 10};
list4 = std::move(list3); // 移动赋值操作符
std::cout << "List4 after move assignment:";
for (auto& elem : list4) {
std::cout << " " << elem;
}
std::cout << std::endl;
// 初始化列表赋值操作符示例
std::list<int> list5 = {11, 12, 13};
list5 = {14, 15, 16}; // 初始化列表赋值操作符
std::cout << "List5 after initializer list assignment:";
for (auto& elem : list5) {
std::cout << " " << elem;
}
std::cout << std::endl;
return 0;
}
注意事项
拷贝赋值操作符和移动赋值操作符的选择通常取决于右侧操作数的类型和实际需求。
初始化列表赋值操作符用于通过大括号 {} 直接赋值一组元素。
迭代器函数
1,begin() 和 end():
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
这些函数返回链表的起始迭代器和结束迭代器。对于非常量对象,begin() 返回指向第一个元素的迭代器,end() 返回指向最后一个元素后面位置的迭代器;对于常量对象,返回的是 const_iterator,用于只读访问链表的元素。
示例:
std::list<int> myList = {1, 2, 3, 4};
// 使用迭代器遍历链表
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
2,rbegin() 和 rend():
reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator rbegin() const;
const_reverse_iterator rend() const;
这些函数返回链表的反向迭代器,用于反向遍历链表元素。rbegin() 返回指向最后一个元素的反向迭代器,rend() 返回指向第一个元素前面位置的反向迭代器;对于常量对象,返回的是 const_reverse_iterator。
示例:
std::list<int> myList = {1, 2, 3, 4};
// 使用反向迭代器遍历链表
for (auto rit = myList.rbegin(); rit != myList.rend(); ++rit) {
std::cout << *rit << " ";
}
3,cbegin() 和 cend():
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
这些函数返回链表的常量迭代器,用于只读访问链表元素。cbegin() 返回指向第一个元素的常量迭代器,cend() 返回指向最后一个元素后面位置的常量迭代器。
示例:
std::list<int> myList = {1, 2, 3, 4};
// 使用常量迭代器遍历链表
for (auto cit = myList.cbegin(); cit != myList.cend(); ++cit) {
std::cout << *cit << " ";
}
4,crbegin() 和 crend():
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
这些函数返回链表的常量反向迭代器,用于只读反向访问链表元素。crbegin() 返回指向最后一个元素的常量反向迭代器,crend() 返回指向第一个元素前面位置的常量反向迭代器。
示例:
std::list<int> myList = {1, 2, 3, 4};
// 使用常量反向迭代器遍历链表
for (auto crit = myList.crbegin(); crit != myList.crend(); ++crit) {
std::cout << *crit << " ";
}
插入和删除函数
- push_front(const T& value)
在链表头部插入一个元素。
语法结构:
void push_front(const T& value);
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
// 在链表头部插入元素
myList.push_front(0); // 现在链表为 {0, 1, 2, 3}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- push_back(const T& value)
在链表尾部插入一个元素。
语法结构:
void push_back(const T& value);
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
// 在链表尾部插入元素
myList.push_back(4); // 现在链表为 {1, 2, 3, 4}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- pop_front()
删除链表头部的元素。
语法结构:
void pop_front();
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
// 删除链表头部的元素
myList.pop_front(); // 现在链表为 {2, 3}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- pop_back()
删除链表尾部的元素。
语法结构:
void pop_back();
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
// 删除链表尾部的元素
myList.pop_back(); // 现在链表为 {1, 2}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- insert(const_iterator pos, const T& value)
在迭代器 pos 所指位置插入元素 value。
语法结构:
iterator insert(const_iterator pos, const T& value);
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
auto it = myList.begin();
++it; // 移动到第二个位置
// 在指定位置插入元素
myList.insert(it, 4); // 现在链表为 {1, 4, 2, 3}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- erase(const_iterator pos)
删除迭代器 pos 所指位置的元素。
语法结构:
iterator erase(const_iterator pos);
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 3};
auto it = myList.begin();
++it; // 移动到第二个位置
// 删除指定位置的元素
myList.erase(it); // 现在链表为 {1, 3}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
清空和删除函数
- clear()
清空链表,移除所有元素。
语法结构:
void clear();
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 2, 3};
// 清空链表
myList.clear(); // 现在链表为空
std::cout << "List size after clearing: " << myList.size() << std::endl;
return 0;
}
- remove(const T& value)
移除链表中所有等于 value 的元素。
语法结构:
void remove(const T& value);
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 2, 3};
// 移除所有值为 2 的元素
myList.remove(2); // 现在链表为 {1, 3}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- remove_if(UnaryPredicate p)
根据条件 p 移除符合条件的元素。
语法结构:
template<class UnaryPredicate>
void remove_if(UnaryPredicate p);
示例用法:
#include <iostream>
#include <list>
bool isEven(int num) {
return num % 2 == 0;
}
int main() {
std::list<int> myList = {1, 2, 3, 4, 5};
// 移除所有偶数元素
myList.remove_if(isEven); // 现在链表为 {1, 3, 5}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
- unique()
移除链表中连续重复的元素,只保留一个。
语法结构:
void unique();
示例用法:
#include <iostream>
#include <list>
int main() {
std::list<int> myList = {1, 2, 2, 3, 3, 3, 4};
// 移除连续重复的元素
myList.unique(); // 现在链表为 {1, 2, 3, 4}
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
自己实现的list容器案例
namespace mylist
{
// List的节点类
template<class T>
struct ListNode
{
ListNode(const T& val = T())
: _pPre(nullptr), _pNext(nullptr), _val(val) {}
ListNode<T>* _pPre;
ListNode<T>* _pNext;
T _val;
};
// List的迭代器类
template<class T, class Ref, class Ptr>
class ListIterator
{
typedef ListNode<T>* PNode;
typedef ListIterator<T, Ref, Ptr> Self;
public:
ListIterator(PNode pNode = nullptr)
: _pNode(pNode) {}
ListIterator(const Self& l)
: _pNode(l._pNode) {}
PNode Ptr() const
{
return _pNode;
}
T& operator*() const
{
return _pNode->_val; // 直接解引用 _pNode
}
T* operator->() const
{
return &_pNode->_val; // 直接返回 _pNode->_val 的地址
}
Self& operator++()
{
_pNode = _pNode->_pNext;
return *this;
}
Self operator++(int)
{
Self tem(_pNode);
_pNode = _pNode->_pNext;
return tem;
}
Self& operator--()
{
_pNode = _pNode->_pPre;
return *this;
}
Self operator--(int)
{
Self tem(_pNode);
_pNode = _pNode->_pPre;
return tem;
}
bool operator!=(const Self& l) const
{
return _pNode != l._pNode;
}
bool operator==(const Self& l) const
{
return _pNode == l._pNode;
}
private:
PNode _pNode;
};
// list类
template<class T>
class list
{
typedef ListNode<T> Node;
typedef Node* PNode;
public:
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T&> const_iterator;
public:
list()
{
CreateHead();
length = 0;
}
list(int n, const T& value = T())
{
CreateHead();
length = n;
while (n--)
{
PNode tem = new Node(value); //创建 Node 对象非 PNode
_pHead->_pPre->_pNext = tem;
tem->_pPre = _pHead->_pPre;
tem->_pNext = _pHead;
_pHead->_pPre = tem;
}
}
template <class Iterator>
list(Iterator first, Iterator last)
{
CreateHead();
while (first != last)
{
PNode tem = new Node(*first); //创建 Node 对象非 PNode
first++;
_pHead->_pPre->_pNext = tem;
tem->_pPre = _pHead->_pPre;
tem->_pNext = _pHead;
_pHead->_pPre = tem;
length++;
}
}
list(const list<T>& l)
{
CreateHead();
PNode sta = l._pHead->_pNext;
PNode end = l._pHead;
while (sta != end)
{
PNode tem = new Node(sta->_val); // 创建 Node 对象而非 PNode
sta = sta->_pNext;
_pHead->_pPre->_pNext = tem;
tem->_pPre = _pHead->_pPre;
tem->_pNext = _pHead;
_pHead->_pPre = tem;
length++;
}
}
list<T>& operator=(const list<T>& l)
{
clear(); // 使用 clear() 而不是 ~list() 来清空内容
CreateHead();
PNode sta = l._pHead->_pNext;
PNode end = l._pHead;
while (sta != end)
{
PNode tem = new Node(sta->_val); // 创建 Node 对象而非 PNode
sta = sta->_pNext;
_pHead->_pPre->_pNext = tem;
tem->_pPre = _pHead->_pPre;
tem->_pNext = _pHead;
_pHead->_pPre = tem;
length++;
}
return *this;
}
~list()
{
clear(); // 使用 clear() 来删除所有节点
delete _pHead; //删除链表头节点
}
///
// List Iterator
iterator begin()
{
return iterator(_pHead->_pNext);
}
iterator end()
{
return iterator(_pHead);
}
const_iterator begin() const
{
return const_iterator(_pHead->_pNext);
}
const_iterator end() const
{
return const_iterator(_pHead);
}
///
// List Capacity
size_t size() const
{
return length;
}
bool empty() const
{
return length == 0;
}
// List Access
T& front()
{
assert(!empty()); // assert(!empty())
return _pHead->_pNext->_val;
}
const T& front() const
{
assert(!empty()); // assert(!empty())
return _pHead->_pNext->_val;
}
T& back()
{
assert(!empty()); // assert(!empty())
return _pHead->_pPre->_val;
}
const T& back() const
{
assert(!empty()); // 修改:assert(!empty())
return _pHead->_pPre->_val;
}
// List Modify
void push_back(const T& val) { insert(end(), val); }
void pop_back() { erase(--end()); }
void push_front(const T& val) { insert(begin(), val); }
void pop_front() { erase(begin()); }
iterator insert(iterator pos, const T& val)
{
assert(pos != end());
PNode cur = pos.Ptr();
PNode tem = new Node(val);
tem->_pPre = cur->_pPre; // 修正插入节点的前驱指针
tem->_pNext = cur;
cur->_pPre->_pNext = tem;
cur->_pPre = tem;
length++;
return iterator(tem);
}
iterator erase(iterator pos)
{
PNode cur = pos.Ptr();
cur->_pPre->_pNext = cur->_pNext;
cur->_pNext->_pPre = cur->_pPre;
iterator next(cur->_pNext);
delete cur;
length--;
return next;
}
void clear()
{
PNode cur = _pHead->_pNext;
while (cur != _pHead)
{
PNode next = cur->_pNext;
delete cur;
cur = next;
}
_pHead->_pNext = _pHead;
_pHead->_pPre = _pHead;
length = 0;
}
void swap(list<T>& l)
{
std::swap(_pHead, l._pHead);
std::swap(length, l.length);
}
private:
void CreateHead()
{
_pHead = new Node;
_pHead->_pNext = _pHead;
_pHead->_pPre = _pHead;
}
PNode _pHead;
size_t length;
};
};