set/multiset容器(二叉树)

本文详细介绍了C++标准库中的set与multiset容器的使用方法,包括构造与赋值、大小与交换、插入与删除等操作,并通过示例展示了如何自定义排序规则。

所有的元素都会在插入时自动被排序
set容器与multiset容器本质在底层结构是二叉树,头文件均为#include <set>

set容器与multiset容器有什么区别?
set容器不允许容器中有重复的元素。
multiset容器允许容器有重复的元素。

#include <iostream>
using namespace std;
#include <set>    //set/multiset

void printSet(set<int> &s)
{
    //遍历输出
	for(set<int>::iterator it = s.begin();it!=s.end();it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

int main(int argc,char *argv[])
{
	set<int> s;  //默认构造
	s.insert(20);
	s.insert(50);    //在容器中插入元素
	s.insert(10);
	s.insert(40);
	s.insert(30);

	printSet(s);

	set<int> s2(s);    //拷贝构造函数
	printSet(s2);

	set<int> s3;
	s3 = s2;           //重载等号操作符
	printSet(s3);

	if( s3.empty() )    //判断容器是否为空
	{
		cout << "s3为空" << endl;
	}
	else{
		cout << "s3不为空" << endl;
		cout << "s3.size() = " << s3.size() << endl; //返回容器中元素的数目
	}

	set<int> s4;
	s4.insert(100);    //在容器中插入元素
	s4.insert(300);
	s4.insert(400);
	s4.insert(200);
	s4.insert(500);

	cout << "交换前:" << endl;
	printSet(s3);
	printSet(s4);

	s3.swap(s4);    //交换两个集合容器

	cout << "交换后:" << endl;
	printSet(s3);
	printSet(s4);

	//s4.clear();    //清除所有元素
	//printSet(s4);
	
	//s4.erase(s4.begin(),s4.end()); //删除区间[beg,end)的所有元素,返回下一个元素的迭代器
	//printSet(s4);
	
	set<int>::iterator it = s4.begin();
	it++;
	s4.erase(it,s4.end());
	printSet(s4);

	set<int>::iterator sit = s3.find(200); 
    //查找是否存在,若存在,返回该元素的迭代器,若不存在,返回
	if(sit != s3.end())
	{
		cout << *sit << endl;
	}
	else{
		cout << "未找到!" << endl;
	}

	int num = s3.count(200); //统计元素的个数 0或1 (set容器不允许容器中有重复的元素)
	cout << "num = " << num << endl;

	return 0;
}
  1. set构造和赋值

构造

set<T> st;  //默认构造函数

set(const set &set);  //拷贝构造函数

赋值:

set& operator=(const set &set);  //重载等号操作符

大小与交换

size();   //返回容器中元素的数目。

empty();  //判断容器是否为空。

swap();   //交换两个集合容器。

插入与删除

insert(elem);  //在容器中插入元素

clear();       //清楚所有元素

erase(pos);    //删除pos迭代器所指的元素,返回下一个元素的迭代器。

erase(beg,end);//删除区间[beg,end)的所有元素,返回下一个元素的迭代器。

erase(elem);   //删除容器中值为elem的元素。

查找与统计

find(key);  //查找key是否存在,若存在,返回该键的元素的迭代器,若果不存在,返回set.end();

count(key);  //统计key元素的个数。

set容器的排序   ---   set容器存放内置数据类型。
set容器默认排序规则从小到大,掌握如何改变排序规则。

主要技术点:
利用仿函数,可以修改排序规则

由于()运算符重载后使用方式非常像函数的调用,因此称为仿函数。

#include <iostream>
using namespace std;
#include <set>    /*set/multiset    (multi)多重*/

//set容器的降序
class MyCompare{
public:
	bool operator()(int v1,int v2)
	{
		//降序:让第一个数字大于第二个数字
		return v1 > v2;
	}
};

void printSet(set<int,MyCompare> &s)
{
	for(set<int,MyCompare>::iterator sit = s.begin();sit!=s.end();sit++)
	{
		cout << *sit << " ";
	}
	cout << endl;
}

int main(int argc,char *argv[])
{
	//set<int> s1;    //默认排序的规则是升序
	//s1.insert(40);
	//s1.insert(20);
	//s1.insert(10);
	//s1.insert(50);
	//s1.insert(30);

	//printSet(s1);  

	set<int,MyCompare> s2;

	//插入之后,已经默认的排序规则从小到大,所以必须要在插入数据之前,就要告诉它排序的规则是什么
	s2.insert(40);
	s2.insert(30);
	s2.insert(20);
	s2.insert(50);
	s2.insert(10);

	printSet(s2);

	return 0;
}

set容器的排序  ---  存放自定义数据类型

#include <iostream>
using namespace std;
#include <set>

class Person{
public:
	Person(string name,int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;  //姓名
	int m_Age;      //年龄
};

class MyCompare{
public:
	bool operator()(const Person &p1,const Person &p2)
	{
		//按照年龄降序
		return p1.m_Age > p2.m_Age;
	}
};

void printSet(set<Person,MyCompare> &s)
{
            //迭代器
	for(set<Person,MyCompare>::iterator it = s.begin();it!=s.end();it++)
	{
		cout << "姓名:" << (*it).m_Name << "年龄:" << it->m_Age << endl;
	}
}

int main(int argc,char *argv[])
{
	set<Person,MyCompare> s;

	Person p1("张三",30);
	Person p2("关羽",20);
	Person p3("孙悟空",10);
	Person p4("ggy",40);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);

	printSet(s);
	return 0;
}

对于自定义数据类型,如果不指定规则,是无法插入数据

<think>好的,我现在需要回答用户关于C++中unordered_set和unordered_multiset的区别及使用场景的问题。首先,我得回忆一下这两个容器的基本概念和它们在标准库中的定义。 用户之前的问题是关于堆、BST和完全二叉树的关系,现在转向了C++的unordered容器,所以可能需要保持技术细节的准确性,同时确保回答结构清晰。根据用户提供的引用内容,引用[1]、[2]、[3]主要介绍了unordered_set和unordered_map的基本用法,但用户现在询问的是unordered_set与unordered_multiset的区别,因此需要重点比较这两个容器。 首先,我应该确定两者的基本区别。根据C++标准库的定义,unordered_set是一个关联容器,存储唯一的元素,而unordered_multiset允许存储重复的元素。这一点类似于setmultiset的区别,但这里是无序的版本。 接下来,我需要考虑它们的内部实现。由于它们都是基于哈希表实现的,查找、插入和删除的平均时间复杂度都是O(1)。但multiset允许重复元素,因此在插入时不需要检查元素是否已存在,可能在某些情况下插入更快,但实际性能可能受哈希冲突的影响。 然后,使用场景方面。当需要确保元素唯一性时,如去重操作,应使用unordered_set。而需要保留重复元素,比如统计词频但不需要排序时,可以用unordered_multiset。例如,用户可能在一个文本处理应用中记录单词出现的次数,但不需要按顺序访问,这时候multiset会更合适。 接口上的差异也很重要。比如,unordered_set的insert方法返回一个pair,包含迭代器和布尔值,指示插入是否成功。而unordered_multiset的insert直接返回迭代器,因为总是插入成功。此外,count方法在set中返回0或1,而在multiset中可能返回更大的数。 用户可能还想知道如何选择两者。需要强调唯一性需求的重要性,以及是否需要频繁检查元素存在性。此外,内存占用方面,multiset可能因为重复元素占用更多空间,但具体实现可能优化存储。 需要验证是否有其他区别,如迭代顺序,但unordered容器本身不保证顺序,所以两者的迭代顺序都是不确定的。同时,查找元素时,set的find会返回第一个匹配项,而multiset可能有多个,但find通常返回第一个,要获取所有可能需要遍历。 最后,应该给出示例代码,帮助用户理解两者的具体使用差异,比如插入重复元素时的不同行为,以及count方法的结果差异。同时,总结使用场景,帮助用户根据需求做出选择。 需要确保回答结构清晰,分点列出区别,使用场景,并附上代码示例。同时,参考用户提供的引用内容,但用户提供的引用更多是关于基本使用,因此可能需要补充更多关于区别的细节。还要注意使用中文,避免Markdown格式,按照用户之前的问题的回答结构来组织内容。</think>### C++ unordered_set与unordered_multiset的区别及使用场景 #### 一、核心区别对比 | **特性** | **unordered_set** | **unordered_multiset** | |----------------------|---------------------------------------------|---------------------------------------------| | **元素唯一性** | 所有元素唯一 | 允许重复元素 | | **插入返回值** | 返回`pair<iterator, bool>`(是否插入成功) | 直接返回插入位置的迭代器 | | **查找效率** | 查找唯一元素时间复杂度$O(1)$ | 查找所有重复元素需遍历,时间复杂度$O(k)$(k为重复次数)| | **count()方法** | 返回0或1 | 可能返回≥1的值 | #### 二、底层实现差异 1. **哈希表结构** - 两者均基于哈希表实现,但处理冲突时: - `unordered_set`:每个哈希桶存储唯一键值,插入时自动去重[^1] - `unordered_multiset`:哈希桶允许存储多个相同键值,通过链表或开放寻址处理冲突 2. **内存占用对比** ```cpp // 示例:插入重复元素 std::unordered_set<int> s; s.insert(10); // 成功插入 s.insert(10); // 插入失败,返回false std::unordered_multiset<int> ms; ms.insert(10); // 返回迭代器 ms.insert(10); // 始终成功,返回新迭代器 ``` - multiset可能因重复元素占用更多内存,但具体实现会优化存储结构 #### 三、关键操作对比 1. **插入操作** ```cpp // unordered_set插入 auto [it, success] = s.insert(42); // 需要检查success标志 // unordered_multiset插入 auto it = ms.insert(42); // 无需检查,始终成功 ``` 2. **查找与计数** ```cpp // unordered_set的count用法 if (s.count(100) > 0) { /* 存在 */ } // unordered_multiset的count用法 int repeat_count = ms.count(100); // 可能返回0,1,2... ``` 3. **元素删除** ```cpp // unordered_set删除指定元素 s.erase(50); // 删除所有值为50的元素(最多1个) // unordered_multiset选择性删除 auto range = ms.equal_range(50); ms.erase(range.first, range.second); // 删除所有重复元素 ``` #### 四、使用场景选择 | **场景** | **推荐容器** | **原因** | |-----------------------------------|-----------------------|-------------------------------------------------------------------------| | 用户唯一性校验(如用户名注册) | unordered_set | 天然去重特性避免数据冗余[^2] | | 词频统计(不关心顺序) | unordered_multiset | 直接记录所有出现次数 | | 实时黑名单过滤 | unordered_set | 快速查找是否存在 | | 日志记录(需保留重复条目) | unordered_multiset | 保留所有原始数据 | #### 五、性能注意事项 1. **负载因子影响** - 两者默认最大负载因子为1.0,超过时会触发哈希表扩容 - multiset可能因重复元素更快达到负载阈值 2. **遍历效率** ```cpp // unordered_multiset遍历重复元素示例 auto range = ms.equal_range(42); for (auto it = range.first; it != range.second; ++it) { std::cout << *it << " "; // 输出所有42的实例 } ``` - 遍历特定值的所有实例时,multiset需要额外处理 --- ### 示例代码对比 ```cpp #include <unordered_set> #include <iostream> void demo() { // unordered_set示例 std::unordered_set<std::string> unique_words; unique_words.insert("apple"); auto [it1, inserted] = unique_words.insert("apple"); // inserted=false // unordered_multiset示例 std::unordered_multiset<std::string> word_bag; word_bag.insert("apple"); word_bag.insert("apple"); // 成功插入第二个"apple" std::cout << "Apple出现次数:" << word_bag.count("apple"); // 输出2 } ``` --- ### 总结选择建议 1. **必须使用unordered_set的情况** - 需要确保元素唯一性 - 依赖`insert`返回值判断是否为新数据 - 使用元素存在性作为业务逻辑判断条件 2. **优先考虑unordered_multiset的情况** - 需要记录所有事件(如日志分析) - 统计频率但无需排序 - 允许重复键值的哈希表应用场景 [^1]: 哈希表通过链地址法解决冲突时,multiset的桶内会形成链表存储重复元素 [^2]: 游戏开发中常用unordered_set实现物品唯一ID校验系统
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

物の哀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值