讨论stl链表

讨论链表

链表是一个序列容器,在任意位置都可以用常数时间插入或者删除,并且可以在两个方向进行迭代。

list迭代器失效

迭代器失效指迭代器所指向的结点无效,即该结点被删除了。

  • list的底层结构是双向带头循环链表,因此向list中插入数据是不会造成迭代器失效。
  • 但删除数据时,指向删除结点的迭代器会失效,其他迭代器不会受影响。

list的模拟实现

创建结点类

  • 链表是由一个一个结点组成,结点中存放储存的元素已经指向下一个以及前一个的指针。
c++ 复制代码
template<class T>
struct list_node {
    T _val;
    list_node<T> *_prev;
    list_node<T> *_next;

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

链表迭代器

  • 链表的迭代器不同于顺序表。顺序表的迭代器可以直接返回头部和尾部指针的位置。++操作只需要移动相应字节数的指针即可完成。
  • 链表迭代器++操作不能依靠简单的指针++完成,因为不是连续空间,因此需要封装一层结点结构,以_node = _node->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 = nullptr)
            : _node(node) {}

    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;
    }

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

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

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

    bool operator==(const self &s) {
        return _node == s._node;
    }
};
  • 其中RefPtr模板为传入引用或者指针对象区分const和非const的模板。

完成实现代码

c++ 复制代码
namespace max {
    template<class T>
    struct list_node {
        T _val;
        list_node<T> *_prev;
        list_node<T> *_next;

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

    // 迭代器封装
    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 = nullptr)
                : _node(node) {}

        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;
        }

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

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

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

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

    template<class T>
    class list {
        typedef list_node<T> node;
        typedef __list_iterator<T, T &, T *> iterator;
        typedef __list_iterator<T, const T &, const T *> const_iterator;
    public:

        void empty_init() {
            _head = new node();
            _head->_next = _head;
            _head->_prev = _head;

            _size = 0;
        }

        list() {
            empty_init();
        }

        list(int n, const T &val = T()) {
            empty_init();
            for (int i = 0; i < n; ++i) {
                push_back(val);
            }
        }

        template<class Iterator>
        list(Iterator first, Iterator last) {
            empty_init();
            while (first != last) {
                push_back(*first);
                ++first;
                ++_size;
            }
        }

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

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

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

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

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


        void push_back(const T &val) {
            node *newNode = new node(val);
            node *end = _head->_prev;

            end->_next = newNode;
            newNode->_prev = end;
            newNode->_next = _head;
            _head->_prev = newNode;

            ++_size;
        }

        void push_front(const T &val) {
            node *newNode = new node(val);
            node *next = _head->_next;

            newNode->_next = next;
            next->_prev = newNode;
            _head->_next = newNode;
            newNode->_prev = _head;
        }

        void pop_back() {
            assert(_head->_next != _head);

            node *del = _head->_prev;
            _head->_prev = del->_prev;

            del->_prev->_next = _head;

            delete del;
            del = nullptr;
            --_size;
        }

        void pop_front() {
            assert(_head->_next != _head);

            node *del = _head->_next;

            _head->_next = del->_next;
            del->_next->_prev = _head;

            delete del;
            del = nullptr;
            --_size;
        }

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

        iterator end() {
            return iterator(_head);
        }

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

        const_iterator end() const {
            return const_iterator(_head);
        }

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

            prev->next = newNode;
            newNode->_prev = prev;
            newNode->_next = pos._node;
            pos._node->_prev = newNode;

            ++_size;

            return iterator(newNode);
        }

        iterator erase(iterator pos) {
            assert(_head != _head->_next);
            node *cur = pos._node;
            node *next = cur->_next;
            node *prev = cur->_prev;

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

            delete cur;
            cur = nullptr;
            --_size;

            return iterator(next);
        }

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

        size_t size() const {
            return _size;
        }

    private:
        node *_head;
        size_t _size;
    };
}

list与vector

  • vectorlist底层结构不同,因此在使用场景上存在一定差异。
vector list
底层结构 动态顺序表,一段连续的空间。 带头双向循环链表
随机访问 支持随机访问,效率为O(1)。 不支持随机访问,访问某个元素的效率为O(N)。
插入和删除 尾插和尾删时效率是O(1)。除此之外插入删除的效率都很低,因为需要移动数据,若插入大量数据还涉及到扩容,异地扩容需要拷贝元素和释放旧空间,效率很低。 任意位置插入和删除数据效率为O(1)。不需要移动数据。
空间利用率 底层为连续空间,不容易产生内存碎片,利用率高,缓存利用率高。 底层为动态开辟的小结点,容易造成内存碎片,空间利用率低,缓存利用率低。
迭代器 原生态指针。 对原生态指针进行封装。
迭代器失效 插入元素时可能会造成迭代器失效,因为可能会异地扩容。删除元素时当前迭代器会失效。 插入元素时不会造成迭代器失效,删除元素时会造成当前迭代器失效,其他迭代器不受影响。
相关推荐
春蕾夏荷_7282977259 分钟前
MFC 简易成绩管理界面
c++·mfc·成绩
weixin_4217252630 分钟前
C语言、C++与C#深度研究报告:从底层控制到现代企业级开发的演进
c语言·c++·c·内存管理·编译模型
basketball61635 分钟前
C++ 多态完全指南:同一个接口,千变万化的行为
java·开发语言·c++
NiceCloud喜云1 小时前
AutoClaw 接入自定义 Anthropic 端点:让 Kanban 工作流跑在自己的模型路由上
java·开发语言·c++·人工智能·python·eclipse·batch
Hua-Jay1 小时前
OpenCV联合C++/Qt 学习笔记(二十五)----加载深度神经网络模型及深度神经网络模型的使用
c++·笔记·qt·opencv·学习·计算机视觉·dnn
计算机安禾2 小时前
【c++面向对象编程】第49篇:面向对象的单元测试:用GoogleTest测试类
开发语言·c++·单元测试
坚定学代码2 小时前
如何在c++中使用MySQL
开发语言·c++·mysql
纽扣6672 小时前
【C++通关之路】C++ 继承深度全景指南:从语法陷阱到内存底层的终极复习
开发语言·c++
楼田莉子2 小时前
C++17特性:强制省略拷贝优化/折叠表达式/非类型模板参数/嵌套命名空间
开发语言·c++
xifangge20252 小时前
Steam/Epic 游戏启动报错 0xc000007b / msvcp140.dll 缺失?VC++ 运行库底层修复指南
开发语言·c++·游戏