SoFunction
Updated on 2025-05-19

Detailed explanation of the usage examples of map and set in C++

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:setandmapRequire key unique;multisetandmultimapAllow 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

  • setThe statement is as follows, T issetThe type of underlying keyword.
  • setThe default requirement is that T supports less than comparison. If not, you can manually implement a functor passing to the second template parameter.
  • setThe 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.
  • setThe 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

setWe only need to pay attention to the structure of

//empty (1) Default construct without parametersexplicit set(const key_compare&amp; comp = key_compare(),
	 const allocator_type&amp; alloc = allocator_type());
// range (2) Iterator interval constructiontemplate &lt;class InputIterator&gt;
set(InputIterator first, InputIterator last,
	const key_compare&amp; comp = key_compare(),
	const allocator_type &amp; = allocator_type());
// copy (3) copy constructset (const set&amp; x);
// initializer list (5) initializer list constructionset (initializer_list&lt;value_type&gt; il,
 const key_compare&amp; comp = key_compare(),
 const allocator_type&amp; alloc = allocator_type());

Iterator

// Iterator is a bidirectional iteratoriterator -&gt; 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 &lt;iostream&gt;
#include &lt;set&gt;
using namespace std;
int main()
{
	//Deduplication + ascending order sort	set&lt;int&gt; s;
	//Deduplication + descending order, give a simulated function greater than	//set&lt;int, greater&lt;int&gt;&gt; s;
	//set<int, greater<int>> s = {1,2,7,4,9,6,}; initializer_list initialization	(2);
	(1);
	(5);
	(6);
	//set&lt;int&gt; iterator it =  ();
	auto it = ();
	while (it != ())
	{
		//*it = 1; the value inside cannot be modified		cout &lt;&lt; *it &lt;&lt; " ";
		it++;
	}
	cout &lt;&lt; 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 &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; endl;
	//Insert string class object, the comparison of string class object is based on the ascll code to compare the size.	set&lt;string&gt; strset = {"sort","add","insert"};
	for (auto &amp;e : strset)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; endl;
	return 0;
}

2.5 Examples of use of find and erase

erase,findUse cases:

int main()
{
	set&lt;int&gt; s = {2,7,4,3,1,9,5,0};
	for (auto&amp; e : s)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; endl;
	//Delete the minimum value	(());
	for (auto&amp; e : s)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; endl;
	//Delete the input value, this value may or may not be there	int x;
	cin &gt;&gt; x;
	int num = (x);
	if (num == 0)
	{
		cout &lt;&lt; x  &lt;&lt; "Not exists!" &lt;&lt; endl;
	}
	for (auto&amp; e : s)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; endl;
	//Search directly, and then use the iterator to delete	cin &gt;&gt; x;
	auto pos = (x);
	if (pos != ())
	{
		(pos);
	}
	else
	{
		cout &lt;&lt; x &lt;&lt; "Not exists" &lt;&lt; endl;
	}
	for (auto&amp; e : s)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; 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 &gt;&gt; x;
	if ((x))
	{
		cout &lt;&lt; x &lt;&lt; "exist!" &lt;&lt; endl;
	}
	else
	{
		cout &lt;&lt; x &lt;&lt; "Not exists!" &lt;&lt; endl;
	}
	return 0;
}

Delete the value of a interval

int main()
{
	set&lt;int&gt; myset;
	for (int i = 1; i &lt; 10; i++)
	{
		(i * 10);//10 20 30 40 50 60 70 80 90
	}
	for (auto e : myset)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; 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 &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; endl;
	return 0;
}

2.6 Differences between multiset and set

multisetandsetThe main difference ismultisetSupport key-value redundancy, theninsert/find/count/eraseAll revolve around supporting key-value redundancy.

