STL学习笔记14-集合算法(set_intersection,set_union,set_difference)

本文介绍了C++中用于处理有序序列的三个集合算法:set_intersection用于求交集,set_union用于求并集,set_difference用于求差集。这些算法要求输入的序列必须是升序的。文中通过示例展示了如何使用这些函数处理vector容器中的元素。

15.6 常用集合算法

算法简介:

  • set_intersection //求两个容器的交集
  • set_union //求两个容器的并集
  • set_difference //求两个容器的差集

注意:两个集合必须是有序序列且为升序列

15.6.1 set_intersection 求两个容器的交集

功能描述: 求两个容器的交集

注意:两个集合必须是有序序列且为升序列

函数原型:

  • set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); //求两个集合的交集

    • beg1 容器1开始迭代器
    • end1 容器1结束迭代器
    • beg2 容器2开始迭代器
    • end2 容器2结束迭代器
    • dest 目标容器开始迭代器

示例:

//交集
void test01() {
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 5; i++)
	{
		v1.push_back(i+8);
		v2.push_back(i+10);
	}
	cout << "v1:      ";
	for_each(v1.begin(), v1.end(), myPrint());
	cout << endl;
	cout << "v2:      ";
	for_each(v2.begin(), v2.end(), myPrint());
	cout << endl;

	
	vector<int> tagerV;
	//提前开辟空间,取两者中最小的一个容器大小
	tagerV.resize(min(v1.size(), v2.size()));
	//取交集
	vector<int>::iterator itEnd = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), tagerV.begin());
	//打印输出交集
	cout << "交集:      ";
	for_each(tagerV.begin(), itEnd, myPrint());
	cout << endl;
}

15.6.2 set_union 求两个容器的并集

功能描述: 求两个集合的并集

注意:两个集合必须是有序序列且为升序列

函数原型:

  • set-union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest) //求两个集合的并集

    • beg1 容器1开始迭代器
    • end1 容器1结束迭代器
    • beg2 容器2开始迭代器
    • end2 容器2结束迭代器
    • dest 目标容器开始迭代器

示例:

//并集
void test02() {
	cout << "-------------------------------------" << endl;

	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 5; i++)
	{
		v1.push_back(i + 8);
		v2.push_back(i + 10);
	}

	cout << "v1:      ";
	for_each(v1.begin(), v1.end(), myPrint());
	cout << endl;
	cout << "v2:      ";
	for_each(v2.begin(), v2.end(), myPrint());
	cout << endl;

	
	vector<int> tagerV;
	//提前开辟空间,取两者容器之和
	tagerV.resize(v1.size()+v2.size());
	//取并集
	vector<int>::iterator itEnd = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), tagerV.begin());
	//打印输出并集
	cout << "并集:      ";
	for_each(tagerV.begin(), itEnd, myPrint());
	cout << endl;
}

15.6.3 set_difference 求两个容器的差集

功能描述: 求两个集合的差集

注意:两个集合必须是有序序列且为升序列

函数原型:

  • set-difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest); //求两个集合的差集(结果为容器1中容器2没有的)

    • beg1 容器1开始迭代器
    • end1 容器1结束迭代器
    • beg2 容器2开始迭代器
    • end2 容器2结束迭代器
    • dest 目标容器开始迭代器

示例:

//差集
void test03() {
	cout << "-------------------------------------" << endl;

	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 5; i++)
	{
		v1.push_back(i + 8);
		v2.push_back(i + 10);
	}

	cout << "v1:      ";
	for_each(v1.begin(), v1.end(), myPrint());
	cout << endl;
	cout << "v2:      ";
	for_each(v2.begin(), v2.end(), myPrint());
	cout << endl;


	vector<int> tagerV;
	//提前开辟空间,取两者容器之中最大的一个
	tagerV.resize(max(v1.size(), v2.size()));
	//取差集
	vector<int>::iterator itEnd = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), tagerV.begin());
	//打印输出差集
	cout << "差集:      ";
	for_each(tagerV.begin(), itEnd, myPrint());
	cout << endl;
}

15.6

