当前位置: 首页 > news >正文

数据结构:集合操作(Set Operations): 并集(Union)、交集(Intersection)、 差集(Difference)

目录

并集(Union)A ∪ B

未排序数组的并集

已排序数组的并集 

交集(Intersection) A ∩ B

未排序数组的交集

已排序数组的交集

差集(Difference)A - B

未排序数组的差集 

已排序数组的差集 


并集(Union)A ∪ B

给定两个数组 A 和 B,求一个数组 C,其中包含 A 和 B 的所有元素,不重复。

未排序数组的并集

我们现在处理的是 两个未排序数组 A 和 B 的并集,将它们的所有 不重复元素 放入数组 C 中。

输入:A = {3, 5, 10, 4, 6}   (长度 m = 5)B = {12, 4, 7, 2, 5}   (长度 n = 5)期望输出 C:C = {3, 5, 10, 4, 6, 12, 7, 2}

🧠 算法分解

步骤一:创建一个结果数组 C,长度最大是 m+n(极限情况下所有元素都不同)

int C[m + n];  // 最多 m+n 个不重复元素
int k = 0;     // C 的当前长度

步骤二:把 A 中的所有元素直接复制到 C

for (int i = 0; i < m; i++) {C[k++] = A[i];
}

此时 C 中已经有 A 的全部元素,长度为 m。

步骤三:遍历 B 中的每个元素,逐个与 C 中已有元素比较,若不存在,则添加

for (int i = 0; i < n; i++) {bool found = false;for (int j = 0; j < k; j++) {if (B[i] == C[j]) {found = true;break;}}if (!found) {C[k++] = B[i];}
}

完整代码

#include <iostream>
using namespace std;int main() {int A[] = {3, 5, 10, 4, 6};int B[] = {12, 4, 7, 2, 5};int m = 5, n = 5;int C[100]; // 足够大,防止越界int k = 0;  // 当前C的长度// Step 1: 复制A到Cfor (int i = 0; i < m; i++) {C[k++] = A[i];}// Step 2: 将B中不在C中的元素添加进Cfor (int i = 0; i < n; i++) {bool found = false;for (int j = 0; j < k; j++) {if (B[i] == C[j]) {found = true;break;}}if (!found) {C[k++] = B[i];}}// 输出结果cout << "并集 C = ";for (int i = 0; i < k; i++) {cout << C[i] << " ";}cout << endl;return 0;
}

📊 时间复杂度分析

我们来逐步分析每一部分的时间消耗:

步骤一:复制 A 中所有元素到 C

for (int i = 0; i < m; i++)C[k++] = A[i];
  • 时间复杂度 = O(m)

  • 每个元素复制一次,共 m 次操作。

步骤二:检查 B 中的每个元素是否已在 C 中

for (int i = 0; i < n; i++) {for (int j = 0; j < k; j++) {...}
}
  • 最坏情况:B 中所有元素都不在 A 中

  • 每添加一个元素,k 会增加

  • 所以:

    • 第1个 B 元素最多比较 m 次

    • 第2个 B 元素最多比较 m+1 次

    • ...

    • 第n个 B 元素最多比较 m + n − 1 次

这是一个等差数列求和问题:

因此,总时间复杂度 = O(m + n×m + n²) = O(n² + n×m) = O( n^2 )

空间复杂度分析:

  • 使用了一个新数组 C,其最大长度为 m + n

  • 空间复杂度 = O(m + n)


已排序数组的并集 

给定两个升序排序数组 A 和 B,构造一个新数组 C,包含它们的所有元素(不重复)。

  • 输入:

    • 排序数组 A,长度 m

    • 排序数组 B,长度 n

  • 输出:

    • 新数组 C,其中包含 A 和 B 的所有元素,不重复,且仍为升序。

 问题特点

  • 输入数组 已经排序 ⇒ 可以用“双指针法”快速处理

  • 输出结果数组需要 去重

🧠 核心思路:双指针合并 + 去重

我们借助两个指针 ij 分别指向数组 A 和 B 的当前元素,用一个结果指针 k 构建 C。

 🔹 Step 1:初始化

int i = 0, j = 0, k = 0;

🔹 Step 2:主循环,遍历 A 和 B,直到有一个数组走完 

while (i < m && j < n) {...
}

 我们在循环中对比 A[i] 和 B[j]:

✅ 情况一:A[i] < B[j]

说明 A[i] 较小,可以直接放进 C。别忘了先检查是否重复:

if (k == 0 || C[k - 1] != A[i]) {C[k++] = A[i];
}
i++;

✅ 情况二:A[i] > B[j]

