1, List frame construction
1. Introduction
- list is a sequential container that can be inserted and deleted at any position within the constant range, and the container can iterate back and forth.
- The bottom layer of list is the leading two-way linked list structure. Each element in the two-way linked list is stored in independent nodes that are not related to each other. In the node, the pointer points to its previous element and the latter element. And forward_list is a single linked list and can only iterate forward
- Compared with other sequential containers (array, vector, deque), list usually inserts and removes elements at any position, which is efficient.
- Compared with other sequential containers, list and forward_ The biggest drawback of the list is that it does not support random access to any location. For example, to access an element in the middle, it needs to iterate from the head or unknown to that location. The time complexity is O(N). The list also needs some additional space to save the associated information of each node (this may be an important factor for a large list with small storage types)
2. Use of list
3. List structure
3.1 construct an empty list
list(){ //Lead two-way cycle _head = new Node(T()); //The construction here cannot pass 0, so it is not necessarily of type int. here T may be a vector or a string object _head->_next = _head; _head->_prev = _head; }
3.2 copy structure
list(const list<T>& lt){ //Deep copy _head = new Node; _head->_next = _head; _head->_prev = _head; for(const auto& e : lt){ push_back(e); } }
3.3 constructing a list with interval elements
template<class InputIterator> list(InputIterator first,InputIterator last){ //This supports not only the initialization of linked lists, but also the iterators of string s or vector s _head = new Node; _head->_next = _head; _head->_prev = _head; while(first != last){ push_back(*first); ++first; } }
4. List node
template<class T> struct __list_node{ __list_node<T>* _next; __list_node<T>* _prev; T _data; __list_node(const T& x = T()) //Initialize an anonymous object :_next(nullptr) , _prev(nullptr) ,_data(x) {} };
5. Iterator framework implementation
Iterator for List
Iterators can be implemented in two ways, which should be implemented according to the underlying data structure of the container:
- Original ecological pointer, such as vector
- Encapsulate the original pointer. Because the use form of iterator is exactly the same as that of pointer, the following must be implemented in the custom class
method:
- The pointer can be dereferenced, and the iterator class must be overloaded with operator * ()
- The pointer can access the space member it refers to through - > and the iterator class must be overloaded with oprator - > ()
- The pointer can move backwards in + +, and the iterator class must overload operator + + () and operator++(int)
As for the operator – () / operator – (int) release, it needs to be overloaded. It depends on the specific structure. The two-way linked list can move forward, so it needs to be overloaded. If it is forward_list does not need to be overloaded – - The iterator needs to compare whether it is equal, so it also needs to overload operator = = () and operator= ()
Here typdef's__ list_ The iterator takes three parameters. The second and third class templates are used to determine what type of object operator * () returns and operator - > () returns the data of the current node, respectively
template<class T> class list { typedef __list_node<T> Node; public: typedef __list_iterator<T, T&, T*> iterator; typedef __list_iterator<T, const T&, const T*> const_iterator; 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); } private: Node* _head; };
6. Assignment overload
//Dereference Ref operator*(){ return _node->_data; } Ptr operator->(){ return &_node->_data; } // ++it self& operator++(){ _node = _node->_next; return *this; } // it++ self operator++(int){ //Post++ self tmp(*this); _node = _node->_next; return tmp; } // --it self& operator--(){ _node = _node->_prev; return *this; } // it-- self operator--(int){ //Post-- self tmp(*this); _node = _node->_prev; return tmp; } //Compare for equality bool operator!=(const self& it){ return _node != it._node; } bool operator==(const self& it){ return _node == it._node; }
7. About the capacity of the List
Go through it until you meet the leader
size_t size(){ size_t n = 0; iterator it = begin(); while(it != end()){ ++it; ++n; } return n; } bool empty(){ return begin() == end(); }
8. Add a new node to the list
iterator insert(iterator pos, const T& x){ Node* cur = pos._node; //Current node Node* prev = cur->_prev; Node* newnode = new Node(x); prev->_next = newnode; newnode->_prev = prev; newnode->_next = cur; cur->_prev = newnode; return iterator(newnode); //Reverses the newly inserted node } void push_back(const T& x){ insert(end(), x) } void push_front(const T& x){ insert(begin(), x); }
9. Delete a node
Because of the characteristics of list data structure, O(1) can not access any node like array, so only header deletion and tail deletion are provided in the standard library
iterator erase(iterator pos){ assert(pos != end()); Node* cur = pos._node; Node* pre = cur->_prev; Node* next = cur->_next; delete cur; pre->_next = next; next->_prev = prev; return iterator(next); } void pop_back(){ erase(--end()); } void pop_front(){ erase(begin()); }
10. Empty the linked list and destruct
It should be noted here that destruct only after the instantiated object has opened up space, otherwise an error will be reported after entering the clear function
void clear(){ iterator it = begin(); while(it != end()){ it = erase(it); }
~list(){ clear(); delete _head; _head = nullptr; }
2, Complete code
Gitee link 🔗 🔗 🔗
👉 👉 👉 List simulation 👈 👈 👈
It's not easy to create. If the article helps you, please like it for three times:)