酒类网站建设,足球网站模板,中国建筑招聘官网2022,北太平庄网站建设#x1f307;个人主页#xff1a;平凡的小苏 #x1f4da;学习格言#xff1a;命运给你一个低的起点#xff0c;是想看你精彩的翻盘#xff0c;而不是让你自甘堕落#xff0c;脚下的路虽然难走#xff0c;但我还能走#xff0c;比起向阳而生#xff0c;我更想尝试逆风… 个人主页平凡的小苏 学习格言命运给你一个低的起点是想看你精彩的翻盘而不是让你自甘堕落脚下的路虽然难走但我还能走比起向阳而生我更想尝试逆风翻盘。 C专栏C内功修炼基地 家人们更新不易你们的点赞和⭐关注⭐真的对我真重要各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问感谢你们的转发 关注我关注我关注我你们将会看到更多的优质内容 一、list的介绍 list是可以在常数范围内在任意位置进行插入和删除的序列式容器并且该容器可以前后双向迭代。 list的底层是双向链表结构双向链表中每个元素存储在互不相关的独立节点中在节点中通过指针指向其前一个元素和后一个元素。 list与forward_list非常相似最主要的不同在于forward_list是单链表只能朝前迭代已让其更简单高效。 与其他的序列式容器相比(arrayvectordeque)list通常在任意位置进行插入、移除元素的执行效率更好。 与其他序列式容器相比list和forward_list最大的缺陷是不支持任意位置的随机访问比如要访问list的第6个元素必须从已知的位置(比如头部或者尾部)迭代到该位置在这段位置上迭代需要线性的时间开销list还需要一些额外的空间以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素
二、迭代器的功能分类
1、单向迭代器只能不能–。例如单链表哈希表
2、双向迭代器既能也能–。例如双向链表
3、随机访问迭代器能±-也能和-。例如vector和string。
迭代器是内嵌类型内部类或定义在类里
三、list的迭代器失效问题
对于list插入操作是不会失效的但是删除操作是会失效的因为删除操作会进行释放节点
迭代器失效即迭代器所指向的节点的无效即该节点被删除了。因为list的底层结构为带头结点的双向循环链表
因此在list中进行插入时是不会导致list的迭代器失效的只有在删除时才会失效并且失效的只是指向被删
除节点的迭代器其他迭代器不会受到影响。
错误代码
void TestListIterator1()
{int array[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };listint l(array, arraysizeof(array)/sizeof(array[0]));auto it l.begin();while (it ! l.end()){// erase()函数执行后it所指向的节点已被删除因此it无效在下一次使用it时必须先给//其赋值l.erase(it); it;}
}修改正确的代码
void TestListIterator()
{int array[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };listint l(array, arraysizeof(array)/sizeof(array[0]));auto it l.begin();while (it ! l.end()){l.erase(it); // it l.erase(it);}
}四、list的迭代器模拟实现
4.1普通迭代器
迭代器的主要作用解引用获取数据、、–
我在模拟实现string、vector时都是用的原生指针没有将迭代器用类进行封装这是为了简易化但是STL标准库也是用用类封装了迭代器。而我在模拟实现list迭代器时不能用原生指针了因为list的节点地址时不连续的不能使用原生指针。
template class T, class Ref, class Ptrstruct __list_iterator{typedef list_nodeT Node;typedef __list_iteratorT, Ref, Ptr Self;Node *_node;__list_iterator(Node *node): _node(node){}Ref operator*(){return _node-_val;}Ptr operator-(){return _node-_val;}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;}bool operator!(const Self it)const{return _node ! it._node;}bool operator(const Self it)const{return _node it._node;}};这是用类封装了迭代器的作用while循环的判断条件在迭代器的类里面进行了运算符重载解引用也进行了运算符重载 否则自定义类型是做不到用运算符的这样做的目的是为了我们不暴露迭代器的底层实现给使用者用统一的方式像指针一样就能访问迭代器这就是封装的强大之处。 4.2 const迭代器
const迭代器的错误写法
typedef __list_iteratorT iterator;
const listT::iterator itlt.begin();因为typedef后const修饰的是迭代器it只能调用operator*(),调不了operator()。重载operator()为const operator()也不行,因为const版本还是改变不了
正确写法想实现const迭代器不能在同一个类里面动脑筋需要再写一个const版本迭代器的类。
//用类封装const迭代器
template class T
struct __list_const_iterator
{typedef list_nodeT node;//用节点的指针进行构造__list_const_iterator(node* p):_pnode(p){}//迭代器运算符的重载const T operator*()const{return _pnode-_data;}__list_const_iteratorT operator()//返回值不要写成node*因为迭代器肯定返回迭代器啊你返回节点指针类型不对{//return _pnode-_next;//返回类型错误的_pnode _pnode-_next;return *this;//返回的是迭代器}__list_const_iteratorT operator--(){_pnode _pnode-_prev;return *this;}bool operator!(const __list_const_iteratorT it)const{return _pnode ! it._pnode;}
public:node* _pnode;//封装一个节点的指针
};typedef __list_const_iteratorT const_iterator;这样写会让代码显得冗余。STL库中是通过类模板多给一个参数来实现这样同一份类模板就可以生成两种不同的类型的迭代器以下为仿STL库的模拟实现 4.3 反向迭代器
反向迭代器的就是正向迭代器的–反向迭代器的–就是正向迭代器的因此反向迭代器的实现可以借助正向迭代器即反向迭代器内部可以包含一个正向迭代器对正向迭代器的接口进行包装即可。
template class Iterator, class Ref, class Ptr
class ReverseIterator
{
public:typedef ReverseIteratorIterator, Ref, Ptr Self;ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp _it;--tmp;return *tmp;}Ptr operator-(){return operator*();}Self operator(){--_it;return *this;}Self operator--(){_it;return *this;}bool operator!(const Self rit)const{return _it ! rit._it;}bool operator(const Self rit)const{return _it rit.it;}
private:Iterator _it;
};
五、list和vector区别
vector与list都是STL中非常重要的序列式容器由于两个容器的底层结构不同导致其特性以及应用场景不同其主要不同如下
1.底层结构
list:带头结点的双向循环链表
vector:动态顺序表一段连续空间
2.迭代器失效
list:带头结点的双向循环链表
vector:在插入元素时要给所有的迭代器重新赋值因为插入元素有可能会导致重新扩容致使原来迭代器失效删除时当前迭代器需要重新赋值否则会失效
3.使用场景
list大量插入和删除操作不关心随机访问
vector需要高效存储支持随机访问不关心插入删除效率
4.随机访问
list不支持随机访问访问某个元素效率O(N)
vector支持随机访问访问某个元素效率****O(1)
5.插入和删除
list任意位置插入和删除效率高不需要搬移元素时间复杂度为O(1)
vector任意位置插入和删除效率低需要搬移元素时间复杂度为O(N)插入时有可能需要增容增容开辟新空间拷贝元素释放旧空间导致效率更低
六、list的模拟实现源码
#pragma once
#include iostream
#include cassert
#include algorithm
using namespace std;
namespace sqy
{template class Tstruct list_node{list_node(const T val T()): _prev(nullptr), _next(nullptr), _val(val){}list_nodeT *_prev;list_nodeT *_next;T _val;};template class T, class Ref, class Ptrstruct __list_iterator{typedef list_nodeT Node;typedef __list_iteratorT, Ref, Ptr Self;Node *_node;__list_iterator(Node *node): _node(node){}Ref operator*(){return _node-_val;}Ptr operator-(){return _node-_val;}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;}bool operator!(const Self it)const{return _node ! it._node;}bool operator(const Self it)const{return _node it._node;}};template class Iterator, class Ref, class Ptrclass ReverseIterator{public:typedef ReverseIteratorIterator, Ref, Ptr Self;ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp _it;--tmp;return *tmp;}Ptr operator-(){return operator*();}Self operator(){--_it;return *this;}Self operator--(){_it;return *this;}bool operator!(const Self rit)const{return _it ! rit._it;}bool operator(const Self rit)const{return _it rit._it;}private:Iterator _it;};template class Tclass list{typedef list_nodeT Node;public:typedef __list_iteratorT, T , T * iterator;typedef __list_iteratorT, const T , const T * const_iterator;typedef ReverseIteratoriterator, T, T* reverse_iterator;iterator begin(){return iterator(_head-_next);}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head-_next);}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}void empty_init(){_head new Node;_head-_prev _head;_head-_next _head;_size 0;}void swap(listT lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}// listT operator(listT lt)// {// if(this ! lt)// {// clear();// for(auto t : lt)// {// push_back(t);// }// }// }//现代写法赋值运算符重载listT operator(listT lt){swap(lt);return *this;}list(){empty_init();}list(const listT lt){empty_init();for (auto t : lt){push_back(t);}}~list(){clear();delete _head;_size 0;}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;// _size;insert(end(), x);}void push_front(const T x){// Node * tail _head-_prev;// Node * newnode new Node(x);// tail-_next newnode;// newnode-_prev tail;// newnode-_next _head;// _head-_prev newnode;// _size;insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T x){Node *newnode new Node(x);Node *prev pos._node-_prev;newnode-_prev prev;prev-_next newnode;newnode-_next pos._node;pos._node-_prev newnode;_size;return pos;}iterator erase(iterator pos){assert(_size ! 0);Node *prev pos._node-_prev;Node *tail pos._node-_next;Node *cur pos._node;prev-_next tail;tail-_prev prev;delete cur;--_size;return pos;}// 链表小接口bool empty() const{return _head-_next _head;}void clear(){iterator it begin();while (it ! end()){erase(it);it;}}size_t size() const{return _size;}private:Node *_head;size_t _size 0; // 元素个数};void Test_list1(){listint lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);listint::iterator it lt.begin();while (it ! lt.end()){cout *it ;it;}cout endl;// listint::iterator it1 lt2.begin();// while (it1 ! lt2.end())// {// cout *it1 ;// it1;// }// cout endl;// listint::reverse_iterator rit lt.rbegin();// while(rit ! lt.rend())// {// cout *rit endl;// rit;// }}
}