STL-list链表

STL-list链表实现

STL中采用双向带头循环链表来实现 list ,下面将使用 C++ 实现 STL list 链表。

list 类中包含两个主要部分,一个是指向哨兵位头节点的指针(_head ,另一个是结构体类型的迭代器(__list_iterator

哨兵位头节点 本身是不存储数据 的,它只是用于简化代码实现操作 的,让 list 的头插尾插更加的方便。在 list 中通过指向哨兵位头节点的指针(_head)用于链接节点,实现高效快速地访问_next)和_prev)。

stringvector 中我们可以通过指针++访问下一个元素 ,这是因为它们俩都是顺序存储一片连续不断的内存空间 ,自然地可以实现指针++访问下一个元素 。所以它们俩的迭代器基本就是原生指针 套了一个壳子叫 iterator 以配合STL的统一设计。

但是如果想实现遍历 list 容器,单靠指针++访问下一个元素 是实现不了的,因为链表很灵活,每个节点的内存空间并不一定连续,所以不能单靠指针++访问下一个元素 是做不到的。但是为了实现迭代器++访问下一个元素 我们得对 list 的迭代器进行特殊的封装,以实现迭代器++访问下一个元素的操作。

节点模型

首先我们先来了解 list 的节点模型,list 中链接着许多的节点,每个节点都是一个节点模型的实现。都具有前后指针_prev_next),和一个数据值_val)。

C++ 复制代码
template <typename T>
struct list_node
{
    list_node<T>* _next;
    list_node<T>* _prev;
    T _val;

    list_node(const T& val = T())
    :_val(val)
    ,_next(nullptr)
    ,_prev(nullptr)
    {}
};

迭代器模型

下面我来详细解释一下 list 的迭代器模型。为了实现通过迭代器访问 list 节点的成员 ,我们需要在迭代器中封装 list_node 也就是节点模型。

C++ 复制代码
template <class T, class Ref, class Ptr>
struct __list_iterator
{
    typedef list_node<T> Node;
    typedef __list_iterator<T, Ref, Ptr> self;
    Node* _node;

    __list_iterator(Node* node)
    :_node(node)
    {}

    Ref operator*()
    {
        return _node->_val;
    }

    Ptr operator->()
    {
        return &_node->_val;
    }
};

然后你会发现接下来你就看不懂了 typedef __list_iterator<T, Ref, Ptr> self; 一个迭代器要这么多模板参数干嘛使的?这是为 const 迭代器 做铺垫,RefPtr 都是用来区分是否使用 const 修饰函数的返回值

C++ 复制代码
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;

下面我给出完整的 list 迭代器模型实现,迭代器 内访问节点数据都是通过指向节点的指针 _node 来进行访问,迭代器的 ++-- 也是通过修改 _node 节点模型内的前后指针_prev_next)来实现的,并不是直接使用的指针++指针--

C++ 复制代码
template <class T, class Ref, class Ptr>
struct __list_iterator
{
    typedef list_node<T> Node;
    typedef __list_iterator<T, Ref, Ptr> self;
    Node* _node;

    __list_iterator(Node* node)
    :_node(node)
    {}

    Ref operator*()
    {
        return _node->_val;
    }

    Ptr operator->()
    {
        return &_node->_val;
    }

    self& operator++()
    {
        _node = _node->_next;
        return *this;
    }

    self operator++(int)
    {
        self tmp(*this);
        _node = _node->_next;
        return tmp;
    }

    self& operator--()
    {
        _node = _node->_prev;
        return *this;
    }

    self operator--(int)
    {
        self tmp(*this);
        _node = _node->_prev;
        return tmp;
    }

    // end() 返回一个临时对象,临时对象具有常性,所以需要参数添加 const 修饰,并且 operator== 不会做修改操作,加上 const 修饰更加安全
    bool operator==(const self& it) const
    {
        return _node == it._node;
    }

    bool operator!=(const self& it) const
    {
        return _node != it._node;
    }
};

list 模型

了解完节点模型和迭代器模型,再来看 list 模型简直是轻松加愉快,不用多说都了解七七八八了。我就简单讲几个主要的函数,但是这里给出完整的 list 实现,方便大家进行全面的阅读。也可以跳过这一大段代码,看我解释完一些函数再回过头来学习。

C++ 复制代码
template <typename T>
class list
{
    typedef list_node<T> Node;        
public:
    typedef __list_iterator<T, T&, T*> iterator;
    typedef __list_iterator<T, const T&, const T*> const_iterator;