说明 B[j] 较小,处理逻辑与上面一样:

if (k == 0 || C[k - 1] != B[j]) {C[k++] = B[j];
}
j++;

✅ 情况三:A[i] == B[j]

两数组当前元素相同 → 只加入一个,跳过重复:

if (k == 0 || C[k - 1] != A[i]) {C[k++] = A[i];
}
i++;
j++;

🔹 Step 3:处理剩下的 A 或 B 的元素

可能其中一个数组还有剩余未处理的元素,需要单独补上(仍然要去重):

while (i < m) {if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;
}while (j < n) {if (k == 0 || C[k - 1] != B[j])C[k++] = B[j];j++;
}

完整代码

#include <iostream>
using namespace std;void unionSorted(int A[], int m, int B[], int n) {int C[200]; // 结果数组 Cint i = 0, j = 0, k = 0; // 指针初始化// 主循环:合并两个已排序数组while (i < m && j < n) {if (A[i] < B[j]) {if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;}else if (A[i] > B[j]) {if (k == 0 || C[k - 1] != B[j])C[k++] = B[j];j++;}else { // A[i] == B[j]if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;j++;}}// 处理 A 剩余部分while (i < m) {if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;}// 处理 B 剩余部分while (j < n) {if (k == 0 || C[k - 1] != B[j])C[k++] = B[j];j++;}// 输出并集结果cout << "并集 C = ";for (int p = 0; p < k; p++) {cout << C[p] << " ";}cout << endl;
}int main() {int A[] = {1, 3, 5, 7};int B[] = {2, 3, 5, 6, 8};int m = sizeof(A) / sizeof(A[0]);int n = sizeof(B) / sizeof(B[0]);unionSorted(A, m, B, n);return 0;
}

模拟过程

A = {1, 3, 5, 7}
B = {2, 3, 5, 6, 8}
A[i]B[j]比较结果插入CC数组状态
12A < B1{1}
32A > B2{1, 2}
33A == B3{1, 2, 3}
55A == B5{1, 2, 3, 5}
76A > B6{1, 2, 3, 5, 6}
78A < B7{1, 2, 3, 5, 6, 7}
-8A 完了8{1, 2, 3, 5, 6, 7, 8}

📊 时间复杂度:

  • 每次迭代最多移动一次指针 i 或 j → 总最多 m+n 次操作

  • 去重检查仅需 C[k - 1],时间为 O(1)

总时间复杂度 = O(m + n)

空间复杂度:

  • 新数组 C 最多 m + n 个元素

  • 空间复杂度 = O(m + n)


交集(Intersection) A ∩ B

未排序数组的交集

目标: 从两个无序数组 A 和 B 中找出共同存在的元素(不重复),保存到数组 C。

❗️问题特点:

  • 无序数组:不能使用排序、二分、双指针等优化技巧。

  • 只能使用逐一比较(暴力匹配)。

  • 为避免重复,必须手动检查结果数组 C 中是否已经包含某个元素。

🧠 核心思路

我们采用一个构造式方法来思考交集,步骤如下:

步骤 1:初始化辅助数组

  • 创建结果数组 C[m + n],足够大

  • 设置变量 k = 0 表示结果数组长度

int C[m + n];
int k = 0;

 步骤 2:遍历 A 中的每个元素

for (int i = 0; i < m; i++) {...
}

我们要判断 A[i] 是否也出现在 B 中。

步骤 3:对每个 A[i],检查它是否存在于 B 中

这是通过一个内层循环完成的:

bool found_in_B = false;
for (int j = 0; j < n; j++) {if (A[i] == B[j]) {found_in_B = true;break;}
}

步骤 4:如果 A[i] 在 B 中,还要检查是否已添加到 C

避免重复添加元素,使用一个辅助函数:

bool existsInC = false;
for (int x = 0; x < k; x++) {if (C[x] == A[i]) {existsInC = true;break;}
}

步骤 5:如果 A[i] 在 B 中,且未在 C 中,添加到结果数组

if (found_in_B && !existsInC) {C[k++] = A[i];
}

完整代码实现

