STL学习笔记--4序列式容器之list

Posted chengyu779394084

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL学习笔记--4序列式容器之list相关的知识,希望对你有一定的参考价值。

1、概述

list地址不连续的空间。每次插入或删除一个元素,就配置或释放一个元素空间。对于任意位置的元素插入或删除,list永远是常数时间。


2、list节点

list本身和list节点是不同的;

template <class T>
struct __list_node {
  typedef void* void_pointer;

  //void_pointer实际为__list_node<T>*
  void_pointer next;
  void_pointer prev;
  T data;
};

双向链表。


3、list迭代器

list迭代器必须有能力指向list的节点;

递增:指向下一个节点
递减:指向上一个节点
取值:取得是节点的数据值
成员取用:取得是节点的成员

STL的list是双向链表;迭代器提供的为Bidirectional Iterators。

插入insert和接合splice操作不会使原有list的迭代器失效。
删除只会使指向删除元素的迭代器失效。

template<class T, class Ref, class Ptr>
struct __list_iterator {
    typedef __list_iterator<T, T&, T*>             iterator;
    typedef __list_iterator<T, Ref, Ptr>           self;

    //迭代器提供的是BidirectionaIterator
    typedef bidirectional_iterator_tag iterator_category;
    typedef T value_type;
    typedef Ptr pointer;
    typedef Ref reference;
    //link_type为node的指针
    typedef __list_node<T>* link_type;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;

    link_type node;

    //构造函数
    __list_iterator(link_type x) : node(x) {}
    __list_iterator() {}
    __list_iterator(const iterator& x) : node(x.node) {}

    bool operator==(const self& x) const { return node == x.node; }
    bool operator!=(const self& x) const { return node != x.node; }

    //取值取得是节点的数据值
    reference operator*() const { return (*node).data; }
    //成员取用取得是节点的成员
    pointer operator->() const { return &(operator*()); }

    //前置++,指向下一个节点
    self& operator++() { 
        node = (link_type)((*node).next);
        return *this;
    }
    //后置++
    self operator++(int) { 
        self tmp = *this;
        ++*this;
        return tmp;
    }
    self& operator--() { 
        node = (link_type)((*node).prev);
        return *this;
    }
    self operator--(int) { 
        self tmp = *this;
        --*this;
        return tmp;
    }
};

4、list的数据结构

SGI的list不仅是双向链表,更是一个环形双向链表

指针node指向尾端的空白节点,则符合前闭后开的区间要求。

template <class T, class Alloc = alloc>
class list {
protected:
    typedef void* void_pointer;
    typedef __list_node<T> list_node;
protected:
    link_type node;//指向尾段空白节点
    //...
public: 
    iterator begin() { return (link_type)((*node).next); }
    iterator end() { return node; }
    bool empty() const { return node->next == node; }
    size_type size() const {
        size_type result = 0;
        //distance迭代器的成员函数
        distance(begin(), end(), result);
        return result;
    }
    reference front() { return *begin(); }
    reference back() { return *(--end()); }
};

这里写图片描述


5、list的构造和内存管理

1)、应用:

#include <list>
#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
    list<int> ilist;
    cout << "size=" << ilist.size() << endl;  // size=0
    ilist.push_back(0);
    ilist.push_back(1);
    ilist.push_back(2);
    ilist.push_back(3);
    ilist.push_back(4);
    cout << "size=" << ilist.size() << endl;  // size=5
    list<int>::iterator ite;//声明迭代器
    for(ite = ilist.begin(); ite != ilist.end(); ++ite)
        cout << *ite << ' ';  // 0 1 2 3 4
    cout << endl;
    //插入和接合操作不会造成原迭代器失效
    ite = find(ilist.begin(), ilist.end(), 3);
    if (ite != ilist.end())
        ilist.insert(ite, 99);//在以前3的位置处插入一个数99,
                              //插入完成后,新节点位于ite所指节点前方
    cout << "size=" << ilist.size() << endl;  // size=6
    cout << *ite << endl;  // 3 !!!!

    for(ite = ilist.begin(); ite != ilist.end(); ++ite)
        cout << *ite << ' ';  // 0 1 2 99 3 4
    cout << endl;
    //删除操作时,指向被删除元素的那个迭代器失效,其他不受影响
    ite = find(ilist.begin(), ilist.end(), 1);
    if (ite != ilist.end())
        cout << *(ilist.erase(ite)) << endl;// 2 !!!
    cout<<*ite<<endl;// 1 !!!
    cout<<*(ite++)<<endl;// 1 !!!
    for(ite = ilist.begin(); ite != ilist.end(); ++ite)
        cout << *ite << ' ';  // 0 2 99 3 4
    cout << endl;

    return 0;
}

2)、实现:

配置了list_node_allocator,方便以节点大小为单位来分配内存。

/*
//get_node()
//put_node()
//create_node()
//destroy_node()
//empty_initialize()
*/
template <class T, class Alloc = alloc>
class list {
protected:
    typedef void* void_pointer;
    typedef __list_node<T> list_node;
    typedef simple_alloc<list_node, Alloc> list_node_allocator;

protected:
    //配置一个节点
    link_type get_node() { return list_node_allocator::allocate(); }
    //释放一个节点
    void put_node(link_type p) { list_node_allocator::deallocate(p); }

    //产生一个节点,配置并构造,带有元素值x
    link_type create_node(const T& x) {
        link_type p = get_node();
        //空间配置的元素构造函数
        construct(&p->data, x);
        return p;
    }
    //销毁一个节点,析构并释放。
    void destroy_node(link_type p) {
        //空间配置的元素析构函数
        destroy(&p->data);
        put_node(p);
    }

public//产生一个空链表
    list(){empty_initialize();}
protected:
    //只有一个node节点,且node的头尾都指向自己,元素值为空。
    void empty_initialize() { 
        node = get_node();
        node->next = node;
        node->prev = node;
    }
};

