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。
时间: 2024-10-31 10:37:23

STL学习笔记--4、序列式容器之list的相关文章

STL学习笔记(序列式容器)

Vector Vector是一个动态数组. 1.Vector的操作函数 构造.拷贝和析构 vector<Elem> c //产生一个空vector ,其中没有任何元素 vector<Elem> c1(c2) //产生另一个同型vector的副本,(所有元素都被拷贝) vector<Elem> c(n) //利用元素的default构造函数生成一个大小为n的vector vector<Elem> c(n,elem) //产生一个大小为n的vector,每个元素

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

常见的数据结构:array数组,list链表,tree树,stack栈,queue队列,hash table散列表,set集合,map映射-- 根据数据在容器中的排列分为:序列式sequence和关联式associative. 序列式容器之vector 1.vector VS array: array是静态空间,一旦配置则无法改变: vector是动态空间,随着元素的加入,内部机制会自动扩充新的空间来容纳新的元素. 实现技术:对大小的控制和重新配置时的数据移动效率. 扩充空间:配置新空间.数据移

STL源码笔记(12)—序列式容器之deque(二)

STL源码笔记(12)-序列式容器之deque(二) 再谈deque数据结构 我们知道deque是通过map管理很多个互相独立连续空间,由于对deque_iterator的特殊设计,使得在使用的时候就好像连续一样.有了deque_iterator的基础(例如重载的操作符等),对于我们实现容器的一些方法就十分方便了.与vector一样,deque也维护一个start,和finish两个迭代器,start指向容器中的一个元素,finish指向最后一个元素的后一个位置(前闭后开),从微观上讲,star

STL 源码剖析读书笔记三:序列式容器之 vector、list

1. STL 中的容器 容器,置物之所也.STL 容器即是将运用最广的一些数据结构实现出来.如下图所示: 上图以内缩方式来表达基层和衍生层的关系.所谓衍生,并非派生关系,而是内含关系.例如 heap 内含一个 vector,priority-queue 内含一个 heap.stack.queue都内含一个 deque,set/map/multimap/multiset 都内含一个 RB-tree,hash_x 都内含一个 hashtable. 2. 序列式容器之 vector 所谓序列式容器,其

STL 源码剖析读书笔记四:序列式容器之 deque、stack、queue

1. 序列式容器 deque 1.1 deque 概述 vector是单向开口的连续线性空间,用户只能在vector尾部进行插入删除操作,而 deque 是一种双向开口的连续线性空间,允许我们在头尾两端操作. deque 和 vector 的最大差异在于: deque 允许常数时间对头端元素进行插入和移除操作 deque 没有所谓容量(capacity)概念,因为它是动态地以分段连续的空间组合而成,随时可以增加一段新的空间并链接起来 deque提供的迭代器也是 RandomAccess Iter

C++ 序列式容器之vector

什么是容器 容器,顾名思义,是用来容放东西的场所.C++容器容放某种数据结构,以利于对数据的搜寻或排序或其他特殊目的.众所周知,常用的数据结构不外乎:数组array,  链表list,  树tree,  栈stack,  队列queue,  散列表hash table,  集合set.映射表map 等等.容器便是容纳这些数据结构的.这些数据结构分为序列式与关联式两种,故容器也分为序列式容器和关联式容器.   (图来自<STL源码剖析>) vector 概述 1.  vector是STL提供的一

STL序列式容器之list

一,list容器基本概念 1.list容器基本知识 list容器的底部数据结构为双向链表,可以高效的进行插入和删除元素. list因为底层数据结构是双向链表,因此不支持下标操作和.at()函数的操作.要获取元素,必须从头到尾遍历. 使用list容器必须引入头文件# include<list>. 二,list容器构造函数 1.无参构造函数 // 无参构造函数 list<string> l1; 2.有参构造函数 // 有参构造函数,10个字符'A'来初始化容器 list<char

python基础教程_学习笔记:序列-1

序列 数据结构:通过某种方式组织在一起的数据元素的集合,这些数据元素可以是数字或者字符,甚至可以是其他数据结构. python中,最基本的数据结构是序列. 序列中的每个元素被分配一个序号--即元素的位置,也称为索引.第一个索引是0,第二个是1,以此类推. 序列概览 python包含6种内建的序列:列表.元组.字符串.Unicode字符串.buffer对象和xrange对象. 列表和元组的主要区别在于,列表可以修改,元组不能. 使用后者的理由通常是技术性的,它与python内部的运作方式有关.这也

STL学习笔记--&gt;初识STL

“这里要用char类型”; “这里要用int类型”; “其实实现这个方法只需要把另一个方法的返回值的类型和传入参数的类型改成float类型就实现了”; “其实这个算法只需要把以前写的那个稍微改动一下就行了”; ……………… 学过面向对象语言的都知道GP这个概念,就是泛型程序设计,说的再明白点就是编写不依赖于具体数据类型的程序,C++作为一门面向对象语言,当然也有泛型这个概念,这就不得不提STL(Standard Template Library,标准模板库),是被融入C++标准程序库里面的一个高