List
List的介绍
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。
- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高 效。
- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率 更好。
- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间
开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这 可能是一个重要的因素
List的使用
list中的接口比较多,此处类似,只需要掌握如何正确的使用,然后再去深入研究背后的原理,已达到可扩展的能力。以下为list中一些常见的重要接口。
list iterator的使用:
【注意】
- begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
- rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
list capacity:
list element access:
List的测试
void test_list1()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void test_list2()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(2);
lt.push_back(2);
lt.push_back(4);
lt.push_back(5);
lt.push_back(2);
lt.push_back(4);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.reverse();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
//lt.sort();
lt.sort(greater<int>());
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.unique();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void test_list3()
{
list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.splice(lt.end(), lt, lt.begin());
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int> lt1;
lt1.push_back(10);
lt1.push_back(20);
lt1.push_back(30);
lt1.push_back(40);
/*lt.splice(lt.begin(),lt);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;*/
lt.splice(lt.begin(), lt1);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
/*for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;*/
}
List的模拟实现
List的节点
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_prev(nullptr)
, _next(nullptr)
, data(val)
{
}
ListNode<T>* _prev;//为什么是ListNode<T>*的指针呢?因为我们将来要指向
//ListNode<T>类型的节点。
ListNode<T>* _next;
T data;
};
List的构建
双向带头链表,所以list一定要有头结点。
template<class T>
class list
{
public:
typedef ListNode<T> Node;
private:
Node* _head;//头结点是一个节点指针类型。
size_t _size;//因为库中有size这个接口,这里存一个会方便一点。
};
};
List的构造
list()
{
empty_init();
//刚创建一个链表的时候,没有插入任何数据,我们需要让他指向自己
}
尾插push_back:
void push_back(const T& val)
{
Node* node = new Node(val);//申请节点
Node* tail = _head->_prev;//如何找到4?是_head->prev!保存起来方便我们使用。
node->_prev = tail;//链接
node->_next = _head;
tail->_next = node;
_head->_prev = node;
_size++;//修改size
}
尾删pop_back:
先找到尾节点,然后保存一起来(方便我们删除)。然后将指针链接到尾删的前一个值然后保存一起来(方便我们删除)。然后将指针链接到尾删的前一个值。
void pop_back()
{
Node* tail = _head->_prev;//保存
_head->_prev = tail->_prev;//更改指向
tail->_prev->_next = _head;
delete tail;//删除节点
_size--;//别忘了修改个数
}
节点个数:size()
size_t size()
{
return _size;
}
判断是否为空:empty()
bool empty()
{
return _size == 0;
}
迭代器:
template<class T>
class List_Iterator
{
public:
typedef ListNode<T> Node;
Node* _node;
List_Iterator(Node* node)
:_node(node)
{}
};
begin(和)end()
begin()是返回数据的开始和end()返回头结点(有效数据的下一个)。
template<class T>
class list
{
public:
typedef ListNode<T> Node;
iterator begin()
{
//这里存在单参数构造函数的隐式类型转换
//按道理 return iterator(_head->next);
//应该是这样的,匿名对象,但是单参数的构造函数可以进行隐式类型转换。
return _head->_next;
}
iterator end()
{
return _head;
}
private:
Node* _head;//头结点是一个节点指针类型。
size_t _size;//因为库中有size这个接口,这里存一个会方便一点。
};
operator*():
打印节点的数值。
T& operator* ()
{
return _node->data;
}
前置++ operator++():
List_Iterator<T>& operator++()
{
_node= _node->_next;
return *this;
}
后置++ operator++(int):
后置++,就是我们创建一个临时变量,然后返回临时变量,但是让实际已经指向下一个节点了。注:不能返回引用,因为是临时变量。
List_Iterator<T> operator++(int)
{
List_Iterator<T> tmp(*this);
_node = _node->_next;
return tmp;
}
前置-- operator–():
List_Iterator<T> & operator--()
{
_node= _node->_prev;
return *this;
}
后置-- operator–(int)
List_Iterator<T> operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
重载不等于 operator!=(const Self& rhs):
bool operator!=(const Self& rhs )
{
return rhs._node != _node;
}
重载箭头 operator->():
T* operator->()
{
return &_node->data;
}
const迭代器
以上我们都是写的普通的迭代器,那么如果我们要写const的迭代器是要重新写一个。
template<class T>
class List_ConstIterator
{
public:
typedef ListNode<T> Node;
typedef List_ConstIterator<T> Self;
Node* _node;
List_ConstIterator(Node* node)
:_node(node)
{}
//前置++
Self& operator++()
{
_node = _node->_next;
return *this;
}
//后置++
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
//前置--
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//后置--
Self operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& rhs)
{
return rhs._node != _node;
}
bool operator==(const Self& rhs)
{
return _node == rhs._node;
}
const T& operator* ()
{
return _node->data;
}
const T* operator->()
{
return &_node->data;
}
};
但是这个迭代器的代码和普通迭代器的代码有很多相似之处,所以我们会觉得代码冗余,那么我们是不是可以利用类模板写呢?事实证明是可以的。
template<class T ,class Ref ,class Ptr>
class List_Iterator
{
public:
typedef ListNode<T> Node;
typedef List_Iterator< T, Ref, Ptr> Self;
Node* _node;
List_Iterator(Node* node)
:_node(node)
{}
//前置++
Self& operator++()
{
_node= _node->_next;
return *this;
}
//后置++
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
//前置--
Self& operator--()
{
_node= _node->_prev;
return *this;
}
//后置--
Self operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& rhs )
{
return rhs._node != _node;
}
bool operator==(const Self& rhs)
{
return _node == rhs._node;
}
Ptr operator->()
{
return &_node->data;
}
Ref operator* ()
{
return _node->data;
}
};
在某个位置前插入 insert():
iterator insert(iterator pos, const T& val)
{
Node* node = new Node(val);
Node* prev=pos->prev;
node->prev=prev;
node->next=pos;
pos->prev=node;
prev->next=node;
_size++;
return _head
}
最后记得节点的个数+1。
删除某位置的值 erase():
库中要求返回被删除节点的下一个迭代器。
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return next;
}
清空链表 clear():
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
拷贝构造:
拷贝构造,由于你的头指针没有初始化,都是空,插入直接空指针访问,所以不能用push_back。所以你需要初始化。
//lt1(lt2)
list(const list<T>& lt)
{
empty_init();//初始化头结点。
for (auto& e : lt)
{
push_back(e);
}
iterator it = lt.begin();
}
析构函数:
~list()
{
clear();//复用
delete _head;
}
整体代码展现:
#include<iostream>
#include<assert.h>
namespace bit {
template<class T>
struct ListNode
{
ListNode(const T& val = T())
:_prev(nullptr)
, _next(nullptr)
, data(val)
{
}
ListNode<T>* _prev;//为什么是ListNode<T>*的指针呢?因为我们将来要指向
//ListNode<T>类型的节点。
ListNode<T>* _next;
T data;
};
template<class T, class Ref, class Ptr>
class List_Iterator
{
public:
typedef ListNode<T> Node;
typedef List_Iterator< T, Ref, Ptr> Self;
Node* _node;
List_Iterator(Node* node)
:_node(node)
{}
//前置++
Self& operator++()
{
_node = _node->_next;
return *this;
}
//后置++
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
//前置--
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//后置--
Self operator--(int)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& rhs)
{
return rhs._node != _node;
}
Ref operator* ()
{
return _node->data;
}
bool operator==(const Self& rhs)
{
return _node == rhs._node;
}
Ptr operator->()
{
return &_node->data;
}
};
//template<class T>
//class List_ConstIterator
//{
//public:
// typedef ListNode<T> Node;
// typedef List_ConstIterator<T> Self;
// Node* _node;
// List_ConstIterator(Node* node)
// :_node(node)
// {}
// //前置++
// Self& operator++()
// {
// _node = _node->_next;
// return *this;
// }
// //后置++
// Self operator++(int)
// {
// Self tmp(*this);
// _node = _node->_next;
// return tmp;
// }
// //前置--
// Self& operator--()
// {
// _node = _node->_prev;
// return *this;
// }
// //后置--
// Self operator--(int)
// {
// Self tmp(*this);
// _node = _node->_prev;
// return tmp;
// }
// bool operator!=(const Self& rhs)
// {
// return rhs._node != _node;
// }
// const T& operator* ()
// {
// return _node->data;
// }
// bool operator==(const Self& rhs)
// {
// return _node == rhs._node;
// }
// const T* operator->()
// {
// return &_node->data;
// }
//};
template<class T>
class list
{
public:
typedef ListNode<T> Node;
typedef List_Iterator<T, T&, T*> iterator;
typedef List_Iterator<T, const T&, const T*> const_iterator;
//typedef List_Iterator<T> iterator;
//typedef List_ConstIterator<T> const_iterator;
void empty_init()
{
_head = new Node;//给头一个空间
_head->_prev = _head;
_head->_next = _head;
_size = 0;
}
list()
{
empty_init();
//刚创建一个链表的时候,没有插入任何数据,我们需要让他指向自己
}
~list()
{
clear();
delete _head;
}
//lt1(lt2)
list(const list<T>& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
iterator it = lt.begin();
//类型不明确,用范围for更好
//auto it = lt.begin();//这样也可以
//while (it != lt.end())
//{
// push_back(*it);
// it++;
//}
}
void push_back(const T& val)
{
/*Node* node = new Node(val);
Node* tail = _head->_prev;
node->_prev = tail;
node->_next = _head;
tail->_next = node;
_head->_prev = node;*/
insert(end(), val);
}
void pop_back()
{
/*Node* tail = _head->_prev;
_head->_prev = tail->_prev;
tail->_prev->_next = _head;
delete tail;*/
erase(--end());
}
//当我们想使用insert的时候,发现需要迭代器那么我们先实现迭代器,怎么做呢?
iterator begin()
{
//这里存在单参数构造函数的隐式类型转换
//
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin()const
{
//这里存在单参数构造函数的隐式类型转换
return _head->_next;
}
const_iterator end()const
{
return _head;
}
//目前测试的迭代器没有大碍,那么就写insert
iterator insert(iterator pos, const T& val)
{
Node* node = new Node(val);
Node* prev=pos->prev;
node->prev=prev;
node->next=pos;
pos->prev=node;
prev->next=node;
_size++;
return _head
}
iterator erase(iterator pos)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return next;
}
size_t size()
{
return _size;
}
bool empty()
{
return _size == 0;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
_size = 0;
}
private:
Node* _head;
size_t _size;
};
}