list的介绍和模拟实现
- 前言
- `list`的基本操作
-
- 构造函数
- [`list iterator`](#
list iterator
) - [`list capcacity`](#
list capcacity
) - [`list modify`](#
list modify
)
- `list`模拟实现
-
- 存贮结构(双向带头循环)
- `iterator`
-
- `iterator`结构
- [`operator!=` `operator ==`](#
operator!=
operator ==
) - `operator++`
- `operator--`
- `list`数据结构
- [`list modify`](#
list modify
) - [`list operator`](#
list operator
)
- 源码
前言
string
vector
的是存储是基于物理空间上连续的,而list
是作为线性的链式结构,是值得学习的。
list
介绍
std::list
是C++标准模板库(STL
)中的一个容器适配器,它内部实现为双向链表结构。- 这种设计使得
std::list
能够在常数时间内进行任意位置的插入和删除操作,这是其相对于其他序列容器如std::vector
的显著优点。 std::list
不支持随机访问,即无法直接通过索引来访问容器中的元素,这通常需要从头部或尾部开始迭代到目标位置.
标准库容器 std::list
与 std::vector
的优缺点
- std::list: 作为一个双向链表,std::list 在插入和删除操作上具有优势,因为这些操作只涉及到改变相邻节点的指针,而不需要移动其他元素。此外,std::list 不需要预分配额外的内存,可以更好地处理动态内存分配,减少内存碎片.
- std::vector: 作为一个动态数组,std::vector 提供了高效的随机访问能力,可以通过下标直接访问任意位置的元素,其访问效率为 O(1). 此外,std::vector 通常具有较高的空间利用率和缓存友好性,因为其元素在内存中是连续存储的.
缺点
- std::list: 由于非连续的内存存储,std::list 在访问元素时效率较低,因为可能需要从头开始遍历链表。此外,每个节点都需要存储额外的指针信息,这增加了内存开销.
- std::vector: 在 std::vector 的中间位置插入或删除元素可能会引起大量元素的移动,以维持内存的连续性,这会导致较差的性能。当 std::vector 的容量不足以容纳新增元素时,还需要进行动态扩容,这是一个成本较高的操作
vector | list | |
---|---|---|
底层 结构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
随 机 访 问 | 支持随机访问,访问某个元素效率O(1) | 不支持随机访问,访问某个元素 效率O(N) |
插 入 删 除 | 任意位置插入和删除效率低,需要搬移元素, 时间复杂度为O(N),插入时有可能需要增容, 增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不 需要搬移元 素,时间复杂度为 O(1) |
插 入 删 除 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高 | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低 |
迭 代 器 | 原生态指针 | 对原生态指针(节点指针)进行封装 |
迭 代 器 失 效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
使 用 场 景 | 需要高效存储,支持随机访问,不关心插入删除效率 | 大量插入和删除操作,不关心随 机访问 |
list
的基本操作
构造函数
【C++】vector介绍以及模拟实现 | 接口说明 |
---|---|
list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的元素 |
list() | 构造空的list |
list (const list& x) | 拷贝构造函数 |
list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造list |
list iterator
此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。
函数声明 | 接口说明 |
---|---|
begin + end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
rbegin + rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置 |
list capcacity
函数声明 | 接口说明 |
---|---|
empty | 检测list是否为空,是返回true,否则返回false |
size | 返回list中有效节点的个数 |
list modify
函数声明 | 接口说明 |
---|---|
push_front | 在list首元素前插入值为val的元素 |
pop_front | 删除list中第一个元素 |
push_back | 在list尾部插入值为val的元素 |
pop_back | 删除list中最后一个元素 |
insert | 在list position 位置中插入值为val的元素 |
erase | 删除list position位置的元素 |
swap | 交换两个list中的元素 |
clear | 清空list中的有效元素 |
list
模拟实现
存贮结构(双向带头循环)
- 定义一个类(C语言:结构体)存储数据,作为指向下一个上一个的节点
cpp
namespace MyList
{
//LIst节点
template<class T>
struct _list_node
{
_list_node<T>* _next;
_list_node<T>* _prev;
T _val;
_list_node(const T& val = T())
: _next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
}
iterator
迭代器有两种实现方式,具体应根据容器底层数据结构实现:
- 原生态指针,比如:
vector
string
- 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
- 指针可以解引用,迭代器的类中必须重载operator*()
- 指针可以通过->访问其所指空间成员,迭代器类中必须重载 oprator->()
- 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
- 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
iterator
结构
- 三个模板参数
- 第一个模板参数控制类型
- 第二个模板参数控制返回值类型
const
非const
- 第三个模板参数控制返回的
list
类的类型const
非const
Node* _node;
需要定义一个指针,指向list
节点
cpp
template<class T,class Ref,class ptr>
struct _list_iterator
{
typedef _list_node<T> Node;
typedef _list_iterator<T, Ref,ptr> self;
Node* _node;
};
operator!=
operator ==
cpp
//重载operator!=
bool operator!=(const self& it)const
{
return _node != it._node;
}
//重载operator==
bool operator==(const self& it)const
{
return _node == it._node;
}
operator++
cpp
//重载operator++(前置)
self& operator++()
{
_node = _node->_next;
return *this;
}
//重载operator++(后置)
self& operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
operator--
cpp
//重载operator--(前置)
self& operator--()
{
_node = _node->_prev;
return *this;
}
//重载operator--(后置)
self& operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
operator*
operator->
cpp
//重载operator*
Ref& operator* ()
{
return _node->_val;
}
//重载operator->
ptr operator->()
{
return _node->_val;
}
list
数据结构
构造函数
list的初始化
- 双向带带头循环
_head
cpp
//空list初始化
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
节点初始化
- 默认构造函数
- 默认构造函数
- 用迭代器初始化
- 拷贝构造函数
- 赋值运算符重载
- 赋值运算符重载
cpp
//默认构造函数
list()
{
empty_init();
}
//默认构造函数
list(int n, const T& val = T())
{
empty_init();
while (n--)
{
push_back(val);
}
}
//用迭代器初始化
template <class Iterator>
list(Iterator first, Iterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
++first;
}
}
//拷贝构造函数
list(const list<T>& lt)
{
empty_init();
for (auto e : lt)
{
push_back(e);
}
}
//赋值运算符重载
list<T> operator=(list<T> lt)
{
swap(lt);
return *this;
}
//赋值运算符重载
~list()
{
clear();
delete _head;
}
迭代器
cpp
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin()const
{
return _head->_next;
}
const_iterator end()const
{
return _head;
}
list modify
insert
- 在pos位置插入节点,同数据结构是一样的,在这里面不过多赘述(可以参考)
cpp
//insert
iterator insert(iterator pos, const T& x)
{
Node* newcode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newcode;
newcode->_prev = prev;
newcode->_next = cur;
cur->_prev = newcode;
++_size;
return pos;
}
erase
- 删除迫使位置的节点,同样(可以参考)
cpp
//erase
iterator erase(iterator pos)
{
assert(pos._node != _head);
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return next;
}
头插、头删、尾插、尾删
- 可以复用
insert
ersae
STL标准模板库也是进行复用
cpp
// 头插
void push_front(const T& x)
{
insert(begin(), x);
}
//头删
void pop_front()
{
erase(begin());
}
//尾插
void push_back(const T& x)
{
insert(begin());
}
//尾删
void pop_back()
{
erase(--end());
}
list operator
交换
cpp
void swap(list<T> lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
clear
- 析构函数也是利用了clear
cpp
//删除
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
++it;
}
}
源码
cpp
#pragma once
#include <iostream>
#include <assert.h>
namespace MyList
{
//节点
template<class T>
struct _list_node
{
_list_node<T>* _next;
_list_node<T>* _prev;
T _val;
_list_node(const T& val = T())
: _next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
//迭代器
template<class T,class Ref,class ptr>
struct _list_iterator
{
typedef _list_node<T> Node;
typedef _list_iterator<T, Ref,ptr> self;
Node* _node;
_list_iterator (Node* node)
:_node(node)
{}
//重载operator*
Ref& operator* ()
{
return _node->_val;
}
//重载operator->
ptr operator->()
{
return _node->_val;
}
//重载operator++(前置)
self& operator++()
{
_node = _node->_next;
return *this;
}
//重载operator++(后置)
self& operator++(int)
{
self tmp(*this);
_node = _node->_next;
return tmp;
}
//重载operator--(前置)
self& operator--()
{
_node = _node->_prev;
return *this;
}
//重载operator--(后置)
self& operator--(int)
{
self tmp(*this);
_node = _node->_prev;
return tmp;
}
//重载operator!=
bool operator!=(const self& it)const
{
return _node != it._node;
}
//重载operator==
bool operator==(const self& it)const
{
return _node == it._node;
}
};
//list
template<class T>
class list
{
typedef _list_node<T> Node;
public:
typedef _list_iterator<T, T&,T*> iterator;
typedef _list_iterator<T, const T&,const T*> const_iterator;
//空list初始化
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//用n个val初始化
list(int n, const T& val = T())
{
empty_init();
while (n--)
{
push_back(val);
}
}
//用迭代器初始化
template <class Iterator>
list(Iterator first, Iterator last)
{
empty_init();
while (first != last)
{
push_back(*first);
++first;
}
}
//默认构造函数
list()
{
empty_init();
}
//拷贝构造函数
list(const list<T>& lt)
{
empty_init();
for (auto e : lt)
{
push_back(e);
}
}
//赋值运算符重载
list<T> operator=(list<T> lt)
{
swap(lt);
return *this;
}
//析构函数
~list()
{
clear();
delete _head;
}
//删除
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
++it;
}
}
//迭代器
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin()const
{
return _head->_next;
}
const_iterator end()const
{
return _head;
}
//void push_back(const T& x)
//{
// Node* newcode = new Node(x);
// Node* tail = _head->_prev;
// tail->_next = newcode;
// newcode->_next = _head;
// newcode->_prev = tail;
// _head->_prev = newcode;
// ++_size;
//}
// 头插
void push_front(const T& x)
{
insert(begin(), x);
}
//头删
void pop_front()
{
erase(begin());
}
//尾插
void push_back(const T& x)
{
insert(begin());
}
//尾删
void pop_back()
{
erase(--end());
}
//insert
iterator insert(iterator pos, const T& x)
{
assert(pos._node != _head);
Node* newcode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newcode;
newcode->_prev = prev;
newcode->_next = cur;
cur->_prev = newcode;
++_size;
return pos;
}
//erase
iterator erase(iterator pos)
{
assert(pos._node != _head);
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
--_size;
return next;
}
//大小
size_t size()const
{
return _size;
}
//交换
void swap(list<T> lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
private:
Node* _head;
size_t _size;
};
}