目录
一. list类的简介
二. list类的使用方法
2.1 构造函数的使用
2.2 迭代器相关函数的使用
2.3 容量相关函数的使用
2.4 增删查改相关函数的使用
2.5 其余操作函数
三. list的模拟实现
3.1 链表节点和list成员变量
3.2 构造函数的模拟实现
3.3 析构函数的模拟实现
3.4 数据插入和删除相关函数的模拟实现
3.5 链表数据量相关函数的模拟实现
3.6 正向迭代器的模拟实现
3.7 反向迭代器的模拟实现
附录:list的模拟实现完整代码
list,是C++标准类模板中提供的带头双向循环链表的数据结构容器,其抽象结构图如1.1所示。相比于单链表每个节点只存储一个指向后一个节点的指针,带头双向链表首先会有一个哨兵卫头结点(不存储任何有效数据),而且每个节点中还会有一个向前的指针。list最大的优势在于:可以在O(1)的时间复杂度内,删除任意节点的数据。
有四种常用的构造函数重载形式:
演示代码2.1:
int main()
{
list lt1(); //构造空链表
list lt2(3, 6); //构造出含有3个6的链表
int arr[5] = { 1,2,3,4,5 };
list lt3(arr, arr + 5); //使用迭代器区间初始化
list lt4(lt3); //拷贝构造
return 0;
}
list类给出了四种迭代器,分别为:
list迭代器涉及的函数主要有:begin、end、rbegin和rend,可用于实现list的正向和反向遍历,这四个函数的返回值可以理解为节点指针,指向位置见图2.1。但是,其在STL源码中被实现为了类,这样做的原因是list节点在内存中并不是连续分布的,指向++或--等操作无法找到下一个节点,但在类中就可以通过运算符重载找到下一个节点,同时,对iterator迭代器进行类封装也有利于反向迭代器的实现,第三章模拟实现会对此进行详解。
演示代码2.2:
int main()
{
int arr[5] = { 1,2,3,4,5 };
list lt1(arr, arr + 5); //使用迭代器区间初始化
//正向迭代遍历
list::iterator it = lt1.begin();
while (it != lt1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//反向迭代遍历
list::reverse_iterator rit = lt1.rbegin();
while (rit != lt1.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
return 0;
}
演示代码2.3 :
int main()
{
list lt1; //空链表
list lt2(10, 5); //含有10个5
cout << "Is list1 empty: " << lt1.empty() << endl;
cout << "Is list2 empty: " << lt2.empty() << endl; //链表1、2是否为空
cout << "list1 size: " << lt1.size() << endl;
cout << "list2 size: " << lt2.size() << endl; //获取链表1、2中数据个数
lt2.resize(3, 8); //将链表2中的数据删减到3个
cout << "size of list2 after resize: " << lt2.size() << endl;
lt2.resize(5, 1); //将链表2的数据扩大到5个
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
return 0;
}
头插、头删、尾插、尾删
注意:使用pop_front和pop_back函数时,一定要保证链表不为空,否则程序会崩溃报错。
演示代码2.4:
int main()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3); //尾插数据函数
lt.push_front(1);
lt.push_front(2);
lt.push_front(3); //头插数据函数
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
lt.pop_back(); //尾删数据函数
lt.pop_front(); //头删数据函数
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
在特定位置插入或删除数据
insert函数和pos函数一般配合find函数使用来找到插入或删除数据的位置。
演示代码2.5:
int main()
{
list lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5); //尾插数据
list::iterator pos = find(lt.begin(), lt.end(), 2);
pos = lt.insert(pos, 10);
pos = lt.insert(pos, 20); //在pos之前插入数据
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
pos = lt.erase(pos);
pos = lt.erase(pos); //删除pos位置处的数据
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
return 0;
}
删除特定数据
演示代码2.6:
bool isOdd(const int& val)
{
return val % 2 == 1;
}
int main()
{
list lt1;
lt1.push_back(1);
lt1.push_back(2);
lt1.push_back(1);
lt1.push_back(3);
lt1.push_back(5); //尾插数据
//remove函数 -- 删除链表中的特定值
lt1.remove(1);
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
//remove_if函数 --删除链表中满足特定条件的值
list lt2;
lt2.push_back(1);
lt2.push_back(2);
lt2.push_back(3);
lt2.push_back(4);
lt2.remove_if(isOdd); //删除奇数
for (auto e : lt2)
{
cout << e << " ";
}
cout << endl;
return 0;
}
演示代码2.7:
int main()
{
list lt1;
lt1.push_back(1);
lt1.push_back(5);
lt1.push_back(2);
lt1.push_back(2);
lt1.push_back(8);
lt1.push_back(8);
lt1.sort(); //排升序
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt1.unique(); //去除重复元素
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
lt1.reverse(); //逆置
for (auto e : lt1)
{
cout << e << " ";
}
cout << endl;
return 0;
}
首先,模拟实现list类应当定义出链表的节点,这里将链表的节点定义为struct类型的结构体/类,其中包含3个成员变量,分别为:
还包含一个默认构造函数,将prev指针和next指针都初始化为nullptr。在list类中,将链表节点类型重命名为Node,list仅有一个成员变量:Node _head -- 链表头结点。
双向链表节点ListNode的定义:
template
struct ListNode
{
T _data; //节点数据
ListNode* _next; //指向下一个节点的指针
ListNode* _prev; //指向前一个节点的指针
ListNode(const T& x = T())
: _next(nullptr)
, _prev(nullptr)
, _data(x)
{ }
};
在list类中,链表节点被类型重定义为Node:typedef ListNode
explicit list() //构造空链表
{
//创建哨兵卫头结点
_head = new Node; //节点获取
_head->_next = _head;
_head->_prev = _head;
}
//使用迭代器区间初始化
template
explicit list(InputIterator first, InputIterator last)
{
//1.开辟哨兵卫头结点
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//2.将迭代器区间中的数据依次尾插到链表中
while (first != last)
{
push_back(*first);
++first;
}
}
explicit list(const list& lt) //拷贝构造函数
{
//1.开辟哨兵卫头结点
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//2.使用迭代器区间,构造一份与lt相同的临时对象
list tmp(lt.begin(), lt.end());
//3.交换*this和tmp中头结点的指向位置
std::swap(_head, tmp._head);
}
void clear() //清理链表中所有存储有效数据的节点
{
Node* cur = _head->_next;
while (cur != _head)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
}
~list()
{
clear(); //调用clear函数清理除头结点以外的节点
delete _head; //释放哨兵卫头结点
_head = nullptr;
}
iterator insert(iterator pos, const T& x) //在pos位置之前插入数据
{
Node* node = pos.GetNode();
Node* prev = node->_prev; //在prev和node之间插入数据即可
Node* newNode = new Node(x);
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = node;
node->_prev = newNode;
return iterator(newNode);
}
iterator erase(iterator pos) //删除pos位置处的函数
{
Node* del = pos.GetNode(); //要删除的节点
assert(del != _head); //保证哨兵卫头结点不能被删除
Node* prev = del->_prev;
Node* next = del->_next; //被删除del节点的前后节点
//删除del节点,prev节点与next节点相连
delete del;
del = nullptr;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
这样,尾插数据函数push_back和头插数据函数push_front就可以通过复用insert函数 来实现,同理,尾删数据函数pop_back和头删pop_front函数则可以通过复用erase函数来实现。
注意:模拟实现pop_back和pop_front函数时,要先断言链表不为空。
void push_back(const T& x) //尾插数据函数
{
/*Node* tail = _head->_prev; //尾结点
Node* newNode = new Node(x); //新节点
//在尾结点后面插入数据即可
tail->_next = newNode;
newNode->_prev = tail;
newNode->_next = _head;
_head->_prev = newNode;*/
insert(end(), x);
}
void push_front(const T& x) //头插数据函数
{
insert(begin(), x);
}
bool empty()
{
return _head->_next == _head;
}
void pop_back() //尾删数据函数
{
assert(!empty());
erase(--end()); //--end()返回尾结点
}
void pop_front() //头删数据函数
{
assert(!empty());
erase(begin()); //begin返回第一个存储有效数据节点的位置
}
size_t size() //获取链表中存储有效数据的节点个数
{
size_t count = 0;
Node* cur = cur->_next;
while (cur != _head)
{
++count;
cur = cur->_next;
}
return count;
}
void resize(size_t n, const T& x = T()) //删除节点 或 以数个值为x的节点延长链表
{
size_t i = 0;
//找到链表的第n + 1个节点或尾结点
Node* cur = _head->_next;
for (i = 0; i < n; ++i)
{
if (cur == _head)
break;
cur = cur->_next;
}
if (cur != _head)
{
//节点的个数大于n个,要删除从cur往后的所有节点
iterator it = iterator(cur);
while (it != _head)
{
it = erase(it);
}
}
else
{
//原链表中的节点大于或等于n个
for (size_t j = i; j < n; ++j)
{
//尾插数据到含有n个节点
push_back(x);
}
}
}
由于链表中每个节点在内存中不是连续分布的,所以,如果简单地将迭代器定义为链表节点ListNode的原生指针,那么++和--操作就不能实现找到后一个节点和前一个节点的目的,同时,解引用操作也就不能获取节点中的数据。
C++类对象支持运算符重载函数,因此,我们可以将迭代器封装到一个名为__list_iterator的类中。__list_iterator类通过类模板,可以同时应用于普通对象和const对象,其中仅包含一个成员变量_node,为链表节点的指针。同时,其中还重载了多个运算符。
在class list中,还存在着下面的类型重定义:
这里要特别注意->运算符的重载,Ref operator->()当且仅当T表示自定义类型数据时才有意义,假设迭代器为it,理论上的结构体成员访问应当为 it->->(结构体成员变量),但编译器对此进行了优化,只需要 it->(结构体成员变量) 即可。
template
class __list_iterator
{
typedef ListNode Node;
typedef __list_iterator self;
public:
__list_iterator(Node* node)
: _node(node)
{ }
Node* GetNode()
{
return _node;
}
Ref operator*() //解引用函数重载
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
self& operator++() //前置++函数重载
{
_node = _node->_next;
return *this;
}
self operator++(int) //后置++函数重载
{
self tmp(_node);
_node = _node->_next;
return tmp;
}
self& operator--() //前置++函数重载
{
_node = _node->_prev;
return *this;
}
self operator--(int) //前置++函数重载
{
self tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& it) const //不等判断函数
{
return _node != it._node;
}
bool operator==(const self& it) const // 相对判断函数
{
return _node == it._node;
}
private:
Node* _node; //成员变量 -- 节点的地址
};
在class类中,还对begin()、end()进行了定义,begin()返回第一个存储了数据的节点,end()返回头节点。
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
反向迭代器,是在正向迭代器的基础之上实现的,其也被封装到了一个名为reverse_iterator的类中,这个类中也只有一个成员变量:_it -- 类型为整形迭代器。
template
class reverse_iterator
{
typedef reverse_iterator self;
public:
reverse_iterator(Iterator it) //构造函数
: _it(it)
{ }
Ref operator*() //解引用函数重载
{
Iterator prev = _it;
return *--prev;
}
self& operator++() //前置++函数重载
{
--_it;
return *this;
}
self operator++(int) //后置++函数重载
{
self tmp(_it);
--_it;
return tmp;
}
self& operator--() //前置--函数重载
{
++_it;
return *this;
}
self operator--(int)
{
self tmp(_it);
++_it;
return tmp;
}
bool operator!=(const self& it) const
{
return _it != it._it;
}
bool operator==(const self& it) const
{
return _it == it._it;
}
private:
Iterator _it; //正向迭代器
};
class list中对rend和rbegin的定义:
typedef reverse_iterator const_reverse_iterator;
typedef reverse_iterator reverse_iterator;
const_iterator end() const
{
return _head;
}
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
//reverse_iterator.h文件
#pragma once
namespace zhang
{
template
class reverse_iterator
{
typedef reverse_iterator self;
public:
reverse_iterator(Iterator it) //构造函数
: _it(it)
{ }
Ref operator*() //解引用函数重载
{
Iterator prev = _it;
return *--prev;
}
self& operator++() //前置++函数重载
{
--_it;
return *this;
}
self operator++(int) //后置++函数重载
{
self tmp(_it);
--_it;
return tmp;
}
self& operator--() //前置--函数重载
{
++_it;
return *this;
}
self operator--(int)
{
self tmp(_it);
++_it;
return tmp;
}
bool operator!=(const self& it) const
{
return _it != it._it;
}
bool operator==(const self& it) const
{
return _it == it._it;
}
private:
Iterator _it; //正向迭代器
};
}
//list.h文件
#pragma once
#include
#include
#include "reverse_iterator.h"
namespace zhang
{
template
struct ListNode
{
T _data; //节点数据
ListNode* _next; //指向下一个节点的指针
ListNode* _prev; //指向前一个节点的指针
ListNode(const T& x = T())
: _next(nullptr)
, _prev(nullptr)
, _data(x)
{ }
};
template
class __list_iterator
{
typedef ListNode Node;
typedef __list_iterator self;
public:
__list_iterator(Node* node)
: _node(node)
{ }
Node* GetNode()
{
return _node;
}
Ref operator*() //解引用函数重载
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
self& operator++() //前置++函数重载
{
_node = _node->_next;
return *this;
}
self operator++(int) //后置++函数重载
{
self tmp(_node);
_node = _node->_next;
return tmp;
}
self& operator--() //前置++函数重载
{
_node = _node->_prev;
return *this;
}
self operator--(int) //前置++函数重载
{
self tmp(_node);
_node = _node->_prev;
return tmp;
}
bool operator!=(const self& it) const //不等判断函数
{
return _node != it._node;
}
bool operator==(const self& it) const // 相对判断函数
{
return _node == it._node;
}
private:
Node* _node; //成员变量 -- 节点的地址
};
template
class list
{
public:
typedef ListNode Node;
typedef __list_iterator iterator;
typedef __list_iterator const_iterator;
typedef reverse_iterator const_reverse_iterator;
typedef reverse_iterator reverse_iterator;
explicit list() //构造空链表
{
//创建哨兵卫头结点
_head = new Node; //节点获取
_head->_next = _head;
_head->_prev = _head;
}
//使用迭代器区间初始化
template
explicit list(InputIterator first, InputIterator last)
{
//1.开辟哨兵卫头结点
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//2.将迭代器区间中的数据依次尾插到链表中
while (first != last)
{
push_back(*first);
++first;
}
}
explicit list(const list& lt) //拷贝构造函数
{
//1.开辟哨兵卫头结点
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//2.使用迭代器区间,构造一份与lt相同的临时对象
list tmp(lt.begin(), lt.end());
//3.交换*this和tmp中头结点的指向位置
std::swap(_head, tmp._head);
}
//赋值函数
list& operator=(list lt)
{
std::swap(_head, lt._head);
return *this;
}
void clear() //清理链表中所有存储有效数据的节点
{
Node* cur = _head->_next;
while (cur != _head)
{
Node* next = cur->_next;
delete cur;
cur = next;
}
}
~list()
{
clear(); //调用clear函数清理除头结点以外的节点
delete _head; //释放哨兵卫头结点
_head = nullptr;
}
iterator begin()
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
reverse_iterator rbegin()
{
return reverse_iterator(end());
}
reverse_iterator rend()
{
return reverse_iterator(begin());
}
const_reverse_iterator rbegin() const
{
return const_reverse_iterator(end());
}
void push_back(const T& x) //尾插数据函数
{
/*Node* tail = _head->_prev; //尾结点
Node* newNode = new Node(x); //新节点
//在尾结点后面插入数据即可
tail->_next = newNode;
newNode->_prev = tail;
newNode->_next = _head;
_head->_prev = newNode;*/
insert(end(), x);
}
void push_front(const T& x) //头插数据函数
{
insert(begin(), x);
}
bool empty()
{
return _head->_next == _head;
}
void pop_back() //尾删数据函数
{
assert(!empty());
erase(--end()); //--end()返回尾结点
}
void pop_front() //头删数据函数
{
assert(!empty());
erase(begin()); //begin返回第一个存储有效数据节点的位置
}
iterator insert(iterator pos, const T& x) //在pos位置之前插入数据
{
Node* node = pos.GetNode();
Node* prev = node->_prev; //在prev和node之间插入数据即可
Node* newNode = new Node(x);
prev->_next = newNode;
newNode->_prev = prev;
newNode->_next = node;
node->_prev = newNode;
return iterator(newNode);
}
iterator erase(iterator pos) //删除pos位置处的函数
{
Node* del = pos.GetNode(); //要删除的节点
assert(del != _head); //保证哨兵卫头结点不能被删除
Node* prev = del->_prev;
Node* next = del->_next; //被删除del节点的前后节点
//删除del节点,prev节点与next节点相连
delete del;
del = nullptr;
prev->_next = next;
next->_prev = prev;
return iterator(next);
}
size_t size() //获取链表中存储有效数据的节点个数
{
size_t count = 0;
Node* cur = cur->_next;
while (cur != _head)
{
++count;
cur = cur->_next;
}
return count;
}
void resize(size_t n, const T& x = T()) //删除节点 或 以数个值为x的节点延长链表
{
size_t i = 0;
//找到链表的第n + 1个节点或尾结点
Node* cur = _head->_next;
for (i = 0; i < n; ++i)
{
if (cur == _head)
break;
cur = cur->_next;
}
if (cur != _head)
{
//节点的个数大于n个,要删除从cur往后的所有节点
iterator it = iterator(cur);
while (it != _head)
{
it = erase(it);
}
}
else
{
//原链表中的节点大于或等于n个
for (size_t j = i; j < n; ++j)
{
//尾插数据到含有n个节点
push_back(x);
}
}
}
void Print()
{
iterator it = begin();
while (it != end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
}
private:
Node* _head;
};
}