set / multiset 容器
set/multiset containers
Set Basic Concepts
A set is a well-defined collection of distinct objects, called the elements or members of the set. Sets are typically denoted by uppercase letters (e.g., A, B, C), and their elements are enclosed in curly braces. For example:
- A = {1, 2, 3} is a set containing the numbers 1, 2, and 3.
- B = {"apple", "banana", "cherry"} is a set of strings.
Key properties:
- Uniqueness: Each element in a set is unique (no duplicates).
- Unordered: The arrangement of elements does not matter (e.g., {1, 2, 3} = {3, 1, 2}).
Common notation:
- Element of:
x ∈ Ameans "x is an element of set A." - Empty set: ∅ or {} represents a set with no elements.
- Subset:
A ⊆ Bmeans "A is a subset of B" (all elements of A are in B).
Sets are fundamental in mathematics and computer science for grouping and organizing data.
Introduction:
- All elements will be automatically sorted upon insertion.
Essence:
- Sets and multisets are associative containers, implemented using binary trees as their underlying structure.
Difference between set and multiset:
set: Stores unique elements; each value appears only once.multiset: Allows duplicate elements; the same value can appear multiple times.
Example: In a set, inserting "apple", "banana", "apple" results in {"apple", "banana"}. In a multiset, the same insertion yields {"apple", "apple", "banana"}.
- Sets do not allow duplicate elements in a container.
- A multiset permits duplicate elements in the container.
Set construction and assignment
Functional Description: Creating a set container and assigning values to it.
Structure:
set<T> st;//Default constructor:set(const set &st);//copy constructor
Assignment:
set& operator=(const set &st);//overloading the equality operator
Example:
#include <iostream>
#include <set>
using namespace std;
void printSet(set<int> & s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//构造和赋值
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
printSet(s1);
//拷贝构造
set<int>s2(s1);
printSet(s2);
//赋值
set<int>s3;
s3 = s2;
printSet(s3);
}
int main() {
test01();
return 0;
}
运行结果:
10 20 30 40
10 20 30 40
10 20 30 40
Summary:
- When adding data to a set container, use the insert method.
- The data inserted into a set container is automatically sorted.
set size and swap
Description:
- Counting the size of set containers and swapping set containers.
Function prototype:
size();//Returns the number of elements in the containerempty();//Determine whether the container is emptyswap(st);//Swap two collection containers
Example:
#include <iostream>
#include <set>
using namespace std;
void printSet(set<int> & s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//大小
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
if (s1.empty())
{
cout << "s1为空" << endl;
}
else
{
cout << "s1不为空" << endl;
cout << "s1的大小为: " << s1.size() << endl;
}
}
//交换
void test02()
{
set<int> s1;
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
set<int> s2;
s2.insert(100);
s2.insert(300);
s2.insert(200);
s2.insert(400);
cout << "交换前" << endl;
printSet(s1);
printSet(s2);
cout << endl;
cout << "交换后" << endl;
s1.swap(s2);
printSet(s1);
printSet(s2);
}
int main() {
//test01();
test02();
return 0;
}
运行结果:
交换前
10 20 30 40
100 200 300 400
交换后
100 200 300 400
10 20 30 40
Summary:
- size
- Check if empty --- empty
- Exchange Container --- swap
Insertion and deletion in a set.
Description:
- Set container for inserting and deleting data
In C++, std::set is an associative container that stores unique elements in a specific order (typically implemented as a balanced binary search tree). Here’s how to perform insertions and deletions:
Insertion
insert: Adds an element if it’s not already present.std::set<int> mySet; mySet.insert(10); // Inserts 10 mySet.insert(20); // Inserts 20 mySet.insert(10); // Duplicate, no effectemplace: Constructs the element in-place (useful for complex objects).mySet.emplace(30); // Inserts 30- Insert range (from another container):
std::set<int> anotherSet = {40, 50}; mySet.insert(anotherSet.begin(), anotherSet.end());
Return value: insert returns a pair where:
first: iterator to the inserted element (or existing element if duplicate).second:trueif insertion took place,falseotherwise.
Deletion
eraseby key (removes all elements matching the key):mySet.erase(10); // Removes the element with value 10eraseby iterator (removes a specific element):auto it = mySet.find(20); if (it != mySet.end()) { mySet.erase(it); // Removes 20 }eraserange:mySet.erase(mySet.begin(), mySet.end()); // Clears the setclear: Removes all elements.mySet.clear(); // Empties the set
Example
#include <iostream>
#include <set>
int main() {
std::set<int> nums = {1, 2, 3};
nums.insert(4); // {1, 2, 3, 4}
nums.erase(2); // {1, 3, 4}
for (int n : nums) {
std::cout << n << " ";
}
// Output: 1 3 4
return 0;
}
Complexity
- Insert/Erase by key: O(log n)
- Insert/Erase by iterator: Amortized O(1) (but still O(log n) for rebalancing)
- Search (find): O(log n)
All operations maintain the sorted order of elements. Duplicates are automatically ignored.
Function prototype:
insert(elem);//Insert an element into the container.clear();// Clear all elementserase(pos);//Deletes the element pointed to by the iterator and returns an iterator to the next element.erase(beg, end);//Erases all elements in the range [beg, end), and returns the iterator to the next element.erase(elem);//Delete the element with value elem from the container.
Example:
#include <iostream>
#include <set>
using namespace std;
void printSet(set<int> & s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01()
{
set<int> s1;
//插入
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
printSet(s1);
//删除
s1.erase(s1.begin());
printSet(s1);
s1.erase(30);
printSet(s1);
//清空
//s1.erase(s1.begin(), s1.end());
s1.clear();
printSet(s1);
}
int main() {
test01();
return 0;
}
运行结果:
10 20 30 40
20 30 40
20 40
Summary:
- insert
- erase
- clear
set lookup and statistics
Description:
- Performing data search and statistics operations on a set container.
Function prototype:
find(key);//Check if the key exists; if present, return the iterator to the element with that key; if not, return set.end().count(key);//Count the number of elements in key
Example:
#include <iostream>
#include <set>
using namespace std;
//查找和统计
void test01()
{
set<int> s1;
//插入
s1.insert(10);
s1.insert(30);
s1.insert(20);
s1.insert(40);
//查找
set<int>::iterator pos = s1.find(30);
if (pos != s1.end())
{
cout << "找到了元素 : " << *pos << endl;
}
else
{
cout << "未找到元素" << endl;
}
//统计
int num = s1.count(30);
cout << "num = " << num << endl;
}
int main() {
test01();
return 0;
}
运行结果:
找到了元素 : 30
num = 1
Summary:
- 查找 --- find (返回的是迭代器)
- Count --- count (for set operations, the result is either 0 or 1)
Set and multiset are both associative containers in C++ that store elements in a sorted order, but they differ in how they handle duplicate elements:
- Set: Each element must be unique. If you try to insert a duplicate, the operation is ignored.
- Multiset: Allows duplicate elements. You can insert multiple identical values.
Example:
std::set<int> mySet→{1, 2, 3}(inserting another1has no effect).std::multiset<int> myMultiSet→{1, 1, 2, 3}(all inserts are stored).
Both provide efficient lookup (typically logarithmic time complexity) and maintain elements in sorted order. Use set when uniqueness is required, and multiset when duplicates are allowed.
Learning Objectives:
- Master the differences between set and multiset.
Difference:
- Sets do not allow the insertion of duplicate data, whereas multisets do.
- The SET command does return an insertion result when inserting data, indicating whether the insertion was successful. Specifically, it returns "OK" when the insertion is successful. When using SET with the NX option, if the key already exists, the operation fails and returns nil. Similarly, with the XX option, if the key does not exist, the operation also fails and returns nil.
- A multiset does not check the data, therefore duplicate data can be inserted.
Example:
#include <iostream>
#include <set>
using namespace std;
//set和multiset区别
void test01()
{
set<int> s;
pair<set<int>::iterator, bool> ret = s.insert(10);
if (ret.second) {
cout << "第一次插入成功!" << endl;
}
else {
cout << "第一次插入失败!" << endl;
}
ret = s.insert(10);
if (ret.second) {
cout << "第二次插入成功!" << endl;
}
else {
cout << "第二次插入失败!" << endl;
}
//multiset
multiset<int> ms;
ms.insert(10);
ms.insert(10);
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
test01();
return 0;
}
运行结果:
第一次插入成功!
第二次插入失败!
10 10
Summary:
- If duplicate data insertion is not allowed, you can use a set.
- If you need to insert duplicate data, you can use a multiset. Unlike a set, a multiset allows duplicate elements and automatically sorts them. Here's how you can use it:
Example Code:
#include <iostream>
#include <set>
int main() {
std::multiset<int> ms;
// Insert duplicate elements
ms.insert(5);
ms.insert(3);
ms.insert(5); // Duplicate allowed
ms.insert(7);
ms.insert(3); // Another duplicate
// Display elements
std::cout << "Elements in multiset: ";
for (const auto& elem : ms) {
std::cout << elem << " ";
}
std::cout << std::endl;
// Count occurrences of an element
std::cout << "Count of 5: " << ms.count(5) << std::endl;
return 0;
}
Output:
Elements in multiset: 3 3 5 5 7
Count of 5: 2
Key Features:
- Allows Duplicates: Can store multiple identical values.
- Automatic Sorting: Elements are sorted in ascending order by default.
- Search Operations: Provides efficient
find(),count(), andlower_bound()/upper_bound()methods. - Erase Operations: Can erase all instances of a value with
erase(value)or a specific iterator.
When to Use:
- When you need a sorted container that allows duplicates.
- When you need efficient lookup and iteration over ordered data with duplicates.
Alternatives:
- Set: If duplicates are not needed.
- Map: If you need key-value pairs with unique keys.
- Unordered containers: For faster average-case performance without sorting.
Use multiset when sorted, duplicate-friendly storage is required!
Creating pair groups
Description:
- When dealing with paired data, tuples can be used to return two data items.
Two creation methods:
pair<type, type> p ( value1, value2 );pair<type, type> p = make_pair( value1, value2 );
Example:
#include <iostream>
#include <string>
using namespace std;
//对组创建
void test01()
{
pair<string, int> p(string("Tom"), 20);
cout << "姓名: " << p.first << " 年龄: " << p.second << endl;
pair<string, int> p2 = make_pair("Jerry", 10);
cout << "姓名: " << p2.first << " 年龄: " << p2.second << endl;
}
int main() {
test01();
return 0;
}
运行结果:
姓名: Tom 年龄: 20
姓名: Jerry 年龄: 10
Summary:
Both methods can be used to create groups; just keep one in mind.
Sorting a set container
In C++, the std::set container is inherently sorted by its key according to the comparison function used. It maintains elements in ascending order by default, so explicit sorting is not required. If you need to change the sorting order, you can provide a custom comparator at the time of declaration.
Learning Objectives:
- The default sorting rule for set containers is in ascending order; learn how to change the sorting rule.
主要技术点:
- By using functors, you can change the sorting rules.
Example 1 set stores built-in data types
#include <iostream>
#include <set>
using namespace std;
class MyCompare
{
public:
bool operator()(int v1, int v2) {
return v1 > v2;
}
};
void test01()
{
set<int> s1;
s1.insert(10);
s1.insert(40);
s1.insert(20);
s1.insert(30);
s1.insert(50);
//默认从小到大
for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {
cout << *it << " ";
}
cout << endl;
//指定排序规则
set<int,MyCompare> s2;
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(30);
s2.insert(50);
for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
test01();
return 0;
}
运行结果: 编译失败:比较仿函数的 operator() 缺少 const 限定,现代标准库无法在常量比较器对象上调用它。
In summary: functors can be used to specify the sorting rules for a set container.
Example 2 using a set to store custom data types
#include <iostream>
#include <set>
#include <string>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
class comparePerson
{
public:
bool operator()(const Person& p1, const Person &p2)
{
//按照年龄进行排序 降序
return p1.m_Age > p2.m_Age;
}
};
void test01()
{
set<Person, comparePerson> s;
Person p1("刘备", 23);
Person p2("关羽", 27);
Person p3("张飞", 25);
Person p4("赵云", 21);
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++)
{
cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;
}
}
int main() {
test01();
return 0;
}
运行结果: 编译失败:比较仿函数的 operator() 缺少 const 限定,现代标准库无法在常量比较器对象上调用它。
Summary:
For custom data types, a set must specify a sorting rule before data can be inserted.