int main()
{
	multiset&lt;int&gt; s = {4,6,4,3,6,7,8,9,2,5,3,7,8,9};
	auto it = ();
	while (it != ())
	{
		cout &lt;&lt; *it &lt;&lt; " ";
		++it;
	}
	cout &lt;&lt; endl;
	// Compared to set, there may be multiple x, and find is the first in the middle order.	int x;
	cin &gt;&gt; x;
	auto pos = (x);
	while (pos != () &amp;&amp; *pos == x)
	{
		cout &lt;&lt; *pos &lt;&lt; " ";
		++pos;
	}
	cout &lt;&lt; endl;
	//The difference compared to set is that count will return the actual number of x	cout &lt;&lt; (x) &lt;&lt; endl;
	//The difference compared to set is that erase will delete all x in it	(x);
	for (auto e : s)
	{
		cout &lt;&lt; e &lt;&lt; " ";
	}
	cout &lt;&lt; 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&lt;int&gt; intersection(vector&lt;int&gt;&amp; nums1, vector&lt;int&gt;&amp; nums2) {
        // Here we use set to implement sorting + deregulating arrays        set&lt;int&gt; s1((),());//Initialize with a segment of iterator interval        set&lt;int&gt; s2((),());
        // Small ++, equality means intersection        vector&lt;int&gt; ret;
        auto it1 = ();
        auto it2 = ();
        while(it1 != () &amp;&amp; it2 != ())
        {
            if(*it1 &lt; *it2) it1++;
            else if (*it1 &gt; *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, ifcountIt 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&lt;ListNode*&gt; s;
        ListNode* cur = head;
        while(cur)
        {
            if((cur))
                return cur;//Equal to 1 is the entry point            else
                (cur);
            cur = cur -&gt; 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

mapThe 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.mapThe underlying memory for storing data is applied from the space configurator. Generally, we do not need to pass the next two template parameters.mapThe 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

mapThe data in the underlying red and black tree node is used to store key-value pair data using pair<Key,T>.
pairIt 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

mapWe only need to pay attention to the following interfaces
mapSupport 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.mapsupportvalueData modification, but not supportedKeymodification. Modifying the keyword data destroys the underlying search tree structure.

// empty (1) Default construct without parametersexplicit map (const key_compare&amp; comp = key_compare(),
 const allocator_type&amp; alloc = allocator_type());
// range (2) Iterator interval constructiontemplate &lt;class InputIterator&gt;
 map (InputIterator first, InputIterator last,
 const key_compare&amp; comp = key_compare(),
 const allocator_type&amp; = allocator_type());
// copy (3) copy constructmap (const map&amp; x);
// initializer list (5) initializer list constructionmap (initializer_list&lt;value_type&gt; il,
 const key_compare&amp; comp = key_compare(),
 const allocator_type&amp; alloc = allocator_type());
// Iterator is a bidirectional iteratoriterator -&gt; 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

mapPlease follow the following interfaces for adding, deleting, checking and searching
mapThe increment interface is inserted into pair key-value pair data, andsetThere are differences, but the interfaces for checking and deleting only use the keyword Key, andsetExactly 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-&gt;The first template parameter(Key)
mapped_type-&gt;The second template parameter(T)
value_type-&gt;pair&lt;const key_type, mapped_type&gt;
// 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&lt;iterator, bool&gt; insert(const value_type&amp; val);
// List insertion, values ​​that already exist in the container will not be insertedvoid insert(initializer_list&lt;value_type&gt; il);
// Iterator interval is inserted, and values ​​that already exist in the container will not be insertedtemplate &lt;class InputIterator&gt;
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&amp; k);
// Find k and return the number of ksize_type count(const key_type&amp; 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&amp; 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&amp; k);
// Returns an iterator greater than k positionconst_iterator lower_bound(const key_type&amp; 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-&gt;The first template parameter(Key)
mapped_type-&gt;The second template parameter(T)
value_type-&gt;pair&lt;const key_type, mapped_type&gt;
// 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&amp; 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&lt;iterator, bool&gt; insert(const value_type &amp; val);
mapped_type&amp; operator[] (const key_type&amp; k);
// Internal implementation of operatormapped_type&amp; operator[] (const key_type&amp; 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&lt;iterator, bool&gt; ret = insert({ k, mapped_type() });
	iterator it = ;
	return it-&gt;second;
}

3.7 Examples of constructing traversal and adding, deleting, searching and modifying samples

#include &lt;map&gt;
int main()
{
	pair&lt;string, string&gt; kv1 = {"left","left"};
	//Initializer_list construction and iterative traversal	map&lt;string, string&gt; dict = { {"left","left"},{"right","right"},{"insert","insert"},{"erase","delete"}};
	map&lt;string, string&gt;::iterator it = ();
	while (it != ())
	{
		//cout << *it << " "; //pair does not support stream extraction and stream insertion, it is a structure		//cout &lt;&lt; (*it).first &lt;&lt; ":" &lt;&lt; (*it).second &lt;&lt; endl;;
		cout &lt;&lt; it-&gt;first &lt;&lt; ":" &lt;&lt; it-&gt;second &lt;&lt; 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&lt;string, string&gt; kv2("first","First");
	(kv2);
	//Anonymous object	(pair&lt;string, string&gt;("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&amp; e : dict)
	{
		cout &lt;&lt;  &lt;&lt; ":" &lt;&lt;  &lt;&lt; endl;
	}
	cout &lt;&lt; endl;
	//Structured binding C++17	//for (const auto&amp; [k,v] : dict)
	//{
	//	cout &lt;&lt; k &lt;&lt; ":" &lt;&lt; v &lt;&lt; endl;
	//}
	string str;
	while (cin &gt;&gt; str)
	{
		auto ret = (str);
		if (ret != ())
		{
			cout &lt;&lt; "-&gt;" &lt;&lt; ret-&gt;second &lt;&lt; endl;
		}
		else
		{
			cout &lt;&lt; "If there is no such word, please re-enter" &lt;&lt; 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 &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;map&gt;
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&lt;string, int&gt; 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&amp; str : arr)
	{
		auto ret = (str);
		if (ret == ())//Then it proves that		{
			({str,1});
		}
		// Otherwise, in		else
		{
			ret-&gt;second++;
		}
	}
	//countMap[str]++; The second way to write	for (const auto&amp; e : countMap)
	{
		cout &lt;&lt;  &lt;&lt; ":" &lt;&lt;  &lt;&lt; endl;
	}
	cout &lt;&lt; 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/eraseAll revolve around supporting key-value redundancy. Here andsetandmultisetBasically 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 passmapEstablish mapping relationships
Code implementation:

class Solution {
public:
    Node* copyRandomList(Node* head) {
        map&lt;Node*,Node*&gt; 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-&gt;val);
            }
            //If not empty            else
            {
                copytail-&gt;next = new Node(cur-&gt;val);
                copytail = copytail-&gt;next;
            }
            //Let the original node and copy node establish map            nodeMap[cur] = copytail;
            cur = cur-&gt;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-&gt;random == nullptr)
            {
                copy-&gt;random = nullptr;
            }
            else
            {
                copy-&gt;random = nodeMap[cur-&gt;random];
            }
            cur = cur-&gt;next;
            copy = copy-&gt;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&lt;string,int&gt;&amp; kv1,const pair&lt;string,int&gt;&amp; kv2)
        {
            return  &gt; ;
        }
    }; 
    vector&lt;string&gt; topKFrequent(vector&lt;string&gt;&amp; words, int k) {
        //Statistics        map&lt;string,int&gt; countMap;
        for(auto &amp;str : words)
        {
            countMap[str]++;
        }
        vector&lt;pair&lt;string,int&gt;&gt; v((),());//Iterator interval initialization        stable_sort((),(),Compare());
        vector&lt;string&gt; retv;
        for(size_t i = 0; i &lt; 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&lt;string, int&gt;&amp; x, const pair&lt;string, int&gt;&amp; y)
		{
			return  &gt;  || ( ==  &amp;&amp;  &lt; );;
		}
	};
	vector&lt;string&gt; topKFrequent(vector&lt;string&gt;&amp; words, int k) 
	{
		map&lt;string, int&gt; countMap;
		for (auto&amp; e : words)
		{
			countMap[e]++;
		}
		vector&lt;pair&lt;string, int&gt;&gt; v((), ());
		// Descending function controls descending order, functor controls equal times, and the dictionary order is in front		sort((), (), Compare());
		// Take the first k		vector&lt;string&gt; strV;
		for (int i = 0; i &lt; k; ++i)
		{
			strV.push_back(v[i].first);
		}
		return strV;
	}
};
class Solution {
public:
	struct Compare
	{
		bool operator()(const pair&lt;string, int&gt;&amp; x, const pair&lt;string, int&gt;&amp; 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  &lt;  || ( ==  &amp;&amp;  &gt; );
		}
	};
	vector&lt;string&gt; topKFrequent(vector&lt;string&gt;&amp; words, int k)
	{
		map&lt;string, int&gt; countMap;
		for (auto&amp; 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&lt;pair&lt;string, int&gt;, vector&lt;pair&lt;string, int&gt;&gt;, Compare&gt;p((), ());
		vector&lt;string&gt; strV;
		for (int i = 0; i &lt; 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!