#include <iostream>
using namespace std;// 判断一个元素是否存在于数组中
bool exists(int arr[], int size, int val) {for (int i = 0; i < size; i++) {if (arr[i] == val)return true;}return false;
}// 求未排序数组交集
void intersectionUnsorted(int A[], int m, int B[], int n) {int C[100];  // 假设最大100个交集元素int k = 0;for (int i = 0; i < m; i++) {// 判断 A[i] 是否存在于 Bbool foundInB = false;for (int j = 0; j < n; j++) {if (A[i] == B[j]) {foundInB = true;break;}}// 如果在B中,且不在C中,加入Cif (foundInB && !exists(C, k, A[i])) {C[k++] = A[i];}}// 输出交集结果cout << "交集 A ∩ B = ";for (int i = 0; i < k; i++) {cout << C[i] << " ";}cout << endl;
}int main() {int A[] = {3, 5, 10, 4, 6};int B[] = {12, 4, 7, 2, 5};int m = sizeof(A) / sizeof(A[0]);int n = sizeof(B) / sizeof(B[0]);intersectionUnsorted(A, m, B, n);return 0;
}

模拟过程

A = {3, 5, 10, 4, 6};
B = {12, 4, 7, 2, 5};

我们按顺序处理每个 A[i]:

A[i]是否在B中是否已在C中是否加入CC数组内容
3-
5{5}
10-{5}
4{5, 4}
6-{5, 4}

📊 复杂度分析

✅ 时间复杂度

我们有两个嵌套循环:

  • 外层:遍历 A → O(m)

  • 内层:查找 A[i] 是否在 B → O(n)

  • 再加一次遍历 C → 最坏 O(m)

总复杂度为:

若 m ≈ n,则为 O(n²),属于平方级别的暴力方法。 

✅ 空间复杂度

  • 新建一个结果数组 C,最多 m 个元素

  • 空间复杂度 = O(m)


已排序数组的交集

给定两个 已排序数组 A 和 B,我们要找出它们的交集,即那些同时出现在 A 和 B 中的元素(不重复),并保存在一个新数组 C 中。

  • 输入:

    • A[] = 已排序数组,长度为 m

    • B[] = 已排序数组,长度为 n

  • 输出:

    • C[] = 存储 A 和 B 中都出现的元素,不重复

📌 特点

  • 数组是 已排序的

  • 可以使用“双指针法”进行高效遍历(参考合并数组:数据结构:数组:合并数组(Merging Arrays)-CSDN博客)

  • 相同的元素只取一次,不重复添加

🧠 核心思路:双指针扫描

我们使用两个下标 ij

  • i 指向数组 A 的当前位置

  • j 指向数组 B 的当前位置

我们从头遍历两个数组,逐步比较当前指向的元素:

 步骤流程如下:

  1. 初始化 i = 0,j = 0,k = 0(k 是结果数组 C 的长度)

  2. 当 i < m 且 j < n 时:

    • 若 A[i] < B[j]:说明 A[i] 小于 B[j],A[i] 不可能是交集元素 → i++

    • 若 A[i] > B[j]:说明 B[j] 小于 A[i],B[j] 不可能是交集元素 → j++

    • 若 A[i] == B[j]:

      • 检查是否已添加过该元素(去重)

      • 如果没添加过,加入到 C[k++],然后 i++, j++

  3. 循环结束,C 就是交集结果

模拟过程 

A = {2, 4, 6, 8, 10}
B = {4, 6, 8, 12, 14}

我们用两个指针逐步比对:

ijA[i]B[j]比较结果C
00242 < 4 → i++
1044相等 → 加入4C = {4}
2166相等 → 加入6C = {4, 6}
3288相等 → 加入8C = {4, 6, 8}
43101210 < 12 → i++
53i >= m → 结束

最终结果:C = {4, 6, 8}

💡 代码实现

🔹 步骤1:定义函数 intersectionSorted()