push_back(x)调用insert来实现。

void push_back(const T& x){insert(end(),x);}

insert()函数完成之后,新节点将位于哨兵迭代器(标识插入点)所直接点的前方。

//在position所指向的位置处插入一个节点,内容为x
iterator insert(iterator position, const T& x) {
    //为插入内容构造节点
    link_type tmp = create_node(x);
    //修改迭代器的指向
    //4步策略
    tmp->next = position.node;
    tmp->prev = position.node->prev;
    (link_type(position.node->prev))->next = tmp;
    position.node->prev = tmp;
    return tmp;
}

这里写图片描述


6、元素操作

1)、实现:

    /*
    //push_front
    //push_back
    //erase
    //pop_front
    //pop_back
    //clear
    //remove(const T& value)将数值为value的所有元素移除
    //unique()移除所有数值相同并且连续的元素。只有连续且相同的元素才会被移除的只剩一个
    */
    void push_front(const T& x) { insert(begin(), x); }
    void push_back(const T& x) { insert(end(), x); }

    //删除position迭代器指向的位置处的节点
    //返回为next_node
    iterator erase(iterator position) {
        link_type next_node = link_type(position.node->next);
        link_type prev_node = link_type(position.node->prev);
        //调整迭代器的指向
        prev_node->next = next_node;
        next_node->prev = prev_node;
        //清除节点
        destroy_node(position.node);
        return iterator(next_node);
    }


    void pop_front() { erase(begin()); }
    //移除尾节点时,end()指向的是尾后节点
    void pop_back() { 
        iterator tmp = end();
        erase(--tmp);
    }


    //清除所有节点
    template <class T, class Alloc> 
    void list<T, Alloc>::clear()
    {
        link_type cur = (link_type) node->next; 
        //从头节点开始删除
        while (cur != node) {
            link_type tmp = cur;
            cur = (link_type) cur->next;
            destroy_node(tmp);
        }
        //最后一个节点头尾指向自身
        node->next = node;
        node->prev = node;
    }

    //将数值为value的所有元素移除
    template <class T, class Alloc>
    void list<T, Alloc>::remove(const T& value) {
        iterator first = begin();
        iterator last = end();
        //遍历节点
        while (first != last) {
            iterator next = first;
            ++next;
            if (*first == value) erase(first);
            first = next;
        }
    }

    //移除所有数值相同并且连续的元素。只有连续且相同的元素才会被移除的只剩一个
    template <class T, class Alloc>
    void list<T, Alloc>::unique() {
        iterator first = begin();
        iterator last = end();
        if (first == last) return;
        iterator next = first;
        while (++next != last) {
            if (*first == *next)
                erase(next);
            else
                first = next;

            next = first;
        }
    }

迁移操作transfer:将某个连续范围的元素迁移到某个特定的位置之前。

//transfer
//将[first,last)的所有元素放到position之前。
  void transfer(iterator position, iterator first, iterator last) {
    if (position != last) {
      (*(link_type((*last.node).prev))).next = position.node;//1
      (*(link_type((*first.node).prev))).next = last.node;//2
      (*(link_type((*position.node).prev))).next = first.node;//3
      link_type tmp = link_type((*position.node).prev);//4
      (*position.node).prev = (*last.node).prev;//5
      (*last.node).prev = (*first.node).prev; //6
      (*first.node).prev = tmp;//7
    }
  }

这里写图片描述

transfer非公开接口。
list中提供了一些公开的接口,底层使用transfer来实现的。

  • splice:将某个连续范围的元素从一个list移动到另一个(或同一个)list的某个定点。
  • merge:将一个list合并到另一个list。两个list都必须先经过递增排序。
  • reverse:将list的内容逆置。
  • sort:排序。

2)、应用:

int iv[5] = { 5,6,7,8,9 };
list<int> ilist2(iv, iv+5);
// 目前,ilist 的內容为0 2 99 3 4
ite = find(ilist.begin(), ilist.end(), 99);//在99前面插入
ilist.splice(ite,ilist2);  // 0 2 5 6 7 8 9 99 3 4
ilist.reverse(); // 4 3 99 9 8 7 6 5 2 0
ilist.sort(); // 0 2 3 4 5 6 7 8 9 99

总结:

  • (1)push_front(const T& value), push_back(const T& value)
  • (2)erase(iterator position):移除迭代器position所指节点
  • (3)pop_front(), pop_back()
  • (4)clear()://清空list节点
  • (5)remove(const T& value):将数值为value的所有元素移除
  • (6)unique():移除数值相同的连续元素,只剩下一个。注意相同而连续。
  • (7)splice:接合操作。将某连续范围的元素从一个list移动到另一个(或同一个)list的某个定点。
  • (8)merge(list &x):将x合并到*this身上。两个lists内容必须先递增排序。
  • (9)reverse():将list的内容逆置。
  • (10)sort():list不能使用STL算法sort(),必须使用自己的sort()成员函数,因为STL算法sort()只接受RamdonAccessIterator。list中sort()函数采用quicksort。

以上是关于STL学习笔记--4序列式容器之list的主要内容,如果未能解决你的问题,请参考以下文章

STL 源码剖析读书笔记四:序列式容器之 dequestackqueue

一文带你认识STL序列式容器--list

stl源码剖析-序列式容器 之 list

STL源码剖析 学习笔记

STL序列式容器之list

STL基础序列式容器之forward_list