1. Serial containers and associated containers
1.1 Serial container
Core features
- Store in insertion order: The physical storage order of elements is consistent with the insertion order.
- Allow duplicate elements: Multiple identical values can be stored.
- Access by location: Access elements directly through indexes (such as arrays) or iterators.
Common containers
-
vector
:Dynamic array, supports fast random access, and efficient tail insertion. -
list
:Boy-directional linked list, supports quick insertion/deletion, but is inefficient in random access. -
deque
: Dual-ended queue, supports efficient head-to-tail insertion/deletion. -
array
: Fixed size array, determine the size during compilation. -
forward_list
:One-directional linked list, with less memory usage.
Adapt to the scene
- The insertion order needs to be maintained (such as logging, operation history).
- Frequently accessed randomly through indexes (such as the [ ] operation of vector).
- Efficient head and tail operations (such as deque) are required.
1.2 Associated containers
- Core features
- Key storage: Elements are stored based on key-value keys themselves.
- Automatic sorting: the sorting rules of elements (default ascending order).
- Uniqueness:
set
andmap
Require key unique;multiset
andmultimap
Allow duplicate keys.
- Common containers
-
set
: Ordered sets of unique keys. -
map
:Storing ordered mapping of key-value pairs. -
multiset
: Ordered sets of repeated keys allowed. -
multimap
: Allows ordered mapping of repeated keys.
-
- Applicable scenarios
- Need to find quickly (such as dictionary, database index).
- It needs to be sorted automatically (such as ranking list, orderly event scheduling).
- Unique constraints (such as user ID management) are required.
2. Use of set series
2.1 Reference documentation for set and multiset
Click to arrive quickly
2.2 Introduction to set class
-
set
The statement is as follows, T isset
The type of underlying keyword. -
set
The default requirement is that T supports less than comparison. If not, you can manually implement a functor passing to the second template parameter. -
set
The underlying memory for storing data is applied from the space configurator. If necessary, you can manually implement a memory pool yourself and pass it to the third parameter. Generally speaking, we do not need to pass the two template parameters. -
set
The bottom layer is implemented using a red and black tree. The efficiency of adding, deleting and searching is O (logn). The iterator traversal is the middle order traversal of the search tree, so the traversal elements are ordered.
template < class T, // set::key_type/value_type class Compare = less<T>, // set::key_compare/value_compare class Alloc = allocator<T> // set::allocator_type > class set;
2.3 Set's constructor and iterator
set
We only need to pay attention to the structure of
//empty (1) Default construct without parametersexplicit set(const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); // range (2) Iterator interval constructiontemplate <class InputIterator> set(InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type & = allocator_type()); // copy (3) copy constructset (const set& x); // initializer list (5) initializer list constructionset (initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
Iterator
// Iterator is a bidirectional iteratoriterator -> a bidirectional iterator to const value_type // Forward iteratoriterator begin(); iterator end(); // Reverse iteratorreverse_iterator rbegin(); reverse_iterator rend();
2.4 Set's addition and deletion
#include <iostream> #include <set> using namespace std; int main() { //Deduplication + ascending order sort set<int> s; //Deduplication + descending order, give a simulated function greater than //set<int, greater<int>> s; //set<int, greater<int>> s = {1,2,7,4,9,6,}; initializer_list initialization (2); (1); (5); (6); //set<int> iterator it = (); auto it = (); while (it != ()) { //*it = 1; the value inside cannot be modified cout << *it << " "; it++; } cout << endl; //Insert a value of initializer_list, if it already exists, the insertion fails ({1,5,3,2,7,9}); for (auto e : s) { cout << e << " "; } cout << endl; //Insert string class object, the comparison of string class object is based on the ascll code to compare the size. set<string> strset = {"sort","add","insert"}; for (auto &e : strset) { cout << e << " "; } cout << endl; return 0; }
2.5 Examples of use of find and erase
erase
,find
Use cases:
int main() { set<int> s = {2,7,4,3,1,9,5,0}; for (auto& e : s) { cout << e << " "; } cout << endl; //Delete the minimum value (()); for (auto& e : s) { cout << e << " "; } cout << endl; //Delete the input value, this value may or may not be there int x; cin >> x; int num = (x); if (num == 0) { cout << x << "Not exists!" << endl; } for (auto& e : s) { cout << e << " "; } cout << endl; //Search directly, and then use the iterator to delete cin >> x; auto pos = (x); if (pos != ()) { (pos); } else { cout << x << "Not exists" << endl; } for (auto& e : s) { cout << e << " "; } cout << endl; //Lookup O(n) in the algorithm library will not be used like this auto pos1 = find((), (), x); //Set's own implementation of search O(logn) auto pos2 = (x); //Use cout to quickly realize indirect search cin >> x; if ((x)) { cout << x << "exist!" << endl; } else { cout << x << "Not exists!" << endl; } return 0; }
Delete the value of a interval
int main() { set<int> myset; for (int i = 1; i < 10; i++) { (i * 10);//10 20 30 40 50 60 70 80 90 } for (auto e : myset) { cout << e << " "; } cout << endl; //The [itlow,itup] found in the implementation contains the [30,60] interval //Return>=30 auto itlow = myset.lower_bound(30); //Return>60 auto itup = myset.upper_bound(60); //Delete the value of this interval (itlow,itup); for (auto e : myset) { cout << e << " "; } cout << endl; return 0; }
2.6 Differences between multiset and set
multiset
andset
The main difference ismultiset
Support key-value redundancy, theninsert
/find
/count
/erase
All revolve around supporting key-value redundancy.
int main() { multiset<int> s = {4,6,4,3,6,7,8,9,2,5,3,7,8,9}; auto it = (); while (it != ()) { cout << *it << " "; ++it; } cout << endl; // Compared to set, there may be multiple x, and find is the first in the middle order. int x; cin >> x; auto pos = (x); while (pos != () && *pos == x) { cout << *pos << " "; ++pos; } cout << endl; //The difference compared to set is that count will return the actual number of x cout << (x) << endl; //The difference compared to set is that erase will delete all x in it (x); for (auto e : s) { cout << e << " "; } cout << endl; return 0; }
2.7 The intersection of two arrays - LeetCode
Question link: 349. Intersection of two arrays
Problem solution:
The two arrays are compared in sequence, the small ++, and the equal ones are intersection, and at the same time, ++, one of them ends when it ends.
Code implementation:
class Solution { public: vector<int> intersection(vector<int>& nums1, vector<int>& nums2) { // Here we use set to implement sorting + deregulating arrays set<int> s1((),());//Initialize with a segment of iterator interval set<int> s2((),()); // Small ++, equality means intersection vector<int> ret; auto it1 = (); auto it2 = (); while(it1 != () && it2 != ()) { if(*it1 < *it2) it1++; else if (*it1 > *it2) it2++; else { ret.push_back(*it1); it1++; it2++; } } return ret; } };
2.8 Ring List II - LeetCode
Title link:142. Ring List II
Problem solution:
Iterate through this circular linked list, ifcount
It is 0, insert this node intoset
, If not 0, this node is the entry point.
Code implementation:
class Solution { public: ListNode *detectCycle(ListNode *head) { set<ListNode*> s; ListNode* cur = head; while(cur) { if((cur)) return cur;//Equal to 1 is the entry point else (cur); cur = cur -> next; } return nullptr; } };
3. Use of map series
3.1 Reference documentation for map and multimap
Click to arrive quickly
3.2 Introduction to map class
map
The declaration is as follows. Key is the type of the underlying keyword of map, T is the type of the underlying value of map. Map requires that Key supports less than comparison by default. If it is not supported or required, you can implement the functor to pass it to the second template parameter by yourself.map
The underlying memory for storing data is applied from the space configurator. Generally, we do not need to pass the next two template parameters.map
The underlying layer is implemented using a red and black tree. The efficiency of addition, deletion, search and modification is O (logn). The iterator traversal is in-order traversal, so the value of the Key is ordered.
template < class Key, // map::key_type class T, // map::mapped_type class Compare = less<Key>, // map::key_compare class Alloc = allocator<pair<const Key,T> > // map::allocator_type > class map
3.3 Introduction to pair type
map
The data in the underlying red and black tree node is used to store key-value pair data using pair<Key,T>.pair
It is a class template with two member variables, one isfirst
,One issecond
。
typedef pair<const Key, T> value_type; template <class T1, class T2> struct pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; pair() : first(T1()), second(T2()) {} pair(const T1& a, const T2& b) : first(a), second(b) {} template<class U, class V> pair(const pair<U, V>& pr) : first(), second() {} }; template <class T1, class T2> inline pair<T1, T2> make_pair(T1 x, T2 y) { return (pair<T1, T2>(x, y)); }
3.4 Map construction
map
We only need to pay attention to the following interfacesmap
Support forward iterator traversal and reverse iterator traversal. The traversal is in ascending order of the Key by default, because the underlying layer is a binary search tree, and the middle order traversal is adopted. Supporting iterators also supports scope for.map
supportvalue
Data modification, but not supportedKey
modification. Modifying the keyword data destroys the underlying search tree structure.
// empty (1) Default construct without parametersexplicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); // range (2) Iterator interval constructiontemplate <class InputIterator> map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& = allocator_type()); // copy (3) copy constructmap (const map& x); // initializer list (5) initializer list constructionmap (initializer_list<value_type> il, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type()); // Iterator is a bidirectional iteratoriterator -> a bidirectional iterator to const value_type // Forward iteratoriterator begin(); iterator end(); // Reverse iteratorreverse_iterator rbegin(); reverse_iterator rend();
3.5 map addition and deletion
map
Please follow the following interfaces for adding, deleting, checking and searchingmap
The increment interface is inserted into pair key-value pair data, andset
There are differences, but the interfaces for checking and deleting only use the keyword Key, andset
Exactly similar. But fin returnsiterator
, You can not only find whether the Key is present or not, but also find the mapped Value, and you can also modify the Value through iteration.
Member types key_type->The first template parameter(Key) mapped_type->The second template parameter(T) value_type->pair<const key_type, mapped_type> // Insert a single data, if the key already exists, the insertion fails. If the key exists equal value and is not equal, the insertion fails.pair<iterator, bool> insert(const value_type& val); // List insertion, values that already exist in the container will not be insertedvoid insert(initializer_list<value_type> il); // Iterator interval is inserted, and values that already exist in the container will not be insertedtemplate <class InputIterator> void insert(InputIterator first, InputIterator last); // Find k, return the iterator where k is located, and return end() is not founditerator find(const key_type& k); // Find k and return the number of ksize_type count(const key_type& k) const; // Delete the value of the position of an iteratoriterator erase(const_iterator position); // Delete k, k exists and returns 0, and exists and returns 1size_type erase(const key_type& k); // Delete the value of an iterator intervaliterator erase(const_iterator first, const_iterator last); // Returns an iterator with positions greater than or equal kiterator lower_bound(const key_type& k); // Returns an iterator greater than k positionconst_iterator lower_bound(const key_type& k) const;
3.6 Map data modification
The first way map supports modification is to use iterator to traverse iterator or find to return the iterator where the key is located. Map also has a very important modification interface.operator[]
,butoperator[]
It not only supports data modification, but also supports inserting and searching data, so it is a multi-functional composite interface.
Member types key_type->The first template parameter(Key) mapped_type->The second template parameter(T) value_type->pair<const key_type, mapped_type> // Find k and return the iterator where k is located. If it is not found, return end(). If it is found, the mapped_type value corresponding to the key can be modified through the iterator.iterator find(const key_type& k); // Description of insert return value in the document// The single element versions (1) return a pair, with its member pair::first set to an iterator pointing to either the newly inserted element or to theelement with an equivalent key in the pair::second element in the pairis set to true if a new element was inserted or false if an equivalent keyalready existed. // insert an insert object// 1. If the key is already in the map and the insertion fails, a pair<iterator,bool> object is returned, and the pair object first is the iterator of the node where the key is located, and second is false// 2. If the key is not in the map and the insertion is successful, a pair<iterator,bool> object is returned, and the pair object first is the iterator of the newly inserted key node, second is true// In other words, no matter whether the insertion is successful or failed, the first return to the pair<iterator, bool> object will point to the iterator where the key is located// This means that insert acts as a search function when insert fails. It is precisely because of this that insert can be used to implement it.operator[] // It should be noted that there are two pairs here, so don't confuse it. One is the pair<key, T> stored in the red tree node at the bottom of the map, and the other is the insert return value pair<iterator, bool>pair<iterator, bool> insert(const value_type & val); mapped_type& operator[] (const key_type& k); // Internal implementation of operatormapped_type& operator[] (const key_type& k) { // 1. If k is not in the map, insert will insert the default values of k and mapped_type (the default value is constructed with the default construction), and at the same time [] returns a reference to the mapped_type value stored in the node, then we can modify the return mapped value through reference. So[] has the insert + modification function // 2. If k is in map, insert will fail, but insert returns the first of the pair object, which is an iterator pointing to the key node. The return value is also [] and the reference to the mapped_type value is stored in the node, so [] has the function of search + modification pair<iterator, bool> ret = insert({ k, mapped_type() }); iterator it = ; return it->second; }
3.7 Examples of constructing traversal and adding, deleting, searching and modifying samples
#include <map> int main() { pair<string, string> kv1 = {"left","left"}; //Initializer_list construction and iterative traversal map<string, string> dict = { {"left","left"},{"right","right"},{"insert","insert"},{"erase","delete"}}; map<string, string>::iterator it = (); while (it != ()) { //cout << *it << " "; //pair does not support stream extraction and stream insertion, it is a structure //cout << (*it).first << ":" << (*it).second << endl;; cout << it->first << ":" << it->second << endl; //cout << ->()->first << ":" << ->()->second << endl;//Native writing ++it; } // Map insertion //The four ways to insert the pair, the last one is the most convenient way to compare pair<string, string> kv2("first","First"); (kv2); //Anonymous object (pair<string, string>("second", "Second")); //make_pair (make_pair("sort","Sorting"));//make_pair is a function template. You don’t need to declare template parameters. You can deduce it yourself. Construct a pair object at the bottom and then return it. //The last one is best ({"auto","Automatic"});//Support implicit type conversion //Support scope for traversal for (auto& e : dict) { cout << << ":" << << endl; } cout << endl; //Structured binding C++17 //for (const auto& [k,v] : dict) //{ // cout << k << ":" << v << endl; //} string str; while (cin >> str) { auto ret = (str); if (ret != ()) { cout << "->" << ret->second << endl; } else { cout << "If there is no such word, please re-enter" << endl; } } //First cannot be modified, but second can be modified return 0; }
3.8 Map's iterator function and [] function example
Statistics the number of times fruit appears
#include <iostream> #include <string> #include <map> using namespace std; int main() { //Count the number of times fruit appears string arr[] = { "apple", "watermelon", "apple", "watermelon", "apple", "apple", "watermelon","apple", "banana", "apple", "banana" }; map<string, int> countMap; //First find out if the fruit is in the map //1. Not present, indicating that the fruit appears for the first time, then insert the fruit {fruit, 1} //2. In, the corresponding number of times the fruit found is +1 for (const auto& str : arr) { auto ret = (str); if (ret == ())//Then it proves that { ({str,1}); } // Otherwise, in else { ret->second++; } } //countMap[str]++; The second way to write for (const auto& e : countMap) { cout << << ":" << << endl; } cout << endl; return 0; }
3.9 Differences between multimap and map
The use of multimap and map is basically exactly similar. The main difference is that multimap supports keyword Key redundancy, soinsert
/find
/count
/erase
All revolve around supporting key-value redundancy. Here andset
andmultiset
Basically the same, for example, when find, there are multiple keys to return the first in the order. Secondly, multimap does not support [], because it supports key redundancy, [] can only support insertion and modification.
3.10 Copying of random linked lists - LeetCode
Question link: 138. Copying of random linked lists
Problem solution:Let the original and copy list passmap
Establish mapping relationships
Code implementation:
class Solution { public: Node* copyRandomList(Node* head) { map<Node*,Node*> nodeMap; Node* copyhead = nullptr,*copytail = nullptr;//Define one head and one tail Node* cur = head; while(cur) { //If empty if(copytail == nullptr) { copyhead = copytail = new Node(cur->val); } //If not empty else { copytail->next = new Node(cur->val); copytail = copytail->next; } //Let the original node and copy node establish map nodeMap[cur] = copytail; cur = cur->next; } //Processing random cur = head; Node* copy = copyhead; while(cur) { //The random of the original linked list is empty, and the random of the copy linked list is also empty if(cur->random == nullptr) { copy->random = nullptr; } else { copy->random = nodeMap[cur->random]; } cur = cur->next; copy = copy->next; } return copyhead; } };
3.11 The first K high-frequency words - LeetCode
Title link: 692. The first K high-frequency words
Solution 1:
Use sorting to find the first k words, because the Key words have been sorted in the map, which means that words with the same order when traversing the map, the dictionary order is in front and the dictionary order is in the back. Then we put the data into the vector and use a stable sort to achieve the above special requirements, but the bottom layer of sort is fast ranking, which is unstable, so we need to usetable_sort
, is stable.
Code implementation:
class Solution { public: struct Compare { bool operator()(const pair<string,int>& kv1,const pair<string,int>& kv2) { return > ; } }; vector<string> topKFrequent(vector<string>& words, int k) { //Statistics map<string,int> countMap; for(auto &str : words) { countMap[str]++; } vector<pair<string,int>> v((),());//Iterator interval initialization stable_sort((),(),Compare()); vector<string> retv; for(size_t i = 0; i < k ; ++i) { retv.push_back(v[i].first);//What you take is the word in each pair object } return retv; } };
Solution 2:
Put the order counted by maps into vector, or select the first k in priority_queue, and use functors to force the number of times to control the small dictionary order in front.
Code implementation:
class Solution { public: struct Compare { bool operator()(const pair<string, int>& x, const pair<string, int>& y) { return > || ( == && < );; } }; vector<string> topKFrequent(vector<string>& words, int k) { map<string, int> countMap; for (auto& e : words) { countMap[e]++; } vector<pair<string, int>> v((), ()); // Descending function controls descending order, functor controls equal times, and the dictionary order is in front sort((), (), Compare()); // Take the first k vector<string> strV; for (int i = 0; i < k; ++i) { strV.push_back(v[i].first); } return strV; } };
class Solution { public: struct Compare { bool operator()(const pair<string, int>& x, const pair<string, int>& y) const { // Note that the bottom layer of the priority queue is inverse, and the heap must be implemented less than comparison, so the number of times is equal. If you want a small dictionary order, you must compare the large dictionary order before the first one is true return < || ( == && > ); } }; vector<string> topKFrequent(vector<string>& words, int k) { map<string, int> countMap; for (auto& e : words) { countMap[e]++; } // Put the <word, number of times> in the map into priority_queue, and the functor controls the heap, and the number of times is the same, sorted according to the dictionary order rules. priority_queue<pair<string, int>, vector<pair<string, int>>, Compare>p((), ()); vector<string> strV; for (int i = 0; i < k; ++i) { strV.push_back(().first); (); } return strV; }
This is the end of this article about the use of C++ map and set. For more related content on C++ map and set, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!