在 MyList 里,我们定义了:
它不是存放用户数据的节点,而是一个 特殊的空节点 —— 就叫 sentinel(哨兵)。它用来简化边界处理。 传统链表在插入、删除时需要考虑很多“特殊情况”:
如果在头部插入怎么办?
如果在尾部删除怎么办?
如果链表是空的怎么办?
有了 sentinel,一切都变统一了
sentinel 的结构
当链表为空时:
1 2 3 4 5 6 +---------+ | head_ | |---------| | prev → |───┐ | next → |───┘ +---------+
当链表有元素时,比如 [A, B, C]:
1 head_ <-> A <-> B <-> C <-> head_
也就是说:
head_->next 指向第一个元素节点
head_->prev 指向最后一个元素节点
head_ 本身不存数据,仅作为“循环起点”使用
iterator for my list, should be bidiractional iterator.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 template <typename T>class list_iterator {public : using iterator_catagory = mystl::bidirectional_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t ; using pointer = T*; using reference = T&; using node_ptr = list_node<T>*; private : node_ptr node_; public : list_iterator () : node_ (nullptr ){} explicit list_iterator (node_ptr p) : node_(p){ } reference operator *() const {return node_->data;}; pointer operator ->() const {return &(node_->data);} list_iterator& operator ++(){ node_ = node_->next; return *this ; } list_iterator operator ++(int ){ list_iterator tmp = *this ; ++(*this ); return tmp; } list_iterator& operator --(){ node_ = node_-> prev; return *this ; } list_iterator operator --(int ){ list_iterator tmp = *this ; --(*this ); return tmp; } bool operator ==(const list_iterator& rhs) const {return node_ == rhs.node_;} bool operator !=(const list_iterator& rhs) const {return node_ != rhs.node_;} node_ptr get_node () const {return node_;} };
and the list impl:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 #ifndef MY_LIST_H #define MY_LIST_H #include "my_allocator.h" #include "my_iterator.h" #include <initializer_list> #include <cstddef> #include <cassert> namespace mystl{template <typename T>struct list_node { list_node* prev; list_node* next; T data; list_node (const T& value = T ()) : prev (nullptr ), next (nullptr ), data (value){} }; class MyList {public : using value_type = T; using allocator_type = Alloc; using size_type = std::size_t ; using difference_type = std::ptrdiff_t ; using reference = value_type&; using const_reference = const value_type; using iterator = list_iterator<T>; using const_iterator = list_iterator<const T>; private : using node_type = list_node<T>; using node_ptr = node_type*; allocator_type alloc_; node_ptr head_; size_type size_; public : MyList () : size_ (0 ){ create_head (); } MyList (std::initializer_list<T> ilist) : MyList (){ for (const auto & val : ilist){ push_back (val); } } ~MyList (){ clear (); destroy_head (); } bool empty () const noexcept {return size_ == 0 ;} size_type size () const noexcept {return size_;} iterator begin () noexcept {return iterator (head_->next);} iterator end () noexcept { return iterator{head_};} reference front () {return head_ -> next -> data;} reference back () {return head_ -> prev -> data;} void push_back (const T& value) { insert_node_before (head_, value); } void push_front (const T& value) { insert_node_before (head_->next, value); } void pop_back () { erase (head_->prev); } void pop_front () { erase (head_->next); } void clear () noexcept { node_ptr cur = head_-> next; while (cur != head_){ node_ptr next = cur->next; destroy_node (cur); cur = next; } head_->next = head_->prev = head_; size_ = 0 ; } iterator insert (iterator pos, const T& value) { node_ptr new_node = create_node (value); node_ptr cur = pos.get_node (); new_node->next = cur; new_node->prev = cur->prev; cur->prev->next = new_node; cur->prev = new_node; ++size_; return iterator (new_node); } iterator erase (iterator pos) { node_ptr cur = pos.get_node (); node_ptr next_ = cur->next; cur->next->prev = cur->prev; cur->prev->next = cur->next; destroy_node (cur); --size; return iterator (next_); } private : void create_head () { head_ = alloc_.allocate (1 ); head_ -> next = head_; head_ -> prev = head_; } void destroy_head () { alloc_.deallocate (head_, 1 ); head_ = nullptr ; } node_ptr create_node (const T& value) { node_ptr p = alloc_.allocate (1 ); alloc_.construct (p, node_type (value)); return p; } void destroy_node (node_ptr p) { alloc_.destroy (p); alloc_.deallocate (p, 1 ); } void insert_node_before (node_ptr pos, const T& value) { node_ptr new_node = create_node (value); new_node->next = pos; new_node->prev = pos->prev; pos->prev->next = new_node; pos->prev = new_node; ++size_; } }; } #endif