讨论stl链表

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

链表定义

list迭代器失效

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

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

list的模拟实现

创建结点类

  • 链表是由一个一个结点组成,结点中存放储存的元素已经指向下一个以及前一个的指针。
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来达到++的效果。
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的模板。

完成实现代码

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底层结构不同,因此在使用场景上存在一定差异。
vectorlist
底层结构动态顺序表,一段连续的空间。带头双向循环链表
随机访问支持随机访问,效率为O(1)。不支持随机访问,访问某个元素的效率为O(N)。
插入和删除尾插和尾删时效率是O(1)。除此之外插入删除的效率都很低,因为需要移动数据,若插入大量数据还涉及到扩容,异地扩容需要拷贝元素和释放旧空间,效率很低。任意位置插入和删除数据效率为O(1)。不需要移动数据。
空间利用率底层为连续空间,不容易产生内存碎片,利用率高,缓存利用率高。底层为动态开辟的小结点,容易造成内存碎片,空间利用率低,缓存利用率低。
迭代器原生态指针。对原生态指针进行封装。
迭代器失效插入元素时可能会造成迭代器失效,因为可能会异地扩容。删除元素时当前迭代器会失效。插入元素时不会造成迭代器失效,删除元素时会造成当前迭代器失效,其他迭代器不受影响。
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烛九_阴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值