1.list易错点
(1)慎用list的sort,list的排序比vector慢得多,尽管两者时间复杂度一样,甚至不如先把list转为vector,用vector排完序后再转为list
(2)splice是剪切链表,将x的部分剪切到pos后,也可以自己剪自己的
(3)list迭代器不支持[]运算符,但相比较vector多支持了push_front和pop_front,原因在于[]效率不高,push_front和pop_front效率高
2.list模拟实现
list实现中我们会用到多个模板,注意每个template定义的模板参数都只能供当前类或函数使用,不存在一个文件中所有T都是一个意思
(1)结点
为了适应不同数据的存储,我们采用模板的方式来定义结点。为了能够体现封装特性,我们将创建结点并赋值作为一个构造函数写入类ListNode中。因为在接下来的class list中会频繁调用lt->next,即在外部访问LIstNode的成员变量,所以我们的ListNode使用struct,默认访问限定符是public
(2)无参构造和析构
我们的list是带头双向循环链表,所以所有的构造都需要定义一个哨兵位
还有很多带参的构造,虽然这里我们可以去实现,但是它们需要使用到的功能和后面的函数相符合,所以我会先实现后续的函数。
析构只需要遍历所有节点,将它们释放即可
(3)迭代器
list的迭代器和指针就拉开了差距,list的数据是不连续存储的,因此无法使用指针的加减来遍历list。我们需要定义一个iterator的类,在这个类里重载运算符++、*等操作,使它们在使用过程中和指针一致但能够正常访问数据。
我们使用ListNode*作为结点的标识,当++时就调用它的_next,--就调用它的_prev,*就返回对应_data的值。有了这个思路,迭代器的大部分功能我们都可以顺利实现了。
template<class T, class T1 = T>
struct List_iterator
{
typedef ListNode<T> Node;
typedef List_iterator<T, T1> iterator;
List_iterator(Node* node)
:_curnode(node)
{}
iterator operator++()
{
_curnode = _curnode->_next;
return _curnode;
}
iterator operator--()
{
_curnode = _curnode->_prev;
return _curnode;
}
iterator operator++(int)
{
_curnode = _curnode->_next;
return _curnode->_prev;
}
iterator operator--(int)
{
_curnode = _curnode->_prev;
return _curnode->_next;
}
T1& operator*()//对于const T无法进行修改
{
return _curnode->_data;
}
T1* operator->()//对于const T无法进行修改
{
return &(_curnode->_data);
}
bool operator!=(const List_iterator lt) const
{
return _curnode != lt._curnode;
}
bool operator==(const List_iterator lt) const
{
return _curnode == lt._curnode;
}
Node* _curnode;
};
但是这里面还有一些代码需要解释
a.operator->()
要理解这里,我们要思考迭代器本身想要模仿的是指向数据的指针,如果存储类型是int,对应iterator和int*的使用方式一模一样,类似地,像char、double、int*等内置类型是这样,那么自定义类型呢?operator->()就是为了模仿自定义类型的指针而专门设计的。
我们先将T1就看成T,T是一个结构体,里面有自己的成员变量,当我们使用结构体指针想要访问它们时,我们首先要拿到这个结构体的地址,再用->访问,这里的iterator也是如此,_data是结构体类型,我们直接先指向这个结构体内容本身,即_curnode->_data,这个表达式的返回值是一个结构体,也就是我们想要访问的结构体,但是我们是要模拟指针的操作,要使用->而不是.,所以我们要再对它取地址,即&(_curnode->_data),这样返回的就是一个指向_data的指针了,当我们调用it.operator->()->(成员变量)时就能访问结构体内部的成员变量了,简化为it->(成员变量),省略了一个->为了易读性。
b.T1和T的区分
在我们想要创建一个const_iterator时,T1的出现就很重要了。
当不传第二个模板参数时,默认和第一个一样,如果传了const T,那就使用传的参数
对于这两个要返回指针或引用的函数,加上const修饰T可以防止T被修改。那为什么不直接用const T一个参数呢?要注意const int和int是两个类型,对于一些自定义类型来说有很大区别,所以要分成两块来写。
有了上面的基础,反向迭代器也可以很快的完成,思路就是复用,用刚刚实现的正向迭代器稍加修改得到。
template<class T, class T1 = T>
struct List_reverse_iterator
{
typedef ListNode<T> Node;
typedef List_iterator<T, T1> iterator;
typedef List_reverse_iterator<T, T1> reverse_iterator;
List_reverse_iterator(Node* node)
:_it(node)
{}
List_reverse_iterator(iterator it)
:_it(it)
{}
reverse_iterator operator++()
{
return --_it;
}
reverse_iterator operator--()
{
return ++_it;
}
reverse_iterator operator++(int)
{
return _it--;
}
reverse_iterator operator--(int)
{
return _it++;
}
T1& operator*()//对于const T无法进行修改
{
return *_it;
}
T1* operator->()//对于const T无法进行修改
{
return &(*_it);
}
bool operator!=(const List_reverse_iterator lt) const
{
return _it != lt._it;
}
bool operator==(const List_reverse_iterator lt) const
{
return _it == lt._it;
}
iterator _it;
};
(4)push_back
push_back是非常关键的一个函数,在构造函数中,我们经常用到它
先用val创建一个newnode,找到尾节点并修改尾节点和哨兵位的_prev和_next使得newnode插入链表中
(5)insert
我们实现insert的方式是先实现一个可复用性最强的函数(迭代器版本的insert),再用它实现其它函数
后续的函数只需要调整参数复用即可
iterator insert(iterator pos, int n, int val)//特殊处理,防止定位错误
{
return insert(pos, (size_t)n, (T)val);
}
iterator insert(iterator pos, size_t n, const T& val)
{
list<T> tmp;
for (size_t count = 0; count < n; count++)
{
tmp.push_back(val);
}
return insert(pos, tmp.begin(), tmp.end());
}
iterator insert(iterator pos, const T& val)
{
return insert(pos, 1, val);
}
其中有一个函数需要解释
当我们调用insert(lt.begin(), 1, 3)时,是想在首位置插入1个3,但是编译器会将1,3识别成迭代器(注意看我们之前已经实现了模板函数,1和3同类型是会匹配的),size_t和const int&虽然都能作为1和3的类型,但它们都不叫完美匹配,不完美匹配就会去找模板函数。
注意各大整型家族的区别,10u是unsigned int类型。
因此我们只有针对这一种情况进行单独处理,将它们强转后就可以匹配正确的函数了。
(6)erase
我们的思路也是先实现一个可复用性最强的,再对其它函数复用。这些函数逻辑都很简单,不做过多讲解。
(7)带参构造
这里我们就可以发现,只要有了push_back,insert,我们实现这些带参构造就很快了,我们要学会复用,即实现最关键的几个函数,其它的函数用这几个核心的函数套用就可以了,这不仅节省了我们的时间,还使得代码易于维护,失误率降低。
list(size_t n, const T& val)
{
_head = new Node;
_head->_next = _head->_prev = _head;
for (size_t count = 0; count < n; count++)
{
push_back(val);
}
}
list(const list<T>& lt)
{
_head = new Node;
_head->_next = _head->_prev = _head;
for (const auto& e : lt)
{
push_back(e);
}
}
template<class Init>
list(Init first, Init last)
{
_head = new Node;
_head->_next = _head->_prev = _head;
insert(begin(), first, last);
}
(8)全部代码(包含一些边缘性的函数)
注意模板函数是按需实例化,只检查调用的实例化的函数,不调用就不实例化
#pragma once
#include <iostream>
#include <algorithm>
#include <assert.h>
using namespace std;
namespace my_list
{
template<class T>
struct ListNode
{
ListNode(const T& data = T())
:_data(data)
, _next(nullptr)
, _prev(nullptr)
{}
T _data;
ListNode<T>* _next;
ListNode<T>* _prev;
};
template<class T, class T1 = T>
struct List_iterator
{
typedef ListNode<T> Node;
typedef List_iterator<T, T1> iterator;
List_iterator(Node* node)
:_curnode(node)
{}
iterator operator++()
{
_curnode = _curnode->_next;
return _curnode;
}
iterator operator--()
{
_curnode = _curnode->_prev;
return _curnode;
}
iterator operator++(int)
{
_curnode = _curnode->_next;
return _curnode->_prev;
}
iterator operator--(int)
{
_curnode = _curnode->_prev;
return _curnode->_next;
}
T1& operator*()//对于const T无法进行修改
{
return _curnode->_data;
}
T1* operator->()//对于const T无法进行修改
{
return &(_curnode->_data);
}
bool operator!=(const List_iterator lt) const
{
return _curnode != lt._curnode;
}
bool operator==(const List_iterator lt) const
{
return _curnode == lt._curnode;
}
Node* _curnode;
};
template<class T, class T1 = T>
struct List_reverse_iterator
{
typedef ListNode<T> Node;
typedef List_iterator<T, T1> iterator;
typedef List_reverse_iterator<T, T1> reverse_iterator;
List_reverse_iterator(Node* node)
:_it(node)
{}
List_reverse_iterator(iterator it)
:_it(it)
{}
reverse_iterator operator++()
{
return --_it;
}
reverse_iterator operator--()
{
return ++_it;
}
reverse_iterator operator++(int)
{
return _it--;
}
reverse_iterator operator--(int)
{
return _it++;
}
T1& operator*()//对于const T无法进行修改
{
return *_it;
}
T1* operator->()//对于const T无法进行修改
{
return &(*_it);
}
bool operator!=(const List_reverse_iterator lt) const
{
return _it != lt._it;
}
bool operator==(const List_reverse_iterator lt) const
{
return _it == lt._it;
}
iterator _it;
};
template<class T>
class list
{
public:
typedef ListNode<T> Node;
typedef List_iterator<T> iterator;
typedef List_iterator<T, const T> const_iterator;
typedef List_reverse_iterator<T> reverse_iterator;
typedef List_reverse_iterator<T, const T> const_reverse_iterator;
list()
{
_head = new Node;
_head->_next = _head->_prev = _head;
}
list(size_t n, const T& val)
{
_head = new Node;
_head->_next = _head->_prev = _head;
for (size_t count = 0; count < n; count++)
{
push_back(val);
}
}
list(const list<T>& lt)
{
_head = new Node;
_head->_next = _head->_prev = _head;
for (const auto& e : lt)
{
push_back(e);
}
}
template<class Init>
list(Init first, Init last)
{
_head = new Node;
_head->_next = _head->_prev = _head;
insert(begin(), first, last);
}
~list()
{
Node* cur = _head->_next, * next = _head->_next->_next;
while (cur != _head)
{
delete cur;
cur = next, next = next->_next;
}
delete _head;
_head = nullptr;
}
size_t size()
{
size_t count = 0;
for (const auto& e : *this)
{
count++;
}
return count;
}
bool empty()
{
return _head->_next == _head;
}
list<T>& operator=(const list<T>& lt)
{
if (lt._head != _head)
{
clear();
for (const auto& e : lt)
{
push_back(e);
}
}
return *this;
}
T& front()
{
return *begin();
}
T& back()
{
return *(--end());
}
const T& front() const
{
return *begin();
}
const T& back() const
{
return *(--end());
}
void swap(const list<T>& lt)
{
std::swap(_head, lt._head);
}
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);
}
reverse_iterator rbegin()
{
return reverse_iterator(_head->_prev);
}
reverse_iterator rend()
{
return reverse_iterator(_head);
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(_head->_prev);
}
const_reverse_iterator rend() const
{
return const_reverse_iterator(_head);
}
void push_back(const T& val)
{
Node* newnode = new Node(val);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
void push_front(const T& val)
{
insert(begin(), 1, val);
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
template<typename Input>
iterator insert(iterator pos, Input first, Input last)
{
Node* cur = pos._curnode->_prev, * next = cur->_next;
Node* ret = cur;
while (first != last)
{
Node* newnode = new Node((T)(*first));
cur->_next = newnode, newnode->_prev = cur;
newnode->_next = next, next->_prev = newnode;
cur = cur->_next;
first++;
}
return ret->_next;//新插入的第一个结点对应的迭代器,有隐式类型转换
}
iterator insert(iterator pos, int n, int val)//特殊处理,防止定位错误
{
return insert(pos, (size_t)n, (T)val);
}
iterator insert(iterator pos, size_t n, const T& val)
{
list<T> tmp;
for (size_t count = 0; count < n; count++)
{
tmp.push_back(val);
}
return insert(pos, tmp.begin(), tmp.end());
}
iterator insert(iterator pos, const T& val)
{
return insert(pos, 1, val);
}
iterator erase(iterator first, iterator last)
{
Node* cur = first._curnode->_prev, * next = last._curnode;
iterator nextit = first;
while (first != last)
{
nextit++;
delete first._curnode, first = nextit;
}
cur->_next = next, next->_prev = cur;
return cur->_next;
}
iterator erase(iterator pos)
{
iterator cur = pos;
return erase(pos, ++cur);
}
void clear()
{
erase(begin(), end());
}
private:
Node* _head;
};
}