【C++干货铺】list的使用 | 模拟实现

简介: 【C++干货铺】list的使用 | 模拟实现

list的介绍及使用

list的介绍

ad21280e6c0644f4839808620ccfe10c.png

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素)

9f021a1dc5174e7db38e0006c5e23aa3.png

list的使用

list的构造

函数名称       

函数作用

list(size_t type n,const value_type

构造的list中包含n个值为val的元素

list()

构造空list

list(const list&x)

拷贝构造函数

list(first,last)

迭代器区间中的元素构造list

  list<int> lt1(10, 1);
  list<int> lt2;
  list<int> lt3(lt1);
  list<int> lt4(lt3.begin(), lt3.end());
  list<int>::iterator lt = lt4.begin();
  while (lt != lt4.end())
  {
    cout << *lt << " ";
    lt++;
  }
  cout << endl;
  for (auto e : lt4)
  {
    cout << e << " ";
  }
  cout << endl;

abd1e25b8f704a669139ad400988e4b2.png

list迭代器的使用

函数名称

函数作用

begin+end

返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器

rbegin+rend

返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置

list的增删查改

函数名称

函数作用

push_back

在list尾部插入值为val的元素

push_front

在list首元素前插入值为val的元素

pop_back

删除list中最后一个元素

pop_front

删除list中第一个元素

insert

在list position 位置中插入值为val的元素

erase

删除list position位置的元素

  list<int> lt(5,9);
  //头插
  lt.push_front(1);
  //尾插
  lt.push_back(2);
  for (auto e : lt)
  {
    cout << e << " ";
  }
  cout << endl;
  //尾删
  lt.pop_back();
  //头删
  lt.pop_front();
  for (auto e : lt)
  {
    cout << e << " ";
  }
  cout << endl;
  lt.insert(lt.begin(), 30);
  for (auto e : lt)
  {
    cout << e << " ";
  }
  cout << endl;
  lt.erase(lt.begin());
  for (auto e : lt)
  {
    cout << e << " ";
  }

3a07c0b58784479dbc311df3e67279af.pnglist的模拟实现

list通过一个一个结构体的结点实现,节点中包括指向下一个位置、指向前一个位置的指针和有效数值组成。

结点的封装

使用struct而不是class是因为默认为公开的

  template<class T>
  //封装结点
  struct list_node
  {
    list_node(const T& x=T())
      :_data(x)
      ,_next(nullptr)
      ,_prev(nullptr)
    {
    }
    T _data;
    list_node* _next;
    list_node* _prev;
  };

迭代器的封装

list和顺序表最大的不同是list物理上不连续,需要使用指针进行移动直线下一个或者指向其他的操作,而不像顺序表物理上是连续的,++、--都可以拿到有效数据;因此需要对迭代器单独封装。

//迭代器封装
  template<class T, class Ref, class Ptr>
  struct __list_iterator
  {
    typedef list_node<T> Node;
    typedef __list_iterator<T, Ref, Ptr> self;
    Node* _node;
    __list_iterator(Node* node)
      :_node(node)
    {}
    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& s)
    {
      return _node != s._node;
    }
    bool operator==(const self& s)
    {
      return _node == s._node;
    }
  };


list成员变量

指向结构体节点的指针和有效数据的个数

    Node* _node;
    size_t _size;


构造函数

    typedef list_node<T> Node;
  public:
    typedef __list_iterator<T, T&, T*> iterator;
    typedef __list_iterator<T, const T&, const T*> const_iterator;
    void empty_init()
    {
      _node = new Node;
      _node->_next = _node;
      _node->_prev = _node;
      _size = 0;
    }
    list()
    {
      empty_init();
    }

拷贝构造函数

  list(list<T>& x)
    {
      empty_init();
      for (auto e : x)
      {
        push_back(e);
      }
    }

operator=

    void swap(list<T>& lt)
    {
      std::swap(_node, lt._node);
      std::swap(_size, lt._size);
    }
    list<int>& operator=(list<int> lt)
    {
      swap(lt);
      return *this;
    }

析构函数和清理空间

    ~list()
    {
      clear();
      delete _node;
      _node = nullptr;
    }
    void clear()
    {
      iterator it = begin();
      while (it != end())
      {
        it = erase(it);
      }
    }

insert

    void insert(iterator pos, const T& x)
    {
      Node* cur = pos._node;
      Node* newnode = new Node(x);
      Node* prev = cur->_prev;
      prev->_next = newnode;
      newnode->_next = cur;
      cur->_prev = newnode;
      newnode->_prev = prev;
      _size++;
    }

erase

iterator erase(iterator pos)
    {
      Node* cur = pos._node;
      Node* prev = cur->_prev;
      Node* next = cur->_next;
      prev->_next = next;
      next->_prev = prev;
      return next;
    }

push_back、push_frontpop_backpop_front

    void push_back(const T& x)
    {
      insert(end(), x);
    }
    void push_front(const T& x)
    {
      insert(begin(), x);
    }
    void pop_front()
    {
      erase(begin());
    }
    void pop_back()
    {
      erase(--end());
    }


begin+end、cbegin+cend

    const_iterator begin() const
    {
      return const_iterator(_node->_next);
    }
    const_iterator end() const
    {
      return const_iterator(_node);
    }
    iterator end()
    {
      return _node;
    }
    iterator begin()
    {
      return _node->_next;
    }

listvector的对比

vector

list




动态顺序表,一段连续空间

带头结点的双向循环链表



访

支持随机访问,访问某个元素效率O(1)

不支持随机访问,访问某个元素
效率O(N)





任意位置插入和删除效率低,需要搬移元素,时间复杂
度为O(N),插入时有可能需要增容,增容:开辟新空
间,拷贝元素,释放旧空间,导致效率更低

任意位置插入和删除效率高,不
需要搬移元素,时间复杂度为 O(1)





底层为连续空间,不容易造成内存碎片,空间利用率
高,缓存利用率高

底层节点动态开辟,小节点容易
造成内存碎片,空间利用率低,
缓存利用率低



原生态指针

对原生态指针(节点指针)进行封装





在插入元素时,要给所有的迭代器重新赋值,因为插入
元素有可能会导致重新扩容,致使原来迭代器失效,删
除时,当前迭代器需要重新赋值否则会失效

插入元素不会导致迭代器失效,
删除元素时,只会导致当前迭代
器失效,其他迭代器不受影响

使


需要高效存储,支持随机访问,不关心插入删除效率

大量插入和删除操作,不关心随
机访问

 

相关文章
|
1月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
48 2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
30天前
|
存储 算法 C++
【C++打怪之路Lv10】-- list
【C++打怪之路Lv10】-- list
20 1
|
1月前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
52 5
|
1月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
53 2
|
1月前
|
C++
【C++】C++ STL 探索:List使用与背后底层逻辑(三)
【C++】C++ STL 探索:List使用与背后底层逻辑
|
1月前
|
C++
【C++】C++ STL 探索:List使用与背后底层逻辑(二)
【C++】C++ STL 探索:List使用与背后底层逻辑
|
1月前
|
存储 编译器 C++
【C++】C++ STL 探索:List使用与背后底层逻辑(一)
【C++】C++ STL 探索:List使用与背后底层逻辑
|
1月前
|
存储 缓存 C++
C++番外篇——list与vector的比较
C++番外篇——list与vector的比较
21 0
|
1月前
|
C++
C++番外篇——list的实现
C++番外篇——list的实现
19 0
|
1月前
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
18 0