Introduction to common interface functions
Constructor
Constructor | Interface description |
---|---|
list() | Construct an empty list |
list (size_type n, const value_type& val = value_type()) | The constructed list contains n elements with value val |
list (const list& x) | copy constructor |
list (InputIterator first, InputIterator last) | Construct a list with elements in the [first, last) interval |
list<int> ls1(); //Construct an empty list object list<int> ls2(10, 2); //Create 10 objects with element value 2 list<int> ls3(ls2); //copy constructor list<int> ls4(l3.begin(), l3.end()); //Create an ls4 object using an iterator interval
iterator
Function declaration | Interface description |
---|---|
begin + end | Returns the iterator of the first element + returns the iterator of the next position of the last element |
rbegin + rend | Return the reverse_iterator of the first element, that is, the end position, and the next position of the last element |
1. begin and end are forward iterators. Perform + + operations on the iterator, and the iterator moves backward
2. rbegin(end) and rend(begin) are reverse iterators. Perform + + operation on the iterator, and the iterator moves forward
Three traversal modes
list<int> ls; //Construct an empty list object ls.push_back(1); ls.push_back(2); ls.push_back(3); ls.push_back(4); //Iterator traversal list<int>::iterator it = ls.begin(); while (it != ls.end()) { cout << *it << " "; it++; } //Range for traversal for(auto e : ls) { cout << e << " "; } //Use the array interval to construct an object and traverse the object int arr[] = { 8,4,5,11,23,66,89,45,8,65,65 }; list<int>ls1(arr, arr + sizeof(arr) / sizeof(arr[0])); for (auto e : ls1) { cout << e << " "; } cout << endl;
Common operation functions
Readers can use it in combination with documents
Function declaration | Interface description |
---|---|
front | Returns a reference to the value in the first node of the list |
back | Returns a reference to the value in the last node of the list |
push_front | Insert an element with value val before the first element of the list |
pop_front | Delete the first element in the list |
push_back | Insert an element with value val at the end of the list |
pop_back | Delete the last element in the list |
insert | Insert the element with value val in the position of list |
erase | Delete the element at list position |
swap | Swap elements in two list s |
clear | Clear valid elements in the list |
assign | Assign new content to container |
splice | Move the elements from x to the container and insert them into the location. |
remove | Delete the value val in the container. If there is no value, it will not be processed |
unique | Only the continuously repeated values in a section will be deleted, provided that the container is ordered |
front and back
front function prototype
reference front(); //Returns a reference to the first element of the list const_reference front() const; //The reference type of the first element of the list returned by the function is const_reference, and the object attribute is not allowed to be modified
back function prototype
reference back(); //Returns a reference to the last element of the list const_reference back() const; //The function returns a const_reference, and the object properties are not allowed to be modified
be careful:
Member types reference and const_reference are reference types to container elements
Test:
void func() { list<int> ls; ls.push_back(11); ls.push_back(4); //Returns the first element of the list and the last element of the list, subtracting their values cout << ls.front() - ls.back() << endl;//7 }
push_front and pop_front
push_front
Function prototype: void push_front (const value_type& val); stay list A new element is inserted before the current first element
pop_front
void pop_front(); delete list The first element in the container, effectively reducing its size by 1
Test:
void func() { list<int> ls; for (int i = 0; i < 5; i++) { ls.push_front(i); //Insert five values into the list container } for (auto e : ls) { cout << e << " "; //4 3 2 1 0 } cout << endl; for (int i = 0; i < 5; i++) { ls.pop_front(); //Header delete list data } cout << ls.size() << endl; //0 }
push_back and pop_back
push_back
void push_back (const value_type& val); At the end of the list container, a new element is added after the current last element This effectively increases the size of the container 1.
pop_back
void pop_back(); Deleting the last element in the list container effectively reduces the container size by 1.
Test:
void func() { list<int> ls; for (int i = 0; i < 5; i++) { ls.push_back(i);//Insert five values into the list container } for (auto e : ls) { cout << e << " "; //0 1 2 3 4 } cout <<"\n After inserting values size: "<< ls.size() << endl; for (int i = 0; i < 5; i++) { cout << "When deleting a value size: " << ls.size() << endl; ls.pop_front(); //Tail delete list data } }
Operation results:
insert and erase
Three versions of insert
//Extends the container by inserting a new element before the element at the specified location. iterator insert (iterator position, const value_type& val); //Extend the container by inserting n new elements with value val before the element at the specified location. void insert (iterator position, size_type n, const value_type& val); //Extends the container by inserting the value of an iterator interval before the element at the specified location. template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last);
Test:
void func() { list<int> ls; ls.push_back(1); ls.push_back(2); ls.push_back(3); ls.push_back(4); list<int> ::iterator it = ls.begin(); it++; //The second element of the list when it + + is finished ls.insert(it, 30); //Insert 30 before it position for (auto e : ls) { cout << e << " "; } //1 30 2 3 4 cout << endl; it++; //it now points to the element with a value of 3 //Insert a continuous value before the pos position int arr[] = { 10,20,30 }; ls.insert(it,arr,arr + sizeof(arr) / sizeof(arr[0])); for (auto e : ls) { cout << e << " "; } //1 30 2 10 20 30 3 4 cout << endl; it--; ls.insert(it, 5,1); for (auto e : ls) { cout << e << " "; } //1 30 2 10 20 1 1 1 1 1 30 3 4 cout << endl; }
effect:
erase
//Removes a single element (position) of the specified pos from the list container iterator erase (iterator position); //Removes an element ([first, last]) that specifies an interval from the list container. iterator erase (iterator first, iterator last);
Return value:
Returns an iterator that points to the next element of the element deleted by the function call. If the operation deletes the last element in the sequence,
Test:
void func1() { list<int> ls; ls.push_back(1); ls.push_back(2); ls.push_back(3); ls.push_back(4); for (auto e : ls) { cout << e << " "; } cout << endl; cout << "Before removal capacity: " << ls.size() << endl; //Print after removing values list<int> ::iterator it = ls.begin(); ls.erase(it); //Delete first value for (auto e : ls) { cout << e << " "; } cout << "\n After removal capacity: " << ls.size() << endl; it = ls.begin(); //Remove remaining values ls.erase(it,ls.end()); //Delete the value of a section for (auto e : ls) { cout << e << " "; } cout << "\n After removal capacity: " << ls.size() << endl; }
effect:
swap
There is also a swap function inside the list class. The swap function is often used to exchange elements in two list containers
Test:
void func2() { list<int>l1; //1 2 l1.push_back(1); l1.push_back(2); list<int>l2; //2 3 4 l2.push_back(2); l2.push_back(3); l2.push_back(4); l1.swap(l2); for (auto e : l1) { cout << e << " "; } //2 3 4 cout << endl; for (auto e : l2) { cout << e << " "; }// 1 2 }
From the running results, it can be seen that the swap function is indeed used to exchange elements in the container
Introduction to splice and remove functions
void func2() { list<int>L1, L2; L1.push_back(1); L1.push_back(2); L1.push_back(2); L1.push_back(3); L1.push_back(4); L2.push_back(10); list<int>::iterator it = L2.begin(); L2.splice(it, L1); for (auto e : L2) { cout << e << " "; //At this time, the elements in the L2 container are 1 2 2 3 4 10 } cout << endl; cout << " L1.size(): " << L1.size() << endl;//0 cout << " L2.size(): " << L2.size() << endl;//6 L2.remove(10); for (auto e : L2) { cout << e << " "; //At this time, the elements in the L2 container are 1 2 2 3 4 10 } cout << endl; L2.remove(10); //Remove will only delete the existing elements in the container. If there is no such value, remove will not process for (auto e : L2) { cout << e << " "; //At this time, the elements in the L2 container are 1 2 2 3 4 10 } }
unique
Function: de duplication
Note: unique will only process all elements except the first element in consecutive equal element groups.
Test:
void func2() { int arr[] = {1,2,2,2,2,5,2,3,2,5}; list<int> ls(arr, arr+ sizeof(arr) / sizeof(arr[0]) - 1); cout << "Container creation" << endl; for (auto e : ls) { cout << e << " ";//1 2 2 2 2 5 2 3 2 } cout << "\n Start weight removal" << endl; ls.unique(); for (auto e : ls) { cout << e << " ";//1 2 5 2 3 2 } cout << "\n--------------------------" << endl; //Phenomenon: the remaining duplicate values in the container are not removed //Reason 1: unique will only process all elements except the first element in consecutive equal element groups. //Reason 2: the array is out of order int arr1[] = { 1,2,2,2,2,5,2,3,2,5 }; list<int> ls1(arr1, arr1 + sizeof(arr1) / sizeof(arr1[0]) - 1); cout << "\n Container creation" << endl; for (auto e : ls1) { cout << e << " ";//1 2 2 2 2 5 2 3 2 } cout << "\n Start weight removal" << endl; ls1.sort(); //Sort before de duplication ls1.unique(); for (auto e : ls1) { cout << e << " ";//1 2 3 5 } }
Discussion on iterator failure
The function of the program is to remove even values from the container
void func3() { int arr[] = { 1,2,3,4,5,6 }; list<int> ls(arr,arr + sizeof(arr) / sizeof(arr[0]) - 1); list<int>::iterator it = ls.begin(); while (it != ls.end()) { if (*it % 2 == 0) { ls.erase(it); //erase will delete the node it points to, //When this node is deleted, the space it points to will be deleted //It is recycled by the operating system, and the programmer has no permission to use it, //When dereferencing again, there will be illegal access and the program will crash } it++; } for (auto e : ls) { cout << e << " "; // The expected results are: 1 3 5 } }
When the program runs, it crashes directly
Solution:
void func3() { int arr[] = { 1,2,3,4,5,6 }; list<int> ls(arr,arr + sizeof(arr) / sizeof(arr[0]) - 1); list<int>::iterator it = ls.begin(); while (it != ls.end()) { if (*it % 2 == 0) { it = ls.erase(it); //After deleting the value, the iterator of the next element of this element is returned, // It is updated, even if the old value fails, it will not be affected } else { it++; } } }
list Simulation Implementation
Class declaration
template<class T> struct __list_node { __list_node<T>* prev; __list_node<T>* next; T data; //All default parameters are provided. Anonymous objects are used here, //T() will call the constructor of type T __list_node(const T& val = T()) :prev(nullptr) ,next(nullptr) ,data(val) { } };
Define the _list_node structure for creating nodes
Iterator class
The behavior used to simulate the native pointer is different from the native pointer, but the behavior of the simulation implementation is similar
template<class T,class Ref, class Ptr> struct __list_iterator { typedef __list_iterator<T, Ref, Ptr> Self; typedef __list_node<T> Node; Node* _node; __list_iterator(Node* node) :_node(node) { } Ref operator*() { return _node->data; } Ptr operator->() { return &_node->data; } bool operator!=(const Self &it) { return it._node != _node; } Self operator++(int) { Self tmp((*this)._node); ++(*this); return tmp; } Self& operator++() { _node = _node->next; return *this; } Self& operator--() { _node = _node->prev; return *this; } Self operator--(int) { Self tmp((*this)._node); --(*this); return tmp; } };
Template parameters
//Three template parameters are provided here // T: Represents the type of data //Ref: indicates the type of reference //Ptr: indicates the pointer type template<class T,class Ref, class Ptr>
Iterator framework
//The three template parameters have been explained earlier template<class T,class Ref, class Ptr> struct __list_iterator { typedef __list_iterator<T, Ref, Ptr> Self; //Aliasing iterator types with typedef is simpler, //Therefore, this method is recommended. In addition, it can also be used for different template parameter types //Generate different types of iterator objects, and readers can experience the wonderful use of them typedef __list_node<T> Node; Node* _node;//The _node here is used to record the position of the iterator at this time //Call the iterator's constructor with a pointer of type Node as a parameter //To initialize the member _node of the iterator's object __list_iterator(Node* node) :_node(node) //Initialize list mode { } };
Here, I want readers to understand the framework of list first, and then look at the interface functions implemented later, so as to facilitate the overall learning
Operator * and operator - >
//Overload * operator Ref operator*() //The type of Ref is related to the template parameter type and is referenced or often referenced { return _node->data; //As mentioned above, _node is used to record the position of iterators, //Dereference to the pointer returns the value of the position } //Overload - > operator Ptr operator->() //The type of Ptr is related to the template parameter type, T * or const T* { return &_node->data; //Returning a pointer to an object allows you to access its member variables through - > object }
Front + + / - rear + + / –
//Post++ Self operator++(int) //Self: This is an iterator object { //Call the iterator constructor to create a local iterator object with a pointer, //tmp will be destroyed when it is out of scope, Self tmp((*this)._node); ++(*this); //Multiplex operator + + () return tmp; //Post + + returns the value before + + and creates a temporary object in the middle for return } Self& operator++() // Self: This is an iterator object { _node = _node->next;//Update the position of the iterator and go back return *this; //Pre + + returns the value after + + } Self& operator--()//Self: This is an iterator object { _node = _node->prev;//Update the position of the iterator and go ahead return *this; //Value before -- after -- after -- return } Self operator--(int)//Self: This is an iterator object { //Create a local iterator object using a pointer Self tmp((*this)._node); --(*this); //Multiplex operator -- () return tmp; //Post -- return -- previous value }
list class
Class declaration
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; list() { _head = new Node(); _head->prev = _head; _head->next = _head; } //copy constructor list(const list<T>& ls) { _head = new Node(); _head->next = _head; _head->prev = _head; for (auto& e : ls) { push_back(e); } } //operator= list<T>& operator=(list<T> ls) { swap(_head, ls._head); return *this; } //Returns the normal iterator iterator begin() { return iterator(_head->next); } iterator end() { return iterator(_head); } //const iterator const_iterator begin()const { return const_iterator(_head->next); } const_iterator end()const { return const_iterator(_head); } //Insert node at specified pos position void Insert(iterator pos, const T& val) { Node* cur = pos._node; Node* newNode = new Node(val); Node* prev = cur->prev; prev->next = newNode; newNode->prev = prev; newNode->next = cur; cur->prev = newNode; } //Tail insertion void push_back(const T& val) { Insert(end(),val); } //Head insert void push_Front(const T& val) { Insert(begin(), val); } iterator erase(iterator pos) { assert(pos._node != _head); Node* cur = pos._node; Node* prev = cur->prev; Node* next = cur->next; prev->next = next; next->prev = prev; delete cur; return next; } bool empty() { return _head->next == _head->prev; } //Tail deletion void Pop_back() { assert(!empty()); erase(--end()); } //Header deletion void Pop_Front() { assert(!empty()); erase(begin()); } //Empty container void clear() { iterator it = begin(); while (it != end()) { erase(it++); } } //Deconstruction ~list() { clear(); delete _head; _head = nullptr; } private: Node* _head; };
frame
template<class T> class list { //Recommended writing, concise typedef __list_node<T> Node; public: //Normal iterator type: readable and writable typedef __list_iterator<T, T&, T*> iterator; //const iterator type: read only typedef __list_iterator<T, const T&, const T*> const_iterator; private: Node* _head; //List is a two-way leading circular linked list structure, //So here we need to define a_ head node };
Similarly, in order to facilitate later learning, we should first understand the framework and sort out a good idea for the overall understanding
begin and end
//Returns the normal iterator iterator begin() { //Creates an iterator object using a pointer and returns a readable and writable iterator object //This iterator object records the location of the first node return iterator(_head->next); } iterator end() { //Creates an iterator object using a pointer and returns a readable and writable iterator object //This iterator object records the next location of the last node return iterator(_head); } //const iterator const_iterator begin()const { //Creates an iterator object using a pointer and returns a read-only iterator object //This iterator object records the location of the first node return const_iterator(_head->next); } const_iterator end()const { //Creates an iterator object using a pointer and returns a readable iterator object //This iterator object records the next location of the last node return const_iterator(_head); }
The above methods will produce temporary objects, which can be understood by readers
Add, delete, check and modify
//Insert node at specified pos position void Insert(iterator pos, const T& val) { Node* cur = pos._node; Node* newNode = new Node(val); Node* prev = cur->prev; prev->next = newNode; newNode->prev = prev; newNode->next = cur; cur->prev = newNode; } //Specify pos location to delete node iterator erase(iterator pos) { assert(pos._node != _head); Node* cur = pos._node; Node* prev = cur->prev; Node* next = cur->next; prev->next = next; next->prev = prev; delete cur; return next; }
Head to tail insertion
//Tail insertion void push_back(const T& val) { Insert(end(),val); } //Head insert void push_Front(const T& val) { Insert(begin(), val); }
Head deletion and tail deletion
bool empty() { return _head->next == _head->prev; } //Tail deletion void Pop_back() { assert(!empty()); erase(--end()); //end() returns yes_ head->prev, // --end() multiplex operator -- () //It can also be written in this form: erase (_head - > prev); } //Header deletion void Pop_Front() { assert(!empty()); erase(begin()); //begin() returns the iterator of the first node //erase deletes the first node }
Empty container
void clear() //Use iterators to traverse and delete nodes { iterator it = begin(); while (it != end()) { erase(it++); } }
Construction and copy construction
Constructor
list() //Constructor creates a header node { _head = new Node(); _head->prev = _head; _head->next = _head; }
copy constructor
//copy constructor list(const list<T>& ls) { //Create header node _head = new Node(); _head->next = _head; _head->prev = _head; //While traversing ls objects, take out the nodes and insert them at the end_ Behind the head for (auto& e : ls) { push_back(e); } }
operator=
//operator= list<T>& operator=(list<T> ls) { //Traditional writing if (this != &ls) { clear(); //Empty this object, leaving only the header node for (auto e : ls) { push_back(e); //Insert the node tail of ls object after this object } } }
operator=
list<T>& operator=(list<T> ls) { //Modern writing swap(_head, ls._head); //Swap the of this object_ Of head and ls objects_ head //_ head can connect nodes in ls objects return *this; }
Destructor
~list() { //Complete resource cleanup and release all nodes of the list object clear(); delete _head; _head = nullptr;//Prevent wild pointer }
Summarize the difference between vector and list:
vector is a dynamically growing array, which can support random access and some sorting, such as binary search, heap algorithm and so on
Disadvantages: the efficiency of header insertion and specified pos position insertion is low, because there is not enough space to move the data, and the capacity needs to be increased, which consumes performance
List is a two-way leading circular linked list. It is a linked list that inserts and deletes data at any position. The time efficiency is O(1)
Disadvantages: random access is not supported and traversal speed is slow
Summary: vector and list are two complementary containers