1、list的介绍和使用
1.1 list介绍
list是一个双向带头循环链表
该容器非常适合在任意位置插入和删除,时间复杂度都是O(1)
与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问
1.2 list的常见的重要接口
构造函数
和vector构造差不多
其他的函数也一样
迭代器begin和end: 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin和rend: 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置
cbegin、cend以及crbegin、crend在前面的基础上进行const修饰
empty 判断list是否为空
size 返回list中有效节点个数
front 返回list的第一个节点中值的引用
back 返回list的最后一个节点中值的引用
push_front 头插 pop_front 头删 push_back 尾插 pop_back尾删
insert 在pos位置插入一个值为val的元素
erase删除pos位置的元素
swap 交换两个list中的元素
clear 清除list中的有效元素
1.3 遍历方式
#include <iostream>
#include <list>
using namespace std;
void Test()
{
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;
list<int>::reverse_iterator rit = lt.rbegin();
while (rit != lt.rend())
{
cout << *rit << ' ';
rit++;
}
cout << endl;
}
int main()
{
Test();
return 0;
}
1.4 常见接口函数的使用
void Test1()
{
list<int> lt;
lt.push_front(1);
lt.push_front(3);
lt.push_front(5);
lt.push_front(7);
lt.push_front(9);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
list<int>::iterator it = find(lt.begin(), lt.end(), 3);
lt.insert(it, 2);
lt.erase(it);
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_back();
lt.pop_front();
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
cout << lt.size();
}
2、list的模拟实现
2.1 基本框架
2.1.1 节点、list及其构造
list是个双向带头循环列表,要定义其节点以及其哨兵位头节点
list的节点类
// List的节点类
template<class T>
struct ListNode
{
ListNode<T>* _prev;
ListNode<T>* _next;
T _val;
ListNode(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
节点类设计完后我们就可以开始实现list类
前面说了要用哨兵位头节点去带
namespace xxx
{
// List的节点类
template<class T>
struct ListNode
{
ListNode<T>* _prev;
ListNode<T>* _next;
T _val;
ListNode(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
template<class T>
class list
{
typedef ListNode<T> Node;
public:
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
private:
Node* _head;
};
};
哨兵位头节点是指向list的所以是指针,构造时最开始只有头节点所以new一个空间然后头节点的前后都指向自己
2.1.2 push_back
后面其实可以直接调用insert,在end()位置插入就相当于尾插了
void push_back(const T& x)
{
Node* newnode = new Node(x);
Node* tail = _head->_prev;
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
2.2 迭代器的实现
2.2.1 构造
list 的重点是迭代器,因为这里的迭代器的实现和我们之前讲的实现方式都不同。
我们之前讲的string和vector的迭代器都是一个原生指针,实现起来是非常简单的。
但是list是一个链表,你的迭代器还能这样去实现吗?在空间上不是连续的,如何往后走?
也就是++不能只指向后面的位置,应该重载++;且解引用*也没办法取到list中的数据val也要重载
template<class T>
struct __list_iterator
{
typedef ListNode<T> Node;
Node* _node;
__list_iterator(Node* x)
:_node(x)
{}
};
迭代器可以理解为指向某个节点的指针,所以我这就用指针定义了
构造就只构造这一个就行了
2.2.2 重载
++、前置++、--、前置--
typedef __list_iterator self;
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;
}
解引用
T& operator*()
{
return _node->_val;
}
T* operator->()
{
return &(operator*());
}
==和!=
bool operator!=(const Self& l)
{
return _node != l._node;
}
bool operator==(const Self& l)
{
return _node == l._node;
}
2.2.3 begin()和end()
template<class T>
class list
{
typedef ListNode<T> Node;
typedef __list_iterator<T> iterator;
public:
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
iterator begin()
{
//哨兵位头节点的下一节点才是头
return _head->_next;
}
iterator end()
{
return _head;
}
private:
Node* _head;
};
2.2.4const迭代器
普通迭代器访问普通对象,可读可写;const 迭代器访问 const 对象,可读但不可写。
所以这里自然是需要实现 const 迭代器,即实现一个 "可读但不可写" 的迭代器。(可以 ++ 可以解引用,但解引用的时候不能修改)所以直接在 __list_iterator 里面重载一个 const 类型的 operator* 解决不了问题,我们得重新实现一个 __const_list_iterator 出来。
在迭代器类定义templa模板时添加两个参数 typedef记得改
与此同时list类的传参和typedef也得改
这时候list_print就可以跑了
2.3 list的增删、深浅拷贝及析构
2.3.1增删
insert在pos前插入
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return newnode;
}
头插、尾插
void push_front(const T& x)
{
insert(begin(), x);
}
void push_back(const T& x)
{
//Node* newnode = new Node(x);
//Node* tail = _head->_prev;
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
erase删pos位置的节点返回该节点的下一个位置
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return next;
}
头删尾删
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
2.3.2 深浅拷贝
拷贝构造
//拷贝构造
list(list<T>& lt)
{
empty_init();
for (const auto& e : lt)
{
push_back(e);
}
}
赋值重载
void swap(list<T>& tmp)
{
std::swap(_head, tmp._head);
}
list<T>& operator=(list <T><)
{
swap(lt);
return *this;
}
2.3.3 clear和析构
void clear()
{
iterator it = begin();
while (it!=end())
{
it = erase(it);
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
2.4 代码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace xxx
{
// List的节点类
template<class T>
struct ListNode
{
ListNode<T>* _prev;
ListNode<T>* _next;
T _val;
ListNode(const T& val = T())
:_next(nullptr)
, _prev(nullptr)
, _val(val)
{}
};
template<class T,class Ref,class Ptr>
struct __list_iterator
{
typedef ListNode<T> Node;
typedef __list_iterator<T,Ref,Ptr> self;
Node* _node;
__list_iterator(Node* x)
:_node(x)
{}
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;
}
Ref operator*()
{
return _node->_val;
}
Ptr operator->()
{
return &(operator*());
}
bool operator!=(const self& l)
{
return _node != l._node;
}
bool operator==(const self& l)
{
return _node == l._node;
}
};
template<class T>
class list
{
typedef ListNode<T> Node;
public:
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
list()
{
empty_init();
}
void empty_init()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
//拷贝构造
list(list<T>& lt)
{
empty_init();
for (const auto& e : lt)
{
push_back(e);
}
}
void swap(list<T>& tmp)
{
std::swap(_head, tmp._head);
}
list<T>& operator=(list <T><)
{
swap(lt);
return *this;
}
void clear()
{
iterator it = begin();
while (it!=end())
{
it = erase(it);
}
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
iterator begin()
{
//哨兵位头节点的下一节点才是头
return _head->_next;
}
const_iterator begin()const
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator end()const
{
return _head;
}
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
return newnode;
}
void push_front(const T& x)
{
insert(begin(), x);
}
void push_back(const T& x)
{
//Node* newnode = new Node(x);
//Node* tail = _head->_prev;
//tail->_next = newnode;
//newnode->_prev = tail;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
iterator erase(iterator pos)
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return next;
}
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
private:
Node* _head;
};
void list_print(const list<int>& lt)
{
list<int>::const_iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << ' ';
it++;
}
cout << endl;
}
};