《STL 源码剖析》 list 实现原理

list概述

list对空间的运用是精准的,不浪费的。对于任何位置的元素插入 或 元素移除,list永远是常数时间。

list实现上就是一个双向循环链表,list节点 有prev 和 next 两个指针。

list迭代器

因为list是一个双向链表,他的迭代器就必须具备前移、后移的能力。list提供的是 Bidirectional Iterators。

list性质:插入操作、接合操作,不会造成原有的list迭代器失效,即使删除操作,也只是 被指向删除元素的 那个迭代器失效,其他不受影响。(这个和vector 有很大差别)

list数据结构

list是一个 环状双向链表,因此它只需要一个指针就能够 表现出整个完整链表

template <class T, class Alloc = alloc>
class list
{
protected:
    typedef __list_node<T> list_node;
public:
    typedef list_node* link_type;
 
protected:
    link_type node;// 只要一个指针,就表示整个链表
...
};

node指向尾端的一个空白节点,就能符合 ”前闭后开“ 区间的要求。

基本操作:

iterator begin() { return (link_type)((*node).next); }
 
iterator end() { return node; }
 
bool empty() const { return node->next == node; }
 
size_type size() const { // C++11 之前 复杂度线性,11之后复杂度 常数
    size_type result = 0;
    distance(begin(),end(),result); // stl 全局函数
    return result;
}
 
 
reference front() { return *begin(); }
reference back() { return *(--end()); }

list 构造与内存管理

class list
{
protected:
    // 专属的空间配置器,每次配置一个节点大小
    typedef simple_alloc<list_node, Alloc> list_node_allocator;
 
// list_node_allocator(n) 标识配置n个节点空间
 
protected:
    link_type get_node() { return list_node_allocator::allocate(); } // 分配一个节点并传回
    void put_node(link_type p) { list_node_allocator::deallocate(p); } // 释放节点
 
    link_type create_node(const T& x) // 分配空间 并 构造
    {
        link_type p = get_node();
        construct(&p->data, x);// 构造工具
        return p;
    }
 
    void destory_node(link_type p) // 析构 并 释放空间
    {
        destory(&p->data); // 析构
        put_node(p); // 释放
    }
 
public:
    list() { empty_initialize(); }// 构造空链表
    void push_back(const T& x) { insert(end(), x); }
     
    iterator insert(iterator position, const T& x)
    {
        link_type tmp = create_node(x);
        tmp->next = position.node;
        tmp->prev = position.node->prev;
        (link_type(position.node->prev))->next = tmp;
        position.node->prev = tmp;
        return tmp;
    }
 
 
protected:
    void empty_initialize()
    {
        node = get_node();
        node->next = node;
        node->prev = node;
    }
 
 
...
};

list 操作实现原理

  • push_front、push_back、erase、pop_front、pop_back 这些操作 实现上 和 通常手写链表的实现 相同。

list的transfer(前移操作)

将连续范围的元素迁移到特定位置之前,实现上就是 节点指针的重新连接。

transfer 是 其他复杂操作的基础,如splice、sort、merge

transfer 不是公开接口

splice实现原理

对于transfer的巧妙运用

void splice(iterator position, list& x) //将 x 拼接到 position位置之前
{
    if(!x.empty())
        transfer(position, x.begin(), x.end());
}
 
void splice(iterator position, list&, iterator i) // 将i的元素拼接到position之前,两者 可能是同一个list
{
    iterator j = i;
    ++j;
    if(position == i || position == j) return;
    transfer(position, i, j);
}
 
// 将[first, last)的元素接到position位置之前
// position 和 [first, last) 可能会指向同一个list
// 但是 position 不能位于[first,last)
void splice(iterator position, list&, iterator first, iterator last)
{
    if(first != last)
        transfer(position, first, last);
}

merge实现原理

前提:两个 已经 排好序的链表

遍历其中一个链表,记录过程中 遍历 头尾指针(遍历过程中进行大小比较)。将其拼接到另一个链表上。

实现上 和 普通链表合并 是类似的。

template <class T, class Alloc>
void list<T, Alloc>::merge(list<T, Alloc>& x)
{
    iterator firts1 = begin();
    iterator last1 = end();
    iterator first2 = x.begin();
    iterator last2 = x.end();
 
    // 遍历处理
    while(first1 ! last1 && first2 != last2)
    {
        if(*first2 < *first1)
        {
            iterator next = first2;
            transfer(first1, first2, ++next);
            first2 = next;
        }
        else
            ++first1;
    }
 
    if(first2!=last2) transfer(last1, first2, last2); // 如果末尾还有残留部分
}

list.sort 实现原理

  • list不能使用STL的sort,是因为 STL 的 sort 只接受 RamdonAccessIterator (随机访问的迭代器类型),而list中是 Bidirectional (双向迭代器类型)
  • list.sort实现原理,思想非常类似于 归并排序,但源码实现上 却不太好看出来 和 归并类似,需要自己手动模拟才能 有所体会。
  • 其具体思想可查看 参考博客
  • 下图是对应 该博客描述的 步骤 和 实现源码
posted @ 2021-04-20 17:56  Linese  阅读(930)  评论(0编辑  收藏  举报