以下是对你提供的 C++ 程序的**全面知识点讲解与整理笔记**,适合用于学习 STL集合操作、输入输出控制和算法应用。我们将从整体功能出发,逐模块分析,并总结关键概念。 --- # ✅ **程序功能概述** 该程序实现的功能是: > 给定两个整数集合 A 和 B,输出 A - B(即在 A 中但不在 B 中的所有元素)。 > 若差集为空,则输出 `"NULL"`。 > 支持多组测试数据,以 `0 0` 结束输入。 --- ## 📚 一、头文件详解 ```cpp #include <iostream> // 输入输出流 (cin, cout) #include <vector> // 动态数组 vector #include <set> // 有序集合 set(基于红黑树) #include <algorithm> // 标准算法库(如 set_difference) #include <iterator> // 迭代器工具(back_inserter 等) ``` | 头文件 | 用途 | |--------|------| | `<iostream>` | 标准输入输出 | | `<vector>` | 存储结果列表 | | `<set>` | 自动去重 + 排序,保证唯一性和有序性 | | `<algorithm>` | 提供高效算法:`set_difference`, `sort`, `find` 等 | | `<iterator>` | 提供插入迭代器,如 `back_inserter` | 📌 **重点提醒**:`back_inserter` 必须包含 `<iterator>`,否则可能编译失败! --- ## 🧱 二、核心数据结构解析 ### 1. `set<int>` - 特性: - 元素自动排序(升序)。 - 自动去重(不允许重复元素)。 - 插入、查找时间复杂度为 $O(\log n)$。 - 底层实现:平衡二叉搜索树(通常是红黑树)。 - 使用场景:需要“无重复 + 有序”的集合时使用。 ```cpp set<int> n_array; n_array.insert(x); // 插入元素,自动排序+去重 ``` ✅ 优势:无需手动排序或去重,天然满足 `set_difference` 要求。 --- ### 2. `vector<int>` - 动态数组,可动态增长。 - 用于存储 `set_difference` 的输出结果。 ```cpp vector<int> result; ``` 结合 `back_inserter(result)` 可以将算法结果不断 push_back 到末尾。 --- ## ⚙️ 三、关键函数:`set_difference` ### 函数原型(来自 `<algorithm>`) ```cpp OutputIterator set_difference( InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, InputIterator2 last2, OutputIterator result ); ``` ### 功能说明 计算两个**有序区间**的差集:`A - B = {x ∈ A ∧ x ∉ B}` ### 参数解释 | 参数 | 含义 | |------|------| | `first1, last1` | 第一个集合的起始和结束迭代器(如 `n_array.begin()` 到 `n_array.end()`) | | `first2, last2` | 第二个集合的起始和结束迭代器 | | `result` | 输出目标位置的迭代器(通常用 `back_inserter(vec)`) | ### 示例调用 ```cpp set_difference(n_array.begin(), n_array.end(), m_array.begin(), m_array.end(), back_inserter(result)); ``` 👉 此调用会把 `n_array - m_array` 的结果写入 `result` 向量中。 📌 **前提条件**:两个输入范围必须已排序 —— `set` 天然满足! --- ## 💡 四、`back_inserter` 详解(来自 `<iterator>`) ### 作用 创建一个**插入型迭代器**,当赋值时自动调用容器的 `push_back()` 方法。 ### 举例理解 ```cpp vector<int> result; auto it = back_inserter(result); *it = 10; // 相当于 result.push_back(10); *it = 20; // 相当于 result.push_back(20); ``` 👉 所以 `set_difference(..., back_inserter(result))` 就能自动把每个差集元素加入 `result`。 --- ## 🖨️ 五、输出格式处理(易错点!) 原代码存在 **格式问题**: ```cpp for (int elem : result) { cout << elem << " "; // ❌ 每个元素后都加空格 → 最后有多余空格 } ``` 例如输出:`1 2 3 ` → 尾部有空格,可能导致 OJ 判为 **Presentation Error** ### ✅ 正确做法:避免末尾空格 #### 方法一:判断索引 ```cpp for (int i = 0; i < result.size(); ++i) { if (i > 0) cout << " "; cout << result[i]; } ``` #### 方法二:使用布尔标志 ```cpp bool first = true; for (int x : result) { if (!first) cout << " "; cout << x; first = false; } ``` #### 方法三:若非空,先输出第一个,再输出其余带前缀空格的 ```cpp if (!result.empty()) { cout << result[0]; for (int i = 1; i < result.size(); ++i) cout << " " << result[i]; } ``` --- ## 🔁 六、循环结构与输入控制 ```cpp while (cin >> n >> m) { if (n == 0 && m == 0) break; // 处理一组数据 } ``` - 支持多组输入。 - 读取失败(如 EOF)或遇到 `0 0` 时退出。 - 每次循环内定义局部变量,确保状态隔离。 ✅ 安全且标准的多测处理方式。 --- ## 🧪 七、边界情况测试建议 | 输入情况 | 预期输出 | |---------|----------| | A = {1,2,3}, B = {2,3} | 1 | | A = {1,2}, B = {3,4} | 1 2 | | A = {1,2}, B = {1,2} | NULL | | A = {}, B = {} | NULL(n=0,m=0直接break) | | A = {1,1,2}, B = {1} → 实际 A={1,2} | 2(因 set 去重) | --- ## ✅ 八、完整修正版代码(推荐) ```cpp #include <iostream> #include <vector> #include <set> #include <algorithm> #include <iterator> using namespace std; int main() { int n, m; while (cin >> n >> m) { if (n == 0 && m == 0) break; set<int> n_array, m_array; vector<int> result; // 输入第一个集合 for (int i = 0; i < n; ++i) { int x; cin >> x; n_array.insert(x); } // 输入第二个集合 for (int i = 0; i < m; ++i) { int x; cin >> x; m_array.insert(x); } // 计算差集 set_difference(n_array.begin(), n_array.end(), m_array.begin(), m_array.end(), back_inserter(result)); // 输出(注意格式) if (result.empty()) { cout << "NULL"; } else { for (int i = 0; i < result.size(); ++i) { if (i > 0) cout << " "; cout << result[i]; } } cout << endl; } return 0; } ``` --- ## 📝 九、知识点总结表 | 类别 | 内容 | |------|------| | **STL 容器** | `set<int>`:有序、去重;`vector<int>`:动态数组 | | **STL 算法** | `set_difference`:求有序集合差集 | | **迭代器工具** | `back_inserter`:生成插入迭代器 | | **头文件依赖** | `<iterator>` 不可省略! | | **输入输出** | 多组输入控制、EOF 处理 | | **格式规范** | 避免末尾多余空格 | | **时间复杂度** | $O(n \log n + m \log m + n + m)$,主要来自插入和差集运算 | | **空间复杂度** | $O(n + m)$ | --- ## 🧠 十、常见面试/笔试考点 1. **为什么不能直接用数组?** - 数组无法自动去重,需额外处理。 2. **能否用 `unordered_set` 替代 `set`?** - 可以,但 `set_difference` 要求有序 → 必须先排序,效率更低。 - 更适合手写遍历判断是否存在。 3. **如何求交集、并集、对称差?** - STL 提供了其他函数: ```cpp set_union // 并集 set_intersection // 交集 set_symmetric_difference // 对称差 ``` 4. **如果允许重复元素怎么办?** - 使用 `multiset`,但要注意 `set_difference` 也会考虑重复次数。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值