讨论链表
- list迭代器失效
- list的模拟实现
- 创建结点类
- 链表迭代器
- 完成实现代码
- list与vector
链表是一个序列容器,在任意位置都可以用常数时间插入或者删除,并且可以在两个方向进行迭代。
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;}
};
- 其中
Ref
和Ptr
模板为传入引用或者指针对象区分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> <) {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
- 因
vector
和list
底层结构不同,因此在使用场景上存在一定差异。
vector | list | |
---|---|---|
底层结构 | 动态顺序表,一段连续的空间。 | 带头双向循环链表 |
随机访问 | 支持随机访问,效率为O(1)。 | 不支持随机访问,访问某个元素的效率为O(N)。 |
插入和删除 | 尾插和尾删时效率是O(1)。除此之外插入删除的效率都很低,因为需要移动数据,若插入大量数据还涉及到扩容,异地扩容需要拷贝元素和释放旧空间,效率很低。 | 任意位置插入和删除数据效率为O(1)。不需要移动数据。 |
空间利用率 | 底层为连续空间,不容易产生内存碎片,利用率高,缓存利用率高。 | 底层为动态开辟的小结点,容易造成内存碎片,空间利用率低,缓存利用率低。 |
迭代器 | 原生态指针。 | 对原生态指针进行封装。 |
迭代器失效 | 插入元素时可能会造成迭代器失效,因为可能会异地扩容。删除元素时当前迭代器会失效。 | 插入元素时不会造成迭代器失效,删除元素时会造成当前迭代器失效,其他迭代器不受影响。 |