    iterator begin()
    {
        return _head->_next;
    }

    iterator end()
    {
        return _head;
    }

    const_iterator begin() const
    {
        return _head->_next;
    }

    const_iterator end() const
    {
        return _head;
    }

    void empty_init()
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
        _size = 0;
    }

    list()
    {
        empty_init();
    }

    list(const list<T>& lt)
    {
        empty_init();

        for (auto& e : lt)
        {
            push_back(e);
        }
    }

    ~list()
    {
        clear();
        delete _head;
        _head = nullptr;
    }

    list<T>& operator=(list<T> lt)
    {
        swap(lt);
        return *this;
    }

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

    void push_front(const T& val)
    {
        insert(begin(), val);
    }

    void pop_back()
    {
        erase(--end());
    }

    void pop_front()
    {
        erase(begin());
    }

    size_t size() const
    {
        return _size;
    }

    void swap(list<T> lt)
    {
        std::swap(_size, lt._size);
        std::swap(_head, lt._head);
    }

    iterator insert(iterator pos, const T& val)
    {
        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* newnode = new Node(val);

        prev->_next = newnode;
        newnode->_prev = prev;

        cur->_prev = newnode;
        newnode->_next = cur;

        ++_size;

        return newnode;
    }

    iterator erase(iterator pos)
    {
        assert(pos != end());

        Node* cur = pos._node;
        Node* prev = cur->_prev;
        Node* next = cur->_next;

        prev->_next = next;
        next->_prev = prev;

        delete cur;

        --_size;

        return next;
    }

    void clear()
    {
        iterator it = begin();
        while(it != end())
        {
            it = erase(it);
        }
        _size = 0;
    }

private:
    Node* _head;
    size_t _size;
};

了解一个类,首先我们需要了解的是它的成员变量。list 中只存储哨兵位头节点 ,它具有标准的节点模型 ,还有一个 _size 用来方便计数链表的长度。

然后我们了解迭代器相关的内容:begin()end()哨兵位头节点 的前面就是end() ,后面就是begin()

c++ 复制代码
iterator begin()
{
    return _head->_next;
}

iterator end()
{
    return _head;
}

const_iterator begin() const
{
    return _head->_next;
}

const_iterator end() const
{
    return _head;
}

接下来我们需要了解一下 list 的默认成员函数。但是这部分没什么好看的,大家自行了解吧。

最后我再解释一下 list 的插入数据,其实也很简单。insert 函数用于在 pos 位置之前插入一个数据,我们首先需要得到 pos 位置的节点,然后得到它的前一个节点方便进行后续的链接。最后,前一个节点的 _next 指向新节点,新节点的 _prev 指向前一个节点;pos 位置节点的 _prev 指向新节点,新节点的 _next 指向 pos 位置的节点。

c++ 复制代码
iterator insert(iterator pos, const T& val)
{
    Node* cur = pos._node;
    Node* prev = cur->_prev;
    Node* newnode = new Node(val);

    prev->_next = newnode;
    newnode->_prev = prev;

    cur->_prev = newnode;
    newnode->_next = cur;

    ++_size;

    return newnode;
}

看到这里,我相信你对STL中的 list 链表底层实现应该不陌生了。也不是那么复杂对吧,但是我这个是简化版,STL源码的 list 实现比这复杂得多,更多的封装还有内存池的调用。

相关推荐
purrrew11 分钟前
【数据结构_5】链表(模拟实现以及leetcode上链表相关的题目)
数据结构·leetcode·链表
1024熙1 小时前
【C++】——lambda表达式
开发语言·数据结构·c++·算法·lambda表达式
Python私教2 小时前
Java手写链表全攻略:从单链表到双向链表的底层实现艺术
java·python·链表
LJianK12 小时前
array和list在sql中的foreach写法
数据结构·sql·list
mahuifa2 小时前
(2)VTK C++开发示例 --- 绘制多面锥体
c++·vtk·cmake·3d开发
23级二本计科3 小时前
C++ Json-Rpc框架-3项目实现(2)
服务器·开发语言·c++·rpc
rigidwill6663 小时前
LeetCode hot 100—搜索二维矩阵
数据结构·c++·算法·leetcode·矩阵
矛取矛求4 小时前
栈与队列习题分享(精写)
c++·算法
摆烂能手5 小时前
C++基础精讲-02
开发语言·c++
胡乱儿起个名5 小时前
C++ 标准库中的 <algorithm> 头文件算法总结
开发语言·c++·算法