【C++】list的模拟实现
戴墨镜的恐龙 2024-07-13 15:05:02 阅读 92
文章目录
1. list的介绍2. 模拟实现2.1 链表的节点2.2 链表的数据结构2.3 push_back、pop_back2.4 list的迭代器2.5 构造、析构2.6 insert、erase、push_front、pop_front2.7 容量、clear、swap
3. 完整代码
1. list的介绍
list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。与其他序列式容器相比,list最大的缺陷是不支持任意位置的随机访问;而且list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)
2. 模拟实现
2.1 链表的节点
每一个设计过链表的人都知道,链表本身和链表的节点是不同的结构,需要分开设计。
<code>template <class T>
struct ListNode
{
typedef ListNode<T> Node;
Node* _next;//后继节点
Node* _prev;//前驱节点
T _data;
//构造函数
ListNode(const T& data = T())
:_next(nullptr)
,_prev(nullptr)
,_data(data)
{ }
};
2.2 链表的数据结构
对于list而言,它不仅仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,就可以完整的表现整个链表:
template <class T>
class list
{
public:
typedef ListNode<T> Node;
void empty_init()
{
_head = new Node;//会调用Node的构造
//自己成环
_head->_next = _head;
_head->_prev = _head;
}
list()
{
empty_init();
}
private:
Node* _head;//哨兵位
};
2.3 push_back、pop_back
相信学习过数据结构中链表这一部分的伙伴对这两个操作不陌生了吧。
//尾插
void push_back(const T& val)
{
Node* newnode = new Node(val);//创建新节点
Node* prev = _head->_prev;
newnode->_next = _head;
newnode->_prev = _head->_prev;
prev->_next = newnode;
_head->_prev = newnode;
}
<code>//尾删
void pop_back()
{
assert(_head->_next != _head);//不能删除哨兵位
Node* del = _head->_prev;
Node* prev = del->_prev;
prev->_next = _head;
_head->_prev = prev;
delete del;
del = nullptr;
}
2.4 list的迭代器
list的迭代器不能够再像vector一样以普通指针作为迭代器了,因为其节点不保证再存储空间中是连续存在的。list的迭代器也必须有能力指向list的节点,并能够进行正确的递增、递减、取值、成员存取等操作。
所谓list迭代器正确的递增、递减、取值、成员取用操作是指:递增是指向下一个节点,递减是指向上一个节点,取值时取的是节点的数据值,成员取用时取用的时节点的成员。
所以为了达到这一目的,我们可以将list的迭代器放在一个类中,在类中重载迭代器正常的操作。
<code>template <class T>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T> Self;
Node* _node;//指向节点的指针
ListIterator(Node* node = nullptr)//默认使用空指针构造
:_node(node)
{ }
};
重载迭代器的常规操作。
//前置:++it
Self& operator++()
{
//指针后移,指向下一个节点
_node = _node->_next;
return *this; //this是ListIterator类型的指针,*this才是ListIterator
}
//后置::it++
Self operator++(int)
{
Self tmp(*this);//使用当前迭代器 拷贝构造它
_node = _node->_next;
return tmp;
}
//前置::--it
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//后置::it--
Self operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
要想使用迭代器,需要在list类中实现begin和end方法。
typedef ListIterator<T> iterator;
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
这样,我们就可以使用迭代器遍历我们的链表了。
那const迭代器怎么实现呢?
我们直接在普通迭代器前面加上const可不可行呢?
<code>iterator begin()
{
return iterator(_head->_next);
}
const iterator begin()const
{
return iterator(_head->_next);
}
答案是不可行的,因为即使是const迭代器,也应该支持++、- - 等操作。
const迭代器的目标是指向的内容不能改,并不是迭代器本身不能改,类似const T* p
那么const迭代器如何实现上述目的呢?
我们是不是需要再写一个ListConstIterator类,然后该类中的解引用与箭头使用const修饰,就可以达到想要的效果。
此时我们的const迭代器就可以跑起来了
但是你觉不觉得现在的代码有点冗余,两个类仅仅只有一点不同,大多数代码都是重复的。有没有什么好的办法解决呢?
我直接在原来模板的基础上加个const行不行呢?
答案是不行的。
所以,我们要如何解决呢?
我们可以使用类的模板参数进行传递,为类设计三个模板参数,各参数之间互不影响。
到此,我们的迭代器就完美了。
2.5 构造、析构
这里就非常简单了,无非就是开空间,拷数据。
<code>//构造
void empty_init()
{
_head = new Node;//会调用Node的构造
//自己成环
_head->_next = _head;
_head->_prev = _head;
}
list()
{
empty_init();
}
list(const list<T>& lt)
{
empty_init();//哨兵位
Node* cur = lt._head->_next;
while (cur != lt._head)
{
push_back(cur->_data);
cur = cur->_next;
}
}
list(size_t n, const T& val = T())
{
empty_init();
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
//lt2 = lt1
//传统写法
//list<T>& operator=(const list<T>& lt)
//{
//empty_init();
//for (auto e : lt)
//{
//push_back(e);
//}
//}
//现代写法
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
//列表初始化
list(initializer_list<T> lt)
{
empty_init();
for (const auto& e : lt)
{
push_back(e);
}
}
~list()
{
Node* cur = _head->_next;
Node* next = cur->_next;
while (cur != _head)
{
delete cur;
cur = next;
next = cur->_next;
}
delete _head;//释放哨兵位
_head = nullptr;
}
2.6 insert、erase、push_front、pop_front
list有一个重要性质,插入操作(insert)和接合操作(splice)都不会造成原有的list迭代器失效。甚至list的元素删除操作(erase),也只有“指向被删除元素”的那个迭代器失效,其它迭代器均不受影响
<code>iterator insert(iterator pos, const T& val)
{
Node* newnode = new Node(val);
Node* next = pos._node;
Node* prev = pos._node->_prev;
newnode->_next = next;
newnode->_prev = prev;
next->_prev = newnode;
prev->_next = newnode;
return pos;
}
iterator erase(iterator pos)
{
assert(pos != end());//不能删除哨兵位
Node* next = pos._node->_next;
Node* prev = pos._node->_prev;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
push_front与pop_front可以复用insert和erase函数。
<code>void push_front(const T& val)
{
insert(begin(), val);
}
void pop_front()
{
erase(begin());
}
2.7 容量、clear、swap
//容量
size_t size()
{
size_t n = 0;
Node* cur = _head->_next;
while (cur != _head)
{
n++;
cur = cur->_next;
}
return n;
}
bool empty()
{
return _head->_next == _head;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it); //erase会返回删除元素位置后面元素的迭代器
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
}
3. 完整代码
#include<iostream>
using namespace std;
#include<assert.h>
namespace lt
{
//节点类
template <class T>
struct ListNode
{
ListNode<T>* _next;
ListNode<T>* _prev;
T _data;
ListNode(const T& data = T())
:_next(nullptr)
, _prev(nullptr)
, _data(data)
{ }
};
//迭代器类
template <class T,class Ref,class Ptr>
struct ListIterator
{
typedef ListNode<T> Node;
typedef ListIterator<T, Ref, Ptr> Self;
Node* _node;
ListIterator(Node* node = nullptr)
:_node(node)
{ }
ListIterator(const Self& it)
{
_node = it._node;
}
//++it
Self& operator++()
{
this->_node = this->_node->_next;
return *this;
}
//it++
Self operator++(int)
{
Self tmp(*this); //调用上面的拷贝构造
this->_node = this->_node->_next;
return tmp;
}
//--it
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//it--
Self& operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
Ref& operator*()
{
return _node->_data;
}
Ptr* operator->()
{
return &_node->_data;
}
};
//链表类
template <class T>
class list
{
public:
typedef ListNode<T> Node;
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;
typedef ReverseIterator<T, const T&, const T*> reverse_iterator;
//------------------------------------------------------------------------
//构造
void empty_init()
{
_head = new Node;//哨兵位
_head->_next = _head;
_head->_prev = _head;
}
list()
{
empty_init();
}
list(const list<T>& lt)
{
empty_init();//先整一个哨兵位
Node* cur = lt._head->_next;
while (cur != lt._head)
{
push_back(cur->_data);
cur = cur->_next;
}
}
list(size_t n, const T& val = T())
{
empty_init();
for (size_t i = 0; i < n; i++)
{
push_back(val);
}
}
~list()
{
Node* cur = _head->_next;
Node* next = cur->_next;
while (cur != _head)
{
delete cur;
cur = next;
next = cur->_next;
}
delete _head;
_head = nullptr;
}
list(initializer_list<T> lt)
{
empty_init();
for (const auto e : lt)
{
push_back(e);
}
}
void swap(list<T>& x)
{
std::swap(_head, x._head);
}
list<T>& operator=(list<T> lt)
{
swap(lt._head);
return *this;
}
//------------------------------------------------------------------------
//迭代器
iterator begin()
{
return iterator(_head->_next);
}
iterator end()
{
return iterator(_head);
}
const_iterator cbegin()const
{
return const_iterator(_head->_next);
}
const_iterator cend()const
{
return const_iterator(_head);
}
reverse_iterator rbegin()
{
return reverse_iterator(_head->_prev);
}
reverse_iterator rend()
{
return reverse_iterator(_head);
}
//------------------------------------------------------------------------
//容量
size_t size()const
{
size_t n = 0;
Node* cur = _head->_next;
while (cur != _head)
{
n++;
cur = cur->_next;
}
return n;
}
bool empty()const
{
return _head->_next == _head;
}
//------------------------------------------------------------------------
//访问
T& front()
{
//链表不能为空
assert(_head->_next != _head);
return _head->_next->_data;
}
const T& front()const
{
assert(_head->_next != _head);
return _head->_next->_data;
}
T& back()
{
//链表不能为空
assert(_head->_prev != _head);
return _head->_prev->_data;
}
const T& back()const
{
assert(_head->_prev != _head);
return _head->_prev->_data;
}
//------------------------------------------------------------------------
//修改
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* prev = _head->_prev;
newnode->_next = _head;
newnode->_prev = prev;
_head->_prev = newnode;
prev->_next = newnode;
}
void pop_back()
{
Node* del = _head->_prev;
Node* prev = del->_prev;
prev->_next = _head;
_head->_prev = prev;
delete del;
del = nullptr;
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
iterator insert(iterator pos, const T& val)
{
Node* newnode = new Node(val);
Node* next = pos._node;
Node* prev = pos._node->_prev;
newnode->_next = next;
newnode->_prev = prev;
prev->_next = newnode;
next->_prev = newnode;
return pos;
}
iterator erase(iterator pos)
{
assert(pos != end());//哨兵位不能删除
Node* prev = pos._node->_prev;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
return iterator(next);
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
private:
Node* _head;
};
}
上一篇: 有效 java.sql.SQLNonTransientConnectionException:Could not create connection to database server异常的正确解决
下一篇: 【小沐学Java】VSCode搭建Java开发环境
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。