一、list 类介绍
- list 底层使用了带头结点的双向循环链表,因此其特点:
- 在任意位置的插入和删除效率较高,其他序列式容器相比
- 不支持元素的随机访问
- 查找元素的时间复杂度O(n)
二、list 常用接口
#include <iostream>
#include <list>
#include <vector>
using namespace std;
int main()
{
list<int> l1; // 空的构造
list<int> l2(4, 5); // 4个元素值为100
list<int> l3(l2.begin(), l2.end()); // 迭代器构造
list<int> l4(l3); // 拷贝构造
// 用数组构造
int arr[] = { 1,2,3,4,5 };
list<int> l5(arr, arr + sizeof(arr) / sizeof(arr[0]));
// 迭代器
for (auto it = l5.begin(); it != l5.end(); ++it)
cout << *it << " ";
cout << endl;
// 范围for遍历,要实现范围for遍历,应该在容器中增加begin()、end()还有迭代器比较方法
for (auto& e : l5)
cout << e << " ";
cout << endl;
// 容量
l5.empty(); // 判空
l5.size(); // 返回list 有效元素
// 元素访问
l5.front(); // 返回首元素
l5.back(); // 返回尾元素
l5.push_back(9); // 尾插9
l5.push_front(8); // 头插8
l5.pop_back(); // 尾删
l5.pop_front(); // 头删
l5.insert(l5.end(), 20); // 在尾部插入20
l5.insert(l5.begin(), 2, 3); // 插入2个3
l5.erase(--l5.end()); // 尾部删除
auto it = find(l5.begin(), l5.end(), 5); // 查找元素5返回迭代器
l5.clear(); // 清空
return 0;
}
三、迭代器的认知及失效
- 关于迭代器的认知:
- 概念:是一种设计模式,用于遍历容器中的数据,而不暴露容器实现细节
- 作用:是STL六大组件之一,容器和算法之间的纽带,算法实现通用化必须通过迭代器使用,与数据类型和数据结构都无关
- 本质:原生态指针或者是对原生态指针的封装
- 封装迭代器需要提供的方法:
- 构造函数:构造一个迭代器的对象与元素结合
- 添加指针类似操作:operator*()、operator->()
- 迭代器移动:operator++()、operator- -()、operator++(int)、operator- -(int)
- 迭代器比较:operator!=()、operator==()
- 给容器增加迭代器:
- 取别名:iterator
- 增加 begin()、end() 接口
list 的迭代器不是原生态指针,而是对指针的封装,因为迭代器要实现能到达下一个节点
根本原因:链表底层不是一段连续的空间,而是一个个封装的节点,通过指针域链接起来
- 关于迭代器失效的认知:底层指针所指空间已被释放,继续使用会造成程序崩溃
- vector中迭代器失效:插入/删除/扩容
- list 中迭代器失效:只会出现在删除中,并且只会影响删除节点的迭代器,对其他迭代器没有影响
四、模拟实现 list
#include <iostream>
using namespace std;
namespace my_list
{
template <class T>
struct ListNode
{
ListNode(const T& val = T())
: val_(val)
, pre_(nullptr)
, next_(nullptr)
{}
T val_;
ListNode* pre_;
ListNode* next_;
};
template <class T>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T> Self;
ListIterator(Node* node = nullptr)
:_pNode(node)
{}
T& operator*()
{
return _pNode->val_;
}
T* operator->()
{
return &(operator*());
}
Self& operator++() // 前置++
{
_pNode = _pNode->next_;
return *this;
}
Self operator++(int) // 后置++
{
Self temp(_pNode);
_pNode = _pNode->next_;
return temp;
}
Self& operator--() // 前置--
{
_pNode = _pNode->pre_;
return *this;
}
Self operator--(int) // 后置--
{
Self temp(_pNode);
_pNode = _pNode->pre_;
return temp;
}
bool operator!=(const Self& it)const
{
return _pNode != it._pNode;
}
bool operator==(const Self& it)const
{
return _pNode == it._pNode;
}
Node* _pNode;
};
template <class T>
class list
{
typedef ListNode<T> Node;
public:
typedef ListIterator<T> iterator;
list()
{
CreateHead();
}
list(size_t n, const T& data = T())
{
CreateHead();
for(int i = 0; i < n; ++i)
push_back(data);
}
//template <class Iterator>
list(int* first, int* last) // 迭代器构造必须采用模板,因为不同容器的迭代器实现可能不同
{
CreateHead();
while (first != last)
{
push_back(*first);
++first;
}
}
list(const list<T>& node)
{
CreateHead();
Node* cur = node.head_->next_;
while (cur != node.head_)
{
push_back(cur->val_);
cur = cur->next_;
}
}
~list()
{
Clear();
delete head_;
head_ = nullptr;
}
list<T>& operator=(list<T> node)
{
swap(head_, node.head_);
return *this;
}
iterator begin()
{
return iterator(head_->next_);
}
iterator end()
{
return iterator(head_);
}
size_t size()const
{
size_t count = 0;
Node* cur = head_->next_;
while (cur != head_)
{
cur = cur->next_;
count++;
}
return count;
}
bool empty()const
{
return head_->next_ == head_;
}
void resize(size_t n, const T& data = T())
{
size_t oldsize = size();
if (n > oldsize)
{
for (size_t i = oldsize; i < n; ++i)
{
push_back(data);
}
}
else
{
for (size_t i = n; i < oldsize; ++i)
{
pop_back();
}
}
}
void push_back(const T& data)
{
insert(end(), data);
}
void pop_back()
{
erase(--end());
}
void push_front(const T& data)
{
insert(begin(), data);
}
void pop_front()
{
erase(begin());
}
iterator insert(iterator pos, const T& data)
{
Node* node = new Node(data);
Node* posNode = pos._pNode;
node->pre_ = posNode->pre_;
node->next_ = posNode;
posNode->pre_->next_ = node;
posNode->pre_ = node;
return iterator(node);
}
iterator erase(iterator pos)
{
if (pos == end())
return pos;
Node* node = pos._pNode;
Node* pRet = node->next_;
node->pre_->next_ = node->next_;
node->next_->pre_ = node->pre_;
delete node;
node = nullptr;
return iterator(pRet);
}
iterator erase(iterator begin, iterator end)
{
while (begin != end)
{
begin = erase(begin);
}
return begin;
}
void Clear()
{
erase(begin(), end());
}
private:
void CreateHead()
{
head_ = new Node;
head_->pre_ = head_;
head_->next_ = head_;
}
private:
Node* head_;
};
}
void Test1()
{
my_list::list<int> l1;
my_list::list<int> l2(10, 5);
my_list::list<int> l3(l1);
int array[] = { 1,2,3,4,5 };
my_list::list<int> l4{ array, array + sizeof(array) / sizeof(array[0]) };
//auto it = l2.begin();
my_list::ListIterator<int> it = l4.begin();
while (it != l4.end())
{
cout << *it << " ";
it++;
}
cout << endl;
// 如果想要让容器支持范围for循环,则必须实现begin/end的方法,以及迭代器++操作
for (auto e : l2)
cout << e << " ";
cout << endl;
}
void Test2()
{
my_list::list<int> L;
L.push_back(1);
L.push_back(2);
L.push_back(3);
L.push_back(4);
L.push_back(5);
cout << L.size() << endl;
L.resize(10, 6);
for (auto e : L)
cout << e << " ";
cout << endl;
L.resize(4);
for (auto e : L)
cout << e << " ";
cout << endl;
L.push_front(0);
for (auto e : L)
cout << e << " ";
cout << endl;
L.pop_front();
for (auto e : L)
cout << e << " ";
cout << endl;
L.erase(L.begin());
for (auto e : L)
cout << e << " ";
cout << endl;
L.Clear();
if (L.empty())
{
cout << "ok" << endl;
}
}
int main()
{
// 迭代器是对原生态指针的封装
Test1();
return 0;
}
五、vector 和 list 的区别
相同点:都是STL提供的序列式容器,包含在 std 的命名空间中
vector | list | |
---|---|---|
底层结构 | 动态类型的顺序表 | 带头节点的双向循环链表 |
随机访问 | 支持,访问元素时间复杂度O(1) | 不支持,访问元素O(n) |
插入/删除效率 | 任意位置插入删除元素效率低 (搬移数据) | 任意位置插入删除效率高 |
迭代器 | 原生态指针 | 对原生态指针进行的封装 |
迭代器失效 | 插入/删除/扩容 | 删除 |
应用场景 | 访问元素较多,而插入删除较少 | 任意位置插入删除较多 |