void intersectionSorted(int A[], int m, int B[], int n) {int C[100]; // 足够大的数组int i = 0, j = 0, k = 0;}

🔹 步骤 2:开始主循环,条件是两个指针都没越界 

while (i < m && j < n) {...
}

🔹 情况 A:A[i] < B[j]

说明当前 A[i] 比 B[j] 小,因此不可能等于 B 中任何剩下的元素。
我们可以跳过 A[i]:

if (A[i] < B[j]) {i++;
}

🔹 情况 B:A[i] > B[j]

同理,B[j] 更小,也不可能出现在 A 后面,所以跳过 B[j]:

else if (A[i] > B[j]) {j++;
}

🔹 情况 C:A[i] == B[j]

找到了一个交集元素!
但我们还需要 去重,也就是防止重复元素写入 C。

else {if (k == 0 || C[k - 1] != A[i]) {C[k++] = A[i];  // 添加到交集结果数组 C 中}i++;j++;
}

说明:

  • k == 0:是第一个元素,直接添加

  • C[k - 1] != A[i]:当前值没重复才添加

🔹 步骤 3:循环结束,交集数组 C 已完成

你可以用一个循环输出 C:

for (int p = 0; p < k; p++) {cout << C[p] << " ";
}

完整代码 

#include <iostream>
using namespace std;void intersectionSorted(int A[], int m, int B[], int n) {int C[100]; // 足够大的数组int i = 0, j = 0, k = 0;while (i < m && j < n) {if (A[i] < B[j]) {i++; // A[i] 小了,不可能在交集中}else if (A[i] > B[j]) {j++; // B[j] 小了,也不在交集中}else {// 元素相等,可能是交集,但要去重if (k == 0 || C[k - 1] != A[i]) {C[k++] = A[i]; // 添加到结果数组}i++;j++;}}// 输出结果数组 Ccout << "交集 C = ";for (int p = 0; p < k; p++) {cout << C[p] << " ";}cout << endl;
}

📊 算法复杂度分析

 时间复杂度:

使用的是 双指针遍历:

  • 每次最多移动 i 或 j 中的一个

  • 最多移动 m + n 次

时间复杂度 = O(m + n),这是最优的方式之一,适用于已排序数组。

 空间复杂度:

  • 使用了一个新数组 C,最多 m+n 个元素(极限情况是 A 和 B 完全相同)

  • 但实际上,交集最多是 min(m, n)

空间复杂度 = O(min(m, n))


差集(Difference)A - B

差集 A - B 表示:在数组 A 中出现,但在数组 B 中没有出现的所有元素(去重),存入数组 C。

未排序数组的差集 

问题定义

  • 输入:

    • 无序数组 A(长度 m)

    • 无序数组 B(长度 n)

  • 输出:

    • 数组 C:A 中存在但 B 中没有的所有元素(不重复)

🧠 差集本质分析(以 A - B 为例)

对于 A 中的每个元素:

  1. 判断它是否出现在 B 中

  2. 如果 没有出现在 B 中,再判断它是否已经加入过 C(避免重复)

  3. 满足条件 → 加入 C

 🪜 算法步骤

🔹 Step 1:初始化结果数组 C

int C[m];  // 最多 m 个差集元素
int k = 0; // 当前结果长度

🔹 Step 2:遍历 A 中的每个元素

for (int i = 0; i < m; i++) {...
}

🔹 Step 3:判断 A[i] 是否在 B 中出现

bool foundInB = false;
for (int j = 0; j < n; j++) {if (A[i] == B[j]) {foundInB = true;break;}
}

🔹 Step 4:若 A[i] 不在 B 中,还需判断是否已加入 C

bool existsInC = false;
for (int x = 0; x < k; x++) {if (C[x] == A[i]) {existsInC = true;break;}
}

🔹 Step 5:满足条件,加入结果数组 C

if (!foundInB && !existsInC) {C[k++] = A[i];
}

完整代码实现

#include <iostream>
using namespace std;// 判断某个值是否在数组中
bool exists(int arr[], int size, int val) {for (int i = 0; i < size; i++) {if (arr[i] == val)return true;}return false;
}// 求未排序数组的差集 A - B
void differenceUnsorted(int A[], int m, int B[], int n) {int C[100]; // 差集结果数组int k = 0;for (int i = 0; i < m; i++) {bool foundInB = false;// 检查 A[i] 是否出现在 B 中for (int j = 0; j < n; j++) {if (A[i] == B[j]) {foundInB = true;break;}}// 如果不在 B 中,且还没加入过 Cif (!foundInB && !exists(C, k, A[i])) {C[k++] = A[i];}}// 输出结果cout << "差集 A - B = ";for (int i = 0; i < k; i++) {cout << C[i] << " ";}cout << endl;
}int main() {int A[] = {3, 5, 10, 4, 6};int B[] = {12, 4, 7, 2, 5};int m = sizeof(A) / sizeof(A[0]);int n = sizeof(B) / sizeof(B[0]);differenceUnsorted(A, m, B, n);return 0;
}

🔁 模拟过程

A = {3, 5, 10, 4, 6};
B = {12, 4, 7, 2, 5};
A[i]是否在B中是否在C中是否加入CC内容
3{3}
5-{3}
10{3, 10}
4-{3, 10}
6{3, 10, 6}

📊 时间复杂度分析

✅ 时间复杂度:

我们有三层循环:

  1. 外层遍历 A:O(m)

  2. 内层查找是否在 B 中:O(n)

  3. 再查一遍是否在 C 中:最坏 O(m)

所以:

若 m ≈ n ⇒ 最坏为 O(n²),为暴力算法 

✅ 空间复杂度:

  • 新数组 C,最多 m 个元素

  • 空间复杂度 = O(m)


已排序数组的差集 

输入特点:

  • 两个数组 A 和 B 都是升序排列

  • 可以使用 双指针法 高效比较

🧠 思路分析:双指针遍历差集 A - B

使用两个指针:

  • i 遍历数组 A(长度 m)

  • j 遍历数组 B(长度 n)

  • k 指向结果数组 C 的当前位置

 🪜 步骤讲解

🔹 Step 1:初始化指针

int i = 0, j = 0, k = 0;

🔹 Step 2:同时遍历两个数组,判断 A[i] 是否在 B 中

while (i < m && j < n) {...
}

✅ 情况 1:A[i] < B[j]

说明 A[i] 小于 B[j],它不可能在 B 中出现 → A[i] 应该加入差集 C

if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];
i++;

✅ 情况 2:A[i] > B[j]

说明 B[j] 小于 A[i],还不能判断 A[i] 是否在 B 中,需要继续推进 j

j++;

✅ 情况 3:A[i] == B[j]

说明这个元素在 A 和 B 中都有 → 不应加入差集 → 同时跳过 A[i] 和 B[j]

i++;
j++;

🔹 Step 3:处理 A 剩下的元素(B 已走完)

while (i < m) {if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;
}

完整代码实现

#include <iostream>
using namespace std;void differenceSorted(int A[], int m, int B[], int n) {int C[100]; // 结果数组 Cint i = 0, j = 0, k = 0;while (i < m && j < n) {if (A[i] < B[j]) {if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;}else if (A[i] > B[j]) {j++;}else { // A[i] == B[j]i++;j++;}}// 把 A 中剩余未处理的元素加入 Cwhile (i < m) {if (k == 0 || C[k - 1] != A[i])C[k++] = A[i];i++;}// 输出差集结果cout << "差集 A - B = ";for (int p = 0; p < k; p++) {cout << C[p] << " ";}cout << endl;
}int main() {int A[] = {2, 4, 6, 8, 10};int B[] = {4, 6, 12};int m = sizeof(A) / sizeof(A[0]);int n = sizeof(B) / sizeof(B[0]);differenceSorted(A, m, B, n);return 0;
}

🔁 示例模拟

A = {2, 4, 6, 8, 10}
B = {4, 6, 12}
ijA[i]B[j]比较结果加入CC数组内容
0024A[i] < B[j]{2}
1044相等{2}
2166相等{2}
32812A[i] < B[j]{2, 8}
421012A[i] < B[j]{2, 8, 10}
5---i >= m,结束

📊 复杂度分析

✅ 时间复杂度:

  • 双指针最多移动 m+n 次

  • 每次处理最多一次操作(比较/复制)

总时间复杂度:O(m + n)  

✅ 空间复杂度:

  • 新数组 C 最多 m 个元素(全部保留的极端情况)

  • 空间复杂度 = O(m) 

http://www.dtcms.com/a/284083.html

相关文章:

  • HTTP相关知识
  • os.path 常用的使用方法
  • 高光谱相机有多少种类型?分别有什么特点?
  • Odoo最佳业务实践:从库存管理重构到全链路协同
  • 【数据结构】链表(linked list)
  • slot=“trigger“ 覆盖了组件内部的 ref=“trigger“【详细来龙去脉版 5min】
  • 网络服务(设置邮箱发送告警功能,每1分钟发送一封邮件)
  • CMake综合学习1: Cmake的模块化设计
  • vue3实现web端和小程序端个人签名
  • 《迭代器 VS 生成器:Python 惰性计算的两种实现方案详解》
  • Zabbix 分布式监控系统架构设计与优化
  • 硅谷顶级风投发布《2025年AI实战手册》|附下载
  • 芯谷科技--双四通道模拟/数字多路复用器74HC4052
  • Tcpdump使用
  • linux_线程同步
  • 七彩喜跌倒检测仪:用科技编织银发安全的“隐形防护网”
  • 【Mobx】学习笔记
  • Python-多线程编程
  • 时序数据库
  • AWS WebRTC:RTP讲解
  • GitHub开源轻量级语音模型 Vui:重塑边缘智能语音交互的未来
  • onenote千年老bug,字体bug (calibri微软雅黑) 的解决
  • Spring之【BeanDefinition】
  • MyBatis之核心组件与配置详解
  • LVDS系列21:Xilinx 7系ISERDESE2原语(二)
  • Amazon发布Kiro AI IDE
  • Kali Linux 信息收集完全指南:从原理到实战
  • 月结7-CO模块月结关键点和步骤总结
  • EP-SPY 基於GI6E 編碼無線電通訊間諜工具
  • 串口232通讯数据传输丢失的原因、不可靠性及底层原理分析