zoukankan      html  css  js  c++  java
  • 《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实现原理,思想非常类似于 归并排序,但源码实现上 却不太好看出来 和 归并类似,需要自己手动模拟才能 有所体会。
    • 其具体思想可查看 参考博客
    • 下图是对应 该博客描述的 步骤 和 实现源码
  • 相关阅读:
    nginx访问控制
    nginx的请求限制
    nginx目录及配置语法
    安装Nginx
    Docker Service启动时挂载docker命令
    禁止flyme自动下载rom
    docker.service 修改指南
    debian 10.x (buster) 离线安装docker及卸载
    按照容器名称清除docker容器产生的日志文件内容
    debian修改系统语言为英文
  • 原文地址:https://www.cnblogs.com/q1076452761/p/14682158.html
Copyright © 2011-2022 走看看