目录
list的介绍及使用
list的底层结构
节点类的实现
list的实现
构造函数
拷贝构造
方法一:方法二:
析构函数
赋值重载
insert / erase
push_/pop_(尾插/尾删/头插/头删)
begin和end(在已建立迭代器的基础上)
迭代器实现
迭代器类模板参数说明
list包含在头文件
部分摘自— list模拟实现
list的介绍及使用
1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。 |
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。ps—>2 |
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。 |
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。 |
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间 开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这 可能是一个重要的因素) |
list的底层结构
ps—>2
list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素
节点类的实现
但我们要去实现list,就首先需要定义实现好一个结点类。
而一个结点需要存储:
需要存储的数据、前驱节点、后继节点的地址。
这里用struct定义这个节点类不用class,因为很多地方都要用到和访问这个节点类,成员变量就都共有。
这里也不需要专门单独写一个析构函数,因为这个节点类并没有对应的资源需要清理;
template <class T>
struct ListNode
{ListNode<T>* _prev;ListNode<T>* _next;T _data;ListNode(const T& data = T()):_prev(nullptr), _next(nullptr), _data(data){}
};
list的实现
链表其实就是用一个指向头节点的指针管理起来的,所以,我们可以定义一个list类,它的成员变量是一个指向头节点的指针,因为,list的底层是一个带头双向循环链表,所以这里的指针,应该指向,带有哨兵位的头节点。
template<class T>
class list
{
public:/*typedef ListNode<T> Node;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;list(){_head = new Node();_head->_prev = _head;_head->_next = _head;}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}*/private:Node* _head;
};
构造函数
ist是一个带头双向循环链表,在构造一个list对象时,首先要申请一个头结点,并让其前驱指针和后继指针都指向自己。
list()
{_head = new node; //申请头结点_head->_next = _head; //头结点的后继指针指向自己_head->_prev = _head; //头结点的前驱指针指向自己
}
拷贝构造
拷贝构造有两种方式: ①用一个list构造方法二 ②迭代器区间
为了方便起见,我们可以先封装手撕一个empty()函数,去申请头节点,生成一个空链表,在构造和拷贝构造的时候,可以更加方便地调用。
void empty()
{_head = new Node;_head->_prev = _head;_head->_next = _head;
}
方法一:
已经申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。
list(const list<T>& lt)
{empty();for (const auto& e : lt){push_back(e);}
}
方法二:
我们先实现一个使用迭代器区间构造的函数。
template <class Iterator>
list(Iterator first, Iterator last)
{empty;//不加会出问题while (first != last){push_back(*first);++first;}
}
然后,我们先创建一个临时对象让他利用被拷贝对象的迭代器构造出来,然后再交换,被利用完后的临时对象会在栈帧结束后被清除。
我们先实现swap函数,很简单,交换头节点的指针就可以。
void swap(list<T>& tmp)
{std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{empty();list<T> tmp(lt.begin(), lt.end());swap(tmp);
}
析构函数
我们可以调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空即可
void clear()//将头结点释放
{iterator it = begin();while (it != end()){//it = erase(it);erase(it++);}
}
~list()
{clear();//将头结点释放delete _head;//最后将头指针置空即可_head = nullptr;
}
赋值重载
通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换即可。
list<T>& operator=(list<T> tmp)
{swap(tmp);return *this;
}
insert / erase
insert函数的作用时在指定迭代器的位置之前插入一个数据。
iterator insert(iterator pos, const T& x)
{Node* node = pos._node;//记录当前节点Node* prev = node->_prev;//记录前驱节点Node* newnode = new Node(x);//记录要插入节点newnode->_prev = prev;newnode->_next = node;prev->_next = newnode;node->_prev = newnode;return iterator(newnode);
}
erase函数用来删除指定迭代器位置的数据。
iterator erase(iterator pos)
{assert(pos != end());//防止删除头节点Node* del = pos._node;//记录要删除节点Node* prev = del->_prev;//记录前驱节点Node* next = del->_next;//记录后续节点prev->_next = next;next->_prev = prev;return iterator(next);//返回所给迭代器pos的下一个迭代器,防止迭代器失效
}
push_/pop_(尾插/尾删/头插/头删)
void push_back(const T& val)
{ insert(end(), val);
}void pop_back()
{ erase(--end());
}void push_front(const T& val)
{ insert(begin(), val);
}void pop_front()
{ erase(begin());
}
begin和end(在已建立迭代器的基础上)
begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。
iterator begin()
{return iterator(_head->_next);
}const_iterator begin() const
{return const_iterator(_head->_next);
}iterator end()
{return iterator(_head);
}const_iterator end() const
{return const_iterator(_head);
}
迭代器实现
对于list来说,它的数据不是连续存储的而是通过一个一个节点通过指针连接到一起的,所以,_head++
,并不能到下一个数据的位置,但是可以通过_head = _head->_next
实现,所以这里我们可以使用节点的指针单独封装一个类,通过运算符重载模拟指针的行为。
(vector迭代器可以使用原生指针来实现,因为vector的空间时连续的,可以直接支持运算符++的重载,_start时指向第一个数据的指针,_start++
就指向下一个位置了。)
迭代器类模板参数说明
template<class T, class Ref, class Ptr>
在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
所以,迭代器类的模板参数列表当中的Ref
和Ptr
分别代表的是解引用类型和指针类型。
当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。
template <class T,class Ref,class Ptr>
struct ListIterator
{typedef ListNode<T> Node;Node* _node;typedef ListIterator<T,Ref,Ptr> self;ListIterator(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;}Ref operator*(){return _node->_data;}bool operator!=(const self& node){return _node != node._node;}bool operator==(const self& node){return _node == node._node;}Ptr operator->(){return &_node->_data;}
};
struct Pos{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}};void test_list2(){list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 200));lt1.push_back(Pos(300, 300));list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){//cout << (*it)._row << ":" << (*it)._col << endl;// 为了可读性,省略了一个->cout << it->_row << ":" << it->_col << endl;//cout << it->->_row << ":" << it->->_col << endl;cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;++it;}cout << endl;}
list包含在头文件
因为list类是一种模板,不建议list内部内容函数声明定义分开来写,所以都包含在同一个文件当中
#pragma once
#include<assert.h>namespace bit
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data = T()):_next(nullptr),_prev(nullptr),_data(data){}};template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}// ++it;Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};//template<class T>//class ListConstIterator//{// typedef ListNode<T> Node;// typedef ListConstIterator<T> Self;// Node* _node;//public:// ListConstIterator(Node* node)// :_node(node)// {}// // ++it;// Self& operator++()// {// _node = _node->_next;// return *this;// }// Self& operator--()// {// _node = _node->_prev;// return *this;// }// Self operator++(int)// {// Self tmp(*this);// _node = _node->_next;// return tmp;// }// Self& operator--(int)// {// Self tmp(*this);// _node = _node->_prev;// return tmp;// }// //*it// const T& operator*()// {// return _node->_data;// }// const T* operator->()// {// return &_node->_data;// }// bool operator!=(const Self& it)// {// return _node != it._node;// }// bool operator==(const Self& it)// {// return _node == it._node;// }//};template<class T>class list{typedef ListNode<T> Node;public:// 不符合迭代器的行为,无法遍历//typedef Node* iterator;//typedef ListIterator<T> iterator;//typedef ListConstIterator<T> const_iterator;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){//iterator it(_head->_next);//return it;return iterator(_head->_next);}const_iterator begin() const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end() const{return const_iterator(_head);}list(){_head = new Node();_head->_next = _head;_head->_prev = _head;}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);}void pop_back(){erase(--end());}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}// 没有iterator失效iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;// prev newnode curprev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}// erase 后 pos失效了,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 iterator(next);}private:Node* _head;};void Func(const list<int>& lt){// const iterator const 迭代器不能普通迭代器前面加const修饰// const 迭代器目标本身可以修改,指向的内容不能修改 类似const T* p list<int>::const_iterator it = lt.begin();while (it != lt.end()){// 指向的内容不能修改//*it += 10;cout << *it << " ";++it;}cout << endl;}void test_list1(){list<int> lt1;// 按需实例化(不调用就不实例化这个成员函数)lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);Func(lt1);//ListIterator<int> it = lt1.begin();list<int>::iterator it = lt1.begin();while (it != lt1.end()){*it += 10;cout << *it << " ";++it;}cout << endl;for (auto e : lt1){cout << e << " ";}cout << endl;}struct Pos{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}};void test_list2(){list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 200));lt1.push_back(Pos(300, 300));list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){//cout << (*it)._row << ":" << (*it)._col << endl;// 为了可读性,省略了一个->cout << it->_row << ":" << it->_col << endl;//cout << it->->_row << ":" << it->->_col << endl;cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;++it;}cout << endl;}void test_list4(){list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);Func(lt1);lt1.push_front(10);lt1.push_front(20);lt1.push_front(30);Func(lt1);lt1.pop_front();lt1.pop_front();Func(lt1);lt1.pop_back();lt1.pop_back();Func(lt1);lt1.pop_back();lt1.pop_back();lt1.pop_back();lt1.pop_back();//lt1.pop_back();Func(lt1);}
}