讨论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)。不需要移动数据。
空间利用率 底层为连续空间,不容易产生内存碎片,利用率高,缓存利用率高。 底层为动态开辟的小结点,容易造成内存碎片,空间利用率低,缓存利用率低。
迭代器 原生态指针。 对原生态指针进行封装。
迭代器失效 插入元素时可能会造成迭代器失效,因为可能会异地扩容。删除元素时当前迭代器会失效。 插入元素时不会造成迭代器失效,删除元素时会造成当前迭代器失效,其他迭代器不受影响。
相关推荐
阿龍178731 分钟前
Qt中udp指令,大小端,帧头帧尾实际示例
网络·c++·qt·网络协议·udp
Li&ShuaiShuai1 小时前
Linux系统编程:信号
linux·运维·服务器·c++·信号
Neituijunsir2 小时前
2024.06.27 校招 实习 内推 面经
c++·算法·面试·车载系统·自动驾驶·汽车·求职招聘
DisonTangor2 小时前
新纪录将圆周率计算到了小数点后202万亿位 用了28块61.44TB SSD
算法·链表·数学建模
danaaaa2 小时前
算法力扣刷题 三十一【150. 逆波兰表达式求值】
数据结构·c++·算法·leetcode·职场和发展
史嘉庆3 小时前
【C++ | 继承】|概念、方式、特性、作用域、6类默认函数
c++
菜鸟赵大宝3 小时前
【C++】C++深拷贝与浅拷贝
java·jvm·c++
二进制人工智能4 小时前
【C++设计模式】(一)面向对象编程的八大原则
c++·设计模式
会c++的修勾4 小时前
欧拉筛法与埃氏拉筛
开发语言·c++·算法
Hcoco_me4 小时前
底层软件 | 内存管理和智能指针
c++·算法·指针