目录
STL标准库学习_list
【1】List的介绍及使用
【2】List常用的接口
【2.1】构造函数
【2.2】析构函数
【2.3】迭代器相关
【2.4】容量相关
【2.5】元素访问相关
【2.6】修改相关
【2.7】运行相关
【2.8】观察相关
【2.9】非成员函数重载
【3】list模拟实现
【3.1】list节点结构框架
【3.2】 list类结构框架
【3.2】 无参构造DHList()
【3.3】initializer_list构造函数DHList(initializer_list il)
【3.4】迭代器构造DHList(InputIterator first, InputIterator last)
【3.5】析构函数~DHList()
【3.6】拷贝构造DHList(const DHList& list)
【3.7】赋值拷贝DHList& operator=(DHList list)
【3.8】尾插PushBack(const T& val)
【3.9】头插void PushFront(const T& val)
【3.10】尾删void PopBack()
【3.11】头删void PopFront()
【3.12】指定位置插入iterator Insert(iterator pos, const T& val)
【3.13】指定位置删除iterator Erase(iterator pos)
【3.14】 判断空bool Empty()
【3.15】 返回大小size_t Size()
【3.16】交换void Swap(List& list)
【3.17】清空void Clear()
【3.18】 正向迭代器
【3.19】 list完整类
【3.20】vector和list的对比
https://cplusplus.com/reference/list/list/
// 默认构造
explicit list (const allocator_type& alloc = allocator_type());
// 构造的list中包含n个值为val的元
explicit list (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
// 迭代器构造
template
list (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
// 拷贝构造
list (const list& x);
list& operator= (const list& x);
// 析构函数
~list();
// 正向迭代器
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
// 反向迭代器
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
// C++11
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;
// 检查空
bool empty() const;
// 返回大小
size_type size() const;
// 返回最大元素数
size_type max_size() const;
// 返回list的第一个节点中值的引用
reference front();
const_reference front() const;
// 返回list的最后一个节点中值的引用
reference back();
const_reference back() const;
// 将新内容分配给容器
template
void assign (InputIterator first, InputIterator last);
void assign (size_type n, const value_type& val);
// 在list首元素前插入值为val的元素
void push_front (const value_type& val);
// 删除list中第一个元素
void pop_front();
// 在list尾部插入值为val的元素
void push_back (const value_type& val);
// 删除list中最后一个元素
void pop_back();
// 在list position 位置中插入值为val的元素
iterator insert (iterator position, const value_type& val);
void insert (iterator position, size_type n, const value_type& val);
template
void insert (iterator position, InputIterator first, InputIterator last);
// 删除list position位置的元素
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
// 交换两个list中的元素
void swap (list& x);
// 清空list中的有效元素
void clear();
// 调整大小
void resize (size_type n, value_type val = value_type());
// c++11
template
void emplace_front (Args&&... args);
template
void emplace_back (Args&&... args);
template
iterator emplace (const_iterator position, Args&&... args);
// 转移(将链表转移到另一个链表中)
void splice (iterator position, list& x);
void splice (iterator position, list& x, iterator i);
void splice (iterator position, list& x, iterator first, iterator last);
// 给定一个值,自己查找到删除
void remove (const value_type& val);
// 满足某个条件删除值
template
void remove_if (Predicate pred);
// 去重
void unique();
template
void unique (BinaryPredicate binary_pred);
// 合并
void merge (list& x);
template
void merge (list& x, Compare comp);
// 排序
void sort();
template
void sort (Compare comp);
// 逆置
void reverse();
allocator_type get_allocator() const;
template
bool operator== (const list& lhs, const list& rhs);
template
bool operator< (const list& lhs, const list& rhs);
template
bool operator< (const list& lhs, const list& rhs);
template
bool operator<= (const list& lhs, const list& rhs);
template
bool operator> (const list& lhs, const list& rhs);
template
bool operator>= (const list& lhs, const list& rhs);
template
void swap (list& x, list& y);
强调一下head是哨兵结点,本身不存储数据,作用只是指向头和尾!
// 《带头双向循环链表 - 数据结构》
// 模板参数
template
// 结构体的默认成员是public.
struct ListNode
{
public:
// 显示声明构造函数
ListNode(const T& val = T()) // 匿名构造
: _prev(nullptr)
, _next(nullptr)
, _data(val)
{}
public:
ListNode* _prev; // 指向上一个节点.
ListNode* _next; // 指向下一个节点.
T _data; // 存储的数据
};
这里对于T()说明一下!这里是匿名构造,对于传来的参数类型不确定,可能是内置类型,也可能是类对象!所以不能想当然比如说默认赋值0!需要使用对象的构造函数.
创建链表节点:链表节点本身指向自己。
// 《带头双向循环链表 - 类结构》
template
class List
{
private:
typedef ListNode Node;
private:
Node* _head;
size_t _size;
};
// 《带头双向循环链表 - 无参构造函数》
// 构造函数(无参构造函数)
DHList() {
EmptyInitialize();
}
/ 《带头双向循环链表 - 创建节点初始化》
void EmptyInitialize()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
// initializer_list构造函数
DHList(initializer_list il) {
EmptyInitialize();
typename initializer_list::iterator it = il.begin();
while (it != il.end()) {
PushBack(*it);
++it;
}
}
// 构造函数(迭代器构造)
// 构造函数(迭代器构造)
template
DHList(InputIterator first, InputIterator last) {
EmptyInitialize();
while (first != last) {
PushBack(*first);
++first;
}
}
// 《带头双向循环链表 - 创建节点初始化》
void EmptyInitialize()
{
_head = new Node(T());
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
// 《带头双向循环链表 - 析构函数》
~DHList() {
Clear();
delete _head;
_head = nullptr;
}
// 链表拷贝构造函数 - 传统写法
//DHList(const DHList& list)
//{
// // 创造节点,初始化节点
// EmptyInitialize();
// for (const T& e : list)
// {
// PushBack(e);
// }
//}
// 链表拷贝构造函数 - 现代写法
DHList(const DHList& list) {
// 创造节点,初始化节点
EmptyInitialize();
DHList temp = DHList(list.begin(), list.end());
Swap(temp);
}
// 运算符重载:[] - 传统写法
/*DHList& operator=(const DHList& list)
{
if (this != &list)
{
this->Clear();
for (const auto& v : list)
{
PushBack(v);
}
return *this;
}
}*/
// 运算符重载:[] - 现代写法
DHList& operator=(DHList list) {
Swap(list);
return *this;
}
// 《链表尾插》
void PushBack(const T& val)
{
// 创建一个新的节点.
Node* newNode = new Node(val);
// 找到尾节点.
Node* tail = _head->_prev;
// 链接
// 尾节点->下一个节点 指向新的节点
tailNode->_nextNode = newNode;
// 头节点->上一个节点 指向新的节点
_head->_prevNode = newNode;
// 新节点->下一个节点 指向头节点
newNode->_nextNode = _head;
// 新节点->上一个节点 指向尾节点(更改完成tailNode就不是最后一个节点了)
newNode->_prevNode = tailNode;
}
// 头插数据
void PushFront(const T& val) {
// 创建新的节点
Node* newNode = new Node(val);
// 找到链表头节点的下一个节点
Node* nextNode = _head->_nextNode;
// 头节点的下一个节点->上一个节点 指向新节点
nextNode->_prevNode = newNode;
// 头节点->下一个节点 指向新节点
_head->_nextNode = newNode;
// 新节点->下一个节点 指向原头节点的下一个节点
newNode->_nextNode = nextNode;
// 新节点->上一个节点 指向头节点
newNode->_prevNode = _head;
}
// 尾删数据
void PopBack() {
// 判断链表不为空链表
assert(!Empty());
// 找到链表最后一个节点
Node* pTail = _head->_prevNode;
// 找到链表最有一个节点的上一个节点
Node* pFirst = pTail->_prevNode;
// 头节点->上一个节点值 指向链表最后一个节点的上一个节点
_head->_prevNode = pFirst;
// 链表最后一个节点->上一个节点的下一个节点 指向头节点
pFirst->_nextNode = _head;
// 释放
delete pTail;
pTail = nullptr;
}
// 头删数据
void PopFront() {
// 判断链表不为空链表
assert(!Empty());
// 找到链表第一个节点
Node* pFirst = _head->_nextNode;
// 找到链表第二个节点
Node* pSecond = pFirst->_nextNode;
// 头节点->下一个节点 指向第二个节点
_head->_nextNode = pSecond;
// 第二个节点->上一个节点 指向头节点
pSecond->_prevNode = _head;
// 释放
delete pFirst;
pFirst = nullptr;
}
由于C++特别喜欢复用,我们也发现实现一个Insert()就可以实现以上的功能。
// 《带头双向循环链表 - 尾插》
void PushBack(const T& val)
{
Insert(end(), val);
}
// 《带头双向循环链表 - 头插》
void PushFront(const T& val)
{
Insert(begin(), val);
}
// 《带头双向循环链表 - 指定位置插入》
iterator Insert(iterator pos, const T& val) {
assert(pos != nullptr);
// 创建新的节点
Node* newNode = new Node(val);
// 传递的pos位置是迭代器,先从迭代器中拿到节点.
Node* pCurNode = pos._node;
Node* pPosPrevNode = pCurNode->_prevNode;
// pos上一个节点->下一个节点 指向新的节点
pPosPrevNode->_nextNode = newNode;
// pos上一个节点 指向新节点
pCurNode->_prevNode = newNode;
// 新节点上一个节点指向 原pos上一个节点
newNode->_prevNode = pPosPrevNode;
// 新节点下一个节点指向 原pos节点
newNode->_nextNode = pCurNode;
++_size;
return iterator(newNode);
}
同样根据C++的特性实现Erase(),实现以上所有的功能。
// 《带头双向循环链表 - 尾删》
void PopBack()
{
Erase(--end());
}
// 《带头双向循环链表 - 头删》
void PopFront()
{
Erase(begin());
}
// 《带头双向循环链表 - 指定位置删除》
iterator Erase(iterator pos) {
assert(pos != nullptr);
assert(!Empty());
// 获取pos下一个节点
Node* prevNode = pos._node->_prevNode;
// 获取pos上一个节点
Node* nextNode = pos._node->_nextNode;
// pos上一个节点->下一个节点 指向pos下一个节点
prevNode->_nextNode = nextNode;
// pos下一个节点->上一个节点 指向pos上一个节点
nextNode->_prevNode = prevNode;
// 释放pos位置节点
delete pos._node;
--_size;
return iterator(nextNode);
}
// 判断为空
bool Empty() {
return _head->_nextNode == _head;
}
// 返回大小
size_t Size() {
return _size;
}
// 《链表内部交换》
void Swap(List& list)
{
std::swap(_head, list._head);
std::swap(_size, list._size);
}
// 《带头双向循环链表 - 清理链表保留头节点》
void Clear()
{
// 直接使用内部的迭代器
iterator itBegin = begin();
while (itBegin != end())
{
itBegin = Erase(itBegin);
}
}
iterator 原生指针,数组结构正好支持迭代器的行为。
iterator不能和顺序表一样,iterator 原生指针(Node*),不能满足迭代器行为。我们可以用类封装+运算符重载支持。
// 链表struct结构:迭代器
// typedef __list_iterator iterator;
// typedef __list_iterator const_iterator;
template
struct __ListIterator {
typedef DListNode Node;
typedef __ListIterator Self;
Node* _node;
// 构造函数
__ListIterator(Node* pNode)
: _node(pNode)
{};
// 运算符重载:*
Ref operator*() {
return _node->_data;
}
// 运算符重载:->
Ptr operator->() {
return &_node->_data;
}
// 运算符重载:++前置
Self& operator++() {
_node = _node->_nextNode;
return *this;
}
// 运算符重载:++后置
Self operator++(int)
{
Self temp(*this);
_node = _node->_next;
return temp;
}
// 运算符重载:--前置
Self& operator--() {
_node = _node->_prevNode;
return *this;
}
// 运算符重载:--后置
Self operator--(int)
{
Self temp(*this);
_node = _node->_prev;
return temp;
}
// 运算符重载:!=
bool operator!=(const Self& it) {
return _node != it._node;
}
// 运算符重载:==
bool operator==(const Self& it) {
return _node == it._node;
}
};
// 《迭代器》
typedef __ListIterator iterator;
// typedef __ListConstIterator const_iterator;
typedef __ListIterator const_iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);
}
const_iterator end() const
{
return const_iterator(_head);
}
#include
#include
using namespace std;
namespace ShaXiang_DList1 {
/* 带头双向循环链表 - 数据结构*/
template
struct DListNode {
// 构造函数(带默认构造函数)
DListNode(const T& val = T())
: _prevNode(nullptr)
, _nextNode(nullptr)
, _data(val)
{};
DListNode* _prevNode; // 指向上一个节点地址
DListNode* _nextNode; // 指向下一个节点地址
T _data; // 存储数据值
};
/* 带头双向循环链表 - 迭代器类 */
template
struct __ListIterator {
typedef DListNode Node;
typedef __ListIterator Self;
Node* _node;
// 构造函数
__ListIterator(Node* pNode)
: _node(pNode)
{};
// 运算符重载:*
Ref operator*() {
return _node->_data;
}
// 运算符重载:->
Ptr operator->() {
return &_node->_data;
}
// 运算符重载:++前置
Self& operator++() {
_node = _node->_nextNode;
return *this;
}
// 运算符重载:++后置
Self operator++(int)
{
Self temp(*this);
_node = _node->_next;
return temp;
}
// 运算符重载:--前置
Self& operator--() {
_node = _node->_prevNode;
return *this;
}
// 运算符重载:--后置
Self operator--(int)
{
Self temp(*this);
_node = _node->_prev;
return temp;
}
// 运算符重载:!=
bool operator!=(const Self& it) {
return _node != it._node;
}
// 运算符重载:==
bool operator==(const Self& it) {
return _node == it._node;
}
};
/* 带头双向循环链表 - 类*/
template
class DList {
// 公有取别名
public:
typedef DListNode Node;
typedef __ListIterator iterator;
typedef __ListIterator const_iterator;
// 私有成员变量
private:
Node* _head;
size_t _size;
// 公开成员函数
public:
// 迭代器
iterator begin() {
return iterator(_head->_nextNode);
}
iterator end() {
return iterator(_head);
}
const_iterator begin() const {
return const_iterator(_head->_nextNode);
}
const_iterator end() const {
return const_iterator(_head);
}
// 公有成员函数
public:
// 构造函数(无参构造函数)
DList() {
EmptyInitialize();
}
// 构造函数(迭代器构造)
template
DList(InputIterator first, InputIterator last) {
EmptyInitialize();
while (first != last) {
PushBack(*first);
++first;
}
}
// 析构函数
~DList() {
Clear();
delete _head;
_head = nullptr;
}
// 链表拷贝构造函数 - 传统写法
//DList(const DList& list)
//{
// // 创造节点,初始化节点
// EmptyInitialize();
// for (const T& e : list)
// {
// PushBack(e);
// }
//}
// 链表拷贝构造函数 - 现代写法
DList(const DList& list) {
// 创造节点,初始化节点
EmptyInitialize();
DList temp = DList(list.begin(), list.end());
Swap(temp);
}
// 公有成员函数
public:
// 初始化
void EmptyInitialize(){
// 创建节点->节点指向自己.
_head = new Node(T());
_head->_nextNode = _head; // 初始化指向自己
_head->_prevNode = _head; // 初始化指向自己
_head->_data = 0; // 数据初始化为0
_size = 0; // 头节点存放数据个数初始化为0
}
// 尾插数据
void PushBack(const T& val) {
/*
// 创建新的节点
Node* newNode = new Node(val);
// 找到链表的尾节点
Node* tailNode = _head->_prevNode;
// 尾节点->下一个节点 指向新的节点
tailNode->_nextNode = newNode;
// 头节点->上一个节点 指向新的节点
_head->_prevNode = newNode;
// 新节点->下一个节点 指向头节点
newNode->_nextNode = _head;
// 新节点->上一个节点 指向尾节点(更改完成tailNode就不是最后一个节点了)
newNode->_prevNode = tailNode;
*/
Insert(end(), val);
++_size;
}
// 头插数据
void PushFront(const T& val) {
/*
// 创建新的节点
Node* newNode = new Node(val);
// 找到链表头节点的下一个节点
Node* nextNode = _head->_nextNode;
// 头节点的下一个节点->上一个节点 指向新节点
nextNode->_prevNode = newNode;
// 头节点->下一个节点 指向新节点
_head->_nextNode = newNode;
// 新节点->下一个节点 指向原头节点的下一个节点
newNode->_nextNode = nextNode;
// 新节点->上一个节点 指向头节点
newNode->_prevNode = _head;
*/
Insert(begin(), val);
++_size;
}
// 尾删数据
void PopBack() {
/*
// 判断链表不为空链表
assert(!Empty());
// 找到链表最后一个节点
Node* pTail = _head->_prevNode;
// 找到链表最有一个节点的上一个节点
Node* pFirst = pTail->_prevNode;
// 头节点->上一个节点值 指向链表最后一个节点的上一个节点
_head->_prevNode = pFirst;
// 链表最后一个节点->上一个节点的下一个节点 指向头节点
pFirst->_nextNode = _head;
// 释放
delete pTail;
pTail = nullptr;
*/
Erase(--end());
--_size;
}
// 头删数据
void PopFront() {
/*
// 判断链表不为空链表
assert(!Empty());
// 找到链表第一个节点
Node* pFirst = _head->_nextNode;
// 找到链表第二个节点
Node* pSecond = pFirst->_nextNode;
// 头节点->下一个节点 指向第二个节点
_head->_nextNode = pSecond;
// 第二个节点->上一个节点 指向头节点
pSecond->_prevNode = _head;
// 释放
delete pFirst;
pFirst = nullptr;
*/
Erase(begin());
--_size;
}
// 指定位置插入数据
iterator Insert(iterator pos, const T& val) {
assert(pos != nullptr);
// 创建新的节点
Node* newNode = new Node(val);
// 传递的pos位置是迭代器,先从迭代器中拿到节点.
Node* pCurNode = pos._node;
Node* pPosPrevNode = pCurNode->_prevNode;
// pos上一个节点->下一个节点 指向新的节点
pPosPrevNode->_nextNode = newNode;
// pos上一个节点 指向新节点
pCurNode->_prevNode = newNode;
// 新节点上一个节点指向 原pos上一个节点
newNode->_prevNode = pPosPrevNode;
// 新节点下一个节点指向 原pos节点
newNode->_nextNode = pCurNode;
++_size;
return iterator(newNode);
}
// 指定位置删除数据
iterator Erase(iterator pos) {
assert(pos != nullptr);
assert(!Empty());
// 获取pos下一个节点
Node* prevNode = pos._node->_prevNode;
// 获取pos上一个节点
Node* nextNode = pos._node->_nextNode;
// pos上一个节点->下一个节点 指向pos下一个节点
prevNode->_nextNode = nextNode;
// pos下一个节点->上一个节点 指向pos上一个节点
nextNode->_prevNode = prevNode;
// 释放pos位置节点
delete pos._node;
--_size;
return iterator(nextNode);
}
// 判断为空
bool Empty() {
return _head->_nextNode == _head;
}
// 返回大小
size_t Size() {
return _size;
}
// 清空
void Clear() {
iterator it = begin();
while (it != end()) {
it = Erase(it);
}
}
// 交换
void Swap(DList& list)
{
std::swap(_head, list._head);
std::swap(_size, list._size);
}
// 打印测试
void Print() {
Node* beginNode = _head->_nextNode;
while (beginNode != _head) {
cout << beginNode->_data << " ";
beginNode = beginNode->_nextNode;
}
cout << endl;
}
// 公有成员函数
public:
// 运算符重载:[] - 传统写法
/*DList& operator=(const DList& list)
{
if (this != &list)
{
this->Clear();
for (const auto& v : list)
{
PushBack(v);
}
return *this;
}
}*/
// 运算符重载:[] - 现代写法
DList& operator=(DList list) {
Swap(list);
return *this;
}
};
}
vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:
vector |
list |
|
底 层 结 构 |
动态顺序表,一段连续空间 |
带头结点的双向循环链表 |
随 机 访 问 |
支持随机访问,访问某个元素效率O(1) |
不支持随机访问,访问某个元素 效率O(N) |
插 入 和 删 除 |
任意位置插入和删除效率低,需要搬移元素,时间复杂 度为O(N),插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 |
任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1) |
空 间 利 用 率 |
底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 |
底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
迭 代 器 |
原生态指针 |
对原生态指针(节点指针)进行封装 |
迭 代 器 失 效 |
在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 |
插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
使 用 场 景 |
需要高效存储,支持随机访问,不关心插入删除效率 |
大量插入和删除操作,不关心随 机访问 |