1. 程式人生 > >STL學習筆記--4、序列式容器之list

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。