C++学习笔记---020
C++之set和multiset的基本应用
前言:
前面篇章学习了C++对于二叉树搜索树的知识认识和了解,接下来继续学习,C++的set和multiset等知识。
/知识点汇总/
vecotr/list/deque…序列式容器:单纯的存储数据,存储的数据和数据之间没啥关联。
map/set… 关联式容器:不仅仅是是存储数据,一般还可以查找数据,存储的数据和数据之间存在一定的关联性
1、C++中set和multiset的简单介绍
在C++标准库中,set 和 multiset 是两种基于红黑树实现的关联容器,用于存储唯一(对于set)或可重复(对于multiset)的元素。这些元素在容器中始终按升序排列。
2、set的简单接口应用
set
set 是一个关联容器,它包含可以重复的键值对(在这里键和值相同)。set中的元素默认情况下总是按键的升序排序。set不允许两个元素具有相同的键。
特性:
唯一性:set中的元素是唯一的。
有序性:set中的元素默认按照升序排序。
插入/删除/查找效率高:由于set基于红黑树实现,其插入、删除和查找操作的时间复杂度通常为O(log n)。
#include <iostream>
#include <set>
int main() {
std::set<int> mySet;
// 插入元素
mySet.insert(10);
mySet.insert(20);
mySet.insert(30);
// 遍历set
for (const auto& elem : mySet) {
std::cout << elem << ' ';
}
std::cout << '\n';
// 查找元素
if (mySet.find(20) != mySet.end()) {
std::cout << "Found 20\n";
}
// 删除元素
mySet.erase(20);
return 0;
}
3、multiset的简单接口应用
multiset
multiset 与 set 类似,但它允许存储重复的元素。这意味着你可以在 multiset 中插入多个相同的元素。
特性:
可重复性:multiset中的元素可以重复。
有序性:multiset中的元素默认按照升序排序。
插入/删除/查找效率高:同样基于红黑树实现,其插入、删除和查找操作的时间复杂度通常为O(log n)。
#include <iostream>
#include <multiset>
int main() {
std::multiset<int> myMultiSet;
// 插入元素,包括重复的元素
myMultiSet.insert(10);
myMultiSet.insert(20);
myMultiSet.insert(20);
myMultiSet.insert(30);
// 遍历multiset
for (const auto& elem : myMultiSet) {
std::cout << elem << ' ';
}
std::cout << '\n';
// 查找元素(因为可能存在多个相同的元素,所以可能需要遍历)
for (auto it = myMultiSet.find(20); it != myMultiSet.end() && *it == 20; ++it) {
std::cout << "Found 20\n";
}
// 删除元素(删除所有值为20的元素)
while (myMultiSet.find(20) != myMultiSet.end()) {
myMultiSet.erase(myMultiSet.find(20));
}
return 0;
}
4、set和multiset的基本应用
4.1、set排序+去重
void test_set1()
{
//排序+去重
set<int> s1;
s1.insert(1);
s1.insert(12);
s1.insert(3);
s1.insert(1);
s1.insert(6);
s1.insert(2);
set<int>::iterator it = s1.begin();
while (it != s1.end())
{
//*it//不允许修改
cout << *it << " ";
++it;
}
cout << endl;
vector<int> v = { 3,2,8,1,10,2 };
set<int> s2(v.begin(), v.end());
for (auto e : s2)
{
cout << e << " ";
}
cout << endl;
cout << "-------------------" << endl;
set<int> s3 = { 1,5,6,8,6,9,6,5,6 };
//支持{}的初始化,因为有initializer_list的构造(C++11)
for (auto e : s3)
{
cout << e << " ";
}
cout << endl;
s3.erase(5);
for (auto e : s3)
{
cout << e << " ";
}
cout << endl;
s3.erase(s3.find(9));
for (auto e : s3)
{
cout << e << " ";
}
cout << endl;
auto pos = s3.find(6);
if (pos != s3.end())
{
cout << *pos << endl;
s3.erase(pos);
}
else
{
cout << "找不到" << endl;
}
for (auto e : s3)
{
cout << e << " ";
}
cout << endl;
}
4.2、找区间
void test_set2()
{
set<int> myset;
for (int i = 1; i < 10; i++)
{
myset.insert(i * 10);
}
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
//找左闭右开区间的值
//[30,60)
auto itlow = myset.lower_bound(30);//>=x
auto itup = myset.upper_bound(60);//>x
//删除[30,60)范围的值
myset.erase(itlow, itup);
for (auto e : myset)
{
cout << e << " ";
}
cout << endl;
}
multiset
与set功能几乎类似
区别在于允许冗余。即:只排序不去重
multiset中,相等值可以在左边也可以在右边,具体看怎么实现。
因为是中序,默认是左边树开始,插入右边树;
所以find默认查找的是中序第一个节点
查找x,找到x后节点后,这个节点的左子树没有x节点,则它就是中序的第一个节点
void test_set3()
{
multiset<int> s1;
s1.insert(2);
s1.insert(1);
s1.insert(2);
s1.insert(3);
s1.insert(2);
s1.insert(6);
s1.insert(2);
multiset<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
++it;
}
cout << endl;
auto pos = s1.find(2);
while (pos != s1.end() && *pos == 2)
{
cout << *pos << " ";
pos++;
}
cout << endl;
}
5、set巩固练习题
5.1、两个数组的交集
思路:1.arr的size相等就++ 2.不相等小的arr++
#include <iostream>
#include <vector>
#include <set>
using namespace std;
class Solution
{
public:
vector<int> inertsection(vector<int>& nums1, vector<int>& nums2)
{
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
vector<int> ret;
auto it1 = s1.begin();
auto it2 = s2.begin();
while (it1 != s1.end() && it2 != s2.end())
{
if (*it1 < *it2)
{
it1++;
}
else if (*it1 > *it2)
{
it2++;
}
else
{
ret.push_back(*it1);
it1++;
it2++;
}
}
return ret;
}
};
int main()
{
vector<int> arr1 = { 1,6,8,3,5,9 };
vector<int> arr2 = { 2,3,6,6,7,1 };
Solution st;
vector<int> arr3(arr1.size() + arr2.size());
arr3 = st.inertsection(arr1, arr2);
for (auto e : arr3)
{
cout << e << " ";
}
cout << endl;
return 0;
}
5.2、合并两个数组,去掉重复值
思路:利用set的性质,巧妙解决
#include <iostream>
#include <vector>
#include <set>
using namespace std;
class Solution
{
public:
vector<int> Unionize(vector<int>& nums1, vector<int>& nums2)
{
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
size_t len1 = nums1.size();
size_t len2 = nums2.size();
vector<int> nums3;
for (auto& e : s1)
{
nums3.push_back(e);
}
for (auto& e : s2)
{
nums3.push_back(e);
}
set<int> s3(nums3.begin(), nums3.end());
vector<int> nums4;
for (auto e : s3)
{
nums4.push_back(e);
}
return nums4;
}
};
int main()
{
vector<int> arr1 = { 1,6,8,3,5,9 };
vector<int> arr2 = { 2,3,6,6,7,1 };
Solution st;
vector<int> arr3(arr1.size() + arr2.size());
arr3 = st.Unionize(arr1, arr2);
for (auto e : arr3)
{
cout << e << " ";
}
cout << endl;
return 0;
}
6、小结
set和multiset的区别
set和multiset在C++标准库中都是基于红黑树实现的关联容器,但它们之间存在一些关键的区别。
元素唯一性:
set:set中的元素是唯一的,即不能有两个元素具有相同的值。
multiset:multiset允许存储重复的元素,即可以有多个元素具有相同的值。
用途:
set:由于其元素的唯一性,set通常用于需要唯一集合的场合,例如存储不重复的标识符或关键字。
multiset:当需要记录元素出现的次数或存储可能重复的元素时,可以使用multiset。
操作:
由于两者都是基于红黑树的关联容器,所以它们的插入、删除和查找操作的时间复杂度都是O(log
n),其中n是容器中元素的数量。然而,由于multiset允许重复元素,因此在处理重复元素时可能需要进行额外的操作。
迭代:
当迭代set或multiset时,元素将按照升序排列。但需要注意的是,由于multiset允许重复元素,所以如果你迭代一个multiset并查找一个特定的值,可能会遇到多个相同的元素。
内存使用:
由于multiset需要额外的信息来跟踪重复元素的数量,因此它可能会比set使用更多的内存。然而,这种差异通常很小,并且通常不是选择这两个容器之间的主要决定因素。
修改元素:
需要注意的是,set和multiset都不直接支持修改容器中的元素值。这是因为它们是自动排序的容器,元素的位置取决于其值。如果你尝试修改一个元素的值,它可能会破坏容器的排序属性。如果你需要修改一个元素的值,通常的做法是先删除该元素,然后插入新的元素。
总的来说,set和multiset之间的主要区别在于它们是否允许重复的元素。根据你的需求(是否需要存储重复的元素),你可以选择使用set或multiset。