STL::list插入介绍及自实现插入功能
上一篇文章C++ STL::list常用操作及底层实现(上)——实现list迭代器中,我们实现了链表的迭代器,限于篇幅,STL::list的常用操作将分开讲解,本篇只介绍插入操作。
提示:为了文章的简洁,在介绍完每一个函数的具体用法后,我都直接贴出了实现的代码。所依赖的几个类的定义会在文末连同实现的插入函数一起给出,若是想先看看类的定义,可先转到文末再回过头来看。
1插入数据之insert():
1.1 iterator insert(iterator position,const value_type & val);
- 解释:在position位置前插入val值,并返回插入值位置的迭代器
- 例子:如有一个链表l:1<->2<->3。执行insert(l.begin(),4)后链表变为:4<->1<->2<->3。返回的迭代器指向元素4。
代码实现及示意图:
template <typename T>
Iterator<T> Mylist<T>::Insert(Iterator<T> it, const T& _data)
{
ListNode<T> *newNode = new ListNode<T>(_data);//生成一个节点
//1.确定插入节点的指向
newNode->leftlink = it.current->leftlink;
newNode->rightlink = it.current;
//2.改变迭代器it指向节点的指向
it.current->leftlink = newNode;
//3.改变插入节点的前一个节点的指向
newNode->leftlink->rightlink = newNode;
return Iterator<T>(newNode);//返回插入节点的位置
}
1.2. iterator insert(iterator position,size_type n,const value_type & val);
- 解释:在position位置前插入n个val值,并返回最前面值的迭代器
- 如有一个链表l:1<->2<->3。执行insert(l.begin(),3,9)后链表变为:9<->9<->9<->1<->2<->3。返回的迭代器指向最左边的元素9。
代码实现:
template <typename T>
Iterator<T> Mylist<T>::Insert(Iterator<T> it, int n, const T& _data)
{
for (; n>0; --n)
it = Insert(it, _data); //每次插完一个数据,返回插入数据的迭代器,并以其位置为参考继续插入
return it;
}
直接调用第一个格式的insert()来实现。
1.3. template<calss InputIterator> iterator insert(iterator position, InputIterator first, InputIterator last);
- 解释:这个重载的insert函数是一个模板函数,在position位置前插入迭代器first~last的值。
- 如有一个链表l:1<->2<->3。执行insert(l.begin(),l.begin(),l.end())后链表变为:1<->2<->3<->1<->2<->3。返回的迭代器指向最左边的元素1。
代码实现:
template <typename T>
template <class Iter>
Iterator<T> Mylist<T>::Insert(Iterator<T> it, Iter first, Iter last)
{
Iter _first = first;
//循环插入数据,每次插入位置的参考均为it
for (; last != first; ++first){
Insert(it, *first);
}
//移动it指向最后插入的数据
for (; last != _first; ++_first){
it--;
}
return it;
}
2插入数据之push_front()和push_back():
2.1void push_front(const value_type & val)
- 解释:将数据val插入到链表头部
- 例子:如有一个链表l:1<->2<->3。执行l.push_front(5);后链表变为:5<->1<->2<->3
在写迭代器的文章中,为了测试迭代器,我们写过一个push_front。再这里,我们可以利用insert()函数来实现push_front,代码如下:
template <typename T>
void Mylist<T>::Push_front(const T& _data)
{
Insert(this->Begin(),_data);
}
2.2 void push_back(const value_type & val)
- 解释:将数据val插入到链表尾部
- 例子:如有一个链表l:1<->2<->3。执行l.push_back(5);后链表变为: 1<->2<->3<->5
template <typename T>
void Mylist<T>::Push_back(const T& _data)
{
Insert(this->End(), _data);
}
3插入数据之splice():在两个list之间的插入操作
3.1void splice(iterator position, list& x)
- 解释:将链表x的所有数据移动到position位置前
- 例子:如有如下两个链表l1:1<->2<->3;l2:4<->5<->6。执行l1.splice(l1.begin(),l2);后链表l1变为: 4<->5<->6<->1<->2<->3;链表l2变为空。
- 使用注意:当执行l1.splice(l1.begin(),l1)后,运行不报错,但链表l1和l2不变。
代码如下,调用3.3的splice函数格式实现:
template <typename T>
void Mylist<T>::Splice(Iterator<T> it, Mylist<T> & x)
{
Splice(it,x,x.Begin(),x.End());
}
3.2void splice(iterator position, list& x, iterator i)
- 解释:将链表x的迭代器i指向位置的数据移动到position位置前
- 例子:如有如下两个链表l1:1<->2<->3;l2:4<->5<->6。执行l1.splice(l1.begin(),l2,l2.begin());后链表l1变为: 4<->1<->2<->3;链表l2变为:5<->6。
- 使用注意:不能执行l1.splice(l1.begin(),l2,l2.end()),会报错,因为l2.end()是表头节点的迭代器,并不是最后一个节点的迭代器,应该使用–l2.end()。
template <typename T>
void Mylist<T>::Splice(Iterator<T> it, Mylist<T> & x, Iterator<T> i)
{
Iterator<T> tmp = i;
//检测i是否为x的迭代器
for (Iterator<T> ite = x.Begin(); ite != x.End(); ite++){
if (ite == i){
Splice(it, x, i, ++tmp);
break;
}
}
}
这里首先判断i是否为x的迭代器。若是则调用3.3的splice函数格式实现,否则不处理。
3.3void splice(iterator position, list& x, iterator first, iterator last)
- 解释:将链表x的迭代器first~last区间的数据移动到position位置前
- 例子:如有两个链表l1:1<->2<->3;l2:4<->5<->6。执行l1.splice(l1.begin(),l2,l2.begin(),l2.end());后链表l1变为:4<-> 5<->6<->1<->2<->3;链表l2变为空。
- 使用注意:last必须是first的后面的节点迭代器,否则运行报错。如果first =last,运行时不报错,但不改变l1和l2。
根据STL::list的使用特点,我做的splice代码如下,代码中有详细的注释。
template <typename T>
void Mylist<T>::Splice(Iterator<T> it, Mylist<T> & x, Iterator<T> first, Iterator<T> last)
{
Iterator<T> _first = first;
bool InList = false; //first是x的迭代器标志
bool OneList = false; //first~last处于一条链上的标志
//1.当x和this不是同一个,并且x不为空时,执行操作
if (this != &x && !x.Empty()){
//2.检测first是不是x的迭代器
for (Iterator<T> tmp = x.Begin(); tmp != x.End(); tmp++){
if (tmp == first){
InList = true;
break;
}
}
//3.判断last是否>first,并且判断first到last是不是一条完整的链表
//当first=last时,不做操作
while (InList && first!=last && ++_first != x.End()){
if (_first.current == last.current){
OneList = true;
break;
}
}
//3.1上面的while当_first=x.End()时会退出,如果last=x.End(),我们就漏掉了,所以加下面的判断
if (first != last && _first == x.End() && _first.current == last.current)
OneList = true;
//4.splice操作
if (InList == true && OneList == true){
ListNode<T> *lastNodePre = last.current->leftlink;
first.current->leftlink->rightlink = last.current;
last.current->leftlink = first.current->leftlink;
first.current->leftlink = it.current->leftlink;
it.current->leftlink->rightlink = first.current;
it.current->leftlink = lastNodePre;
lastNodePre->rightlink = it.current;
}
}
}
3.3.1 splice操作示意图如下:
关于第4步的操作,示意图如下:
4.完整插入代码
Mylist.h
#ifndef _MY_LIST_H
#define _MY_LIST_H
template <typename T>
class Mylist;
template <typename T>
class Iterator;
//定义双向链表的结点类
template <typename T>
class ListNode
{
friend class Mylist<T>;//Mylist类为ListNode类的友元,从而可以访问其私有成员
friend class Iterator<T>;//迭代器类Iterator为ListNode类的友元,从而可以访问其私有成员
private:
T data;
ListNode<T> * leftlink;
ListNode<T> * rightlink;
ListNode() :leftlink(nullptr), rightlink(nullptr){};
ListNode(T _data) :data(_data), leftlink(nullptr), rightlink(nullptr){}
};
//定义双向链表类
template <typename T>
class Mylist
{
public:
Mylist();
~Mylist();
Iterator<T> Begin();
Iterator<T> End();
Iterator<T> Insert(Iterator<T> it, const T& _data);//插入数据
Iterator<T> Insert(Iterator<T> it, int n, const T& _data);//插入数据
template <class Iter>
Iterator<T> Insert(Iterator<T> it, Iter first, Iter last);//插入数据
void Push_front(const T& _data);//在表头插入
void Push_back(const T& _data);//在末尾插入
void Splice(Iterator<T> it, Mylist<T> & x);
void Splice(Iterator<T> it, Mylist<T> & x,Iterator<T> i);
void Splice(Iterator<T> it, Mylist<T> & x, Iterator<T> first, Iterator<T> last);
bool Empty();
private:
ListNode<T> * first;
};
//定义迭代器iterator
template <typename T>
class Iterator
{
friend class Mylist<T>;//Mylist类为Iterator类的友元,从而可以访问其私有成员
public:
Iterator() :current(nullptr){}
Iterator(ListNode<T> *& node) :current(node){}
//重载解引用符*
T& operator*()
{
return this->current->data;
}
//重载前缀自加运算符++
Iterator<T>& operator++()
{
this->current = this->current->rightlink;
return *this;
}
//重载后缀自加运算符++
Iterator<T> operator++(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->rightlink;
return tmp;
}
//重载前缀自减运算符--
Iterator<T>& operator--()
{
this->current = this->current->leftlink;
return *this;
}
//重载后缀自加运算符--
Iterator<T> operator--(int)
{
Iterator<T> tmp = *this;
(*this).current = (*this).current->leftlink;
return tmp;
}
//重载不等比较运算!=
bool operator!=(const Iterator<T>& it)
{
return this->current != it.current;
}
//重载等于比较运算==
bool operator==(const Iterator<T>& it)
{
return this->current == it.current;
}
//重载->
T* operator->()
{
return &(this->current->data);
}
//ListNode<T>* getIter()
//{
// return current;
//}
private:
ListNode<T> * current;//节点指针
};
/***************************构造和析构函数*************************/
template <typename T>
Mylist<T>::Mylist()
{
first = new ListNode<T>;//生成一个表头结点,它里面不带数据
first->leftlink = first->rightlink = first;//左右都指向自己
}
template <typename T>
Mylist<T>::~Mylist()
{
}
/*********************Begin()和End()返回链表头和尾的迭代器***********************/
template <typename T>
Iterator<T> Mylist<T>::Begin()
{
return Iterator<T>(first->rightlink);
}
template <typename T>
Iterator<T> Mylist<T>::End()
{
return Iterator<T>(first);
}
/***************************插入数据Insert()*****************************/
template <typename T>
Iterator<T> Mylist<T>::Insert(Iterator<T> it, const T& _data)
{
ListNode<T> *newNode = new ListNode<T>(_data);//生成一个节点
//1.确定插入节点的指向
newNode->leftlink = it.current->leftlink;
newNode->rightlink = it.current;
//2.改变迭代器it指向节点的指向
it.current->leftlink = newNode;
//3.改变插入节点的前一个节点指向
newNode->leftlink->rightlink = newNode;
return Iterator<T>(newNode);
}
template <typename T>
Iterator<T> Mylist<T>::Insert(Iterator<T> it, int n, const T& _data)
{
for (; n>0; --n)
it = Insert(it, _data);
return it;
}
template <typename T>
template <class Iter>
Iterator<T> Mylist<T>::Insert(Iterator<T> it, Iter first, Iter last)
{
Iter _first = first;
for (; last != first; ++first){
Insert(it, *first);
}
for (; last != _first; ++_first){
it--;
}
return it;
}
/***************************插入数据push_front()\push_back()****************************/
template <typename T>
void Mylist<T>::Push_front(const T& _data)
{
Insert(this->Begin(),_data);
}
template <typename T>
void Mylist<T>::Push_back(const T& _data)
{
Insert(this->End(), _data);
}
/***************************插入数据Splice()****************************/
template <typename T>
void Mylist<T>::Splice(Iterator<T> it, Mylist<T> & x)
{
Splice(it,x,x.Begin(),x.End());
}
template <typename T>
void Mylist<T>::Splice(Iterator<T> it, Mylist<T> & x, Iterator<T> i)
{
Iterator<T> tmp = i;
//检测i是否为x的迭代器
for (Iterator<T> ite = x.Begin(); ite != x.End(); ite++){
if (ite == i){
Splice(it, x, i, ++tmp);
break;
}
}
}
template <typename T>
void Mylist<T>::Splice(Iterator<T> it, Mylist<T> & x, Iterator<T> first, Iterator<T> last)
{
Iterator<T> _first = first;
bool InList = false;
bool OneList = false;
//1.当x和this不是同一个,并且x不为空时,执行操作
if (this != &x && !x.Empty()){
//2.检测first是不是x的迭代器
for (Iterator<T> tmp = x.Begin(); tmp != x.End(); tmp++){
if (tmp == first){
InList = true;
break;
}
}
//3.判断last是否>first,并且判断first到last是不是一条完整的链表
while (InList && first!=last && ++_first != x.End()){
if (_first.current == last.current){
OneList = true;
break;
}
}
if (first != last && _first == x.End() && _first.current == last.current)
OneList = true;
//4.splice操作
if (InList == true && OneList == true){
ListNode<T> *lastNodePre = last.current->leftlink;
first.current->leftlink->rightlink = last.current;
last.current->leftlink = first.current->leftlink;
first.current->leftlink = it.current->leftlink;
it.current->leftlink->rightlink = first.current;
it.current->leftlink = lastNodePre;
lastNodePre->rightlink = it.current;
}
}
}
template <typename T>
bool Mylist<T>::Empty()
{
return first->rightlink == first;
}
#endif
5.总结
在我自己实现的底层代码中,都没有加异常处理。比如说在splice操作中,当first 在last 的右边时,STL::list运行报错,而我的处理是保持两个链表不变。我的目的只是写出一个自己的list操作,从而了解大体的实现原理。如果想知道STL::list的底层具体是怎么实现的,朋友们可自己学习。
好了,list的插入操作就完成了,下一篇文章将实现list的常用操作之删除操作。
如果有疑问,欢迎评论区下方留言;本人水平有限 ,如有错误,也欢迎在评论区下方批评指正。若是喜欢本文,就帮忙点赞吧!
系列文章链接:
C++ STL::list常用操作及底层实现(上)——实现list迭代器
C++ STL::list常用操作及底层实现(中1)——实现list常用操作之插入(insert、push_front、push_back、splice)
C++ STL::list常用操作及底层实现(中2)——实现list常用操作之删除(erase、remove、pop_front、pop_back、clear)
C++ STL::list常用操作及底层实现(下)——实现list其他常用操作(reverse、assign、front、back)