众数问题
给定一个含有 n 个元素的多重集合 S,每个元素 S 出现的次数称为该元素的重数。多重集 S 中重数最大的元素称为众数。例如,集合S={1,2,2,2,3,5} 的众数为 2,重数为 3。
1. 算法原理
该算法通过递归分治法来寻找多重集合中的众数。具体步骤如下:
选择中位数:
- 使用
median()
函数选择当前数组的中位数。
分割数组:
- 使用
split()
函数将数组分割为两部分:小于等于中位数的部分和大于中位数的部分。
递归查找:
- 计算分割后两部分的大小。
- 如果某一部分的大小大于当前已知的最大重数,则递归查找该部分。
- 否则,继续查找另一部分。
更新众数:
- 在递归过程中,更新当前找到的最大重数和对应的元素。
2. 算法步骤
初始化:
- 定义数组
a[]
存储多重集合的元素。 - 定义变量
largest
用于记录当前找到的最大重数。 - 定义变量
element
用于记录当前找到的众数。
递归查找:
- 调用
mode(l, r)
函数开始递归查找众数。 - 在
mode()
函数中,选择中位数并分割数组。 - 根据分割后两部分的大小,递归查找最大重数的部分。
更新众数:
- 在递归过程中,更新
largest
和element
。
3. 图示法表示步骤
假设我们有以下多重集合:
S = {1, 2, 2, 2, 3, 5}
步骤 1:初始化
largest = 0
element = -1
a = [1, 2, 2, 2, 3, 5]
步骤 2:递归查找
第一次递归:
- 选择中位数
med = 2
- 分割数组:
l1 = 0
,r1 = 3
- 更新
largest = 3
,element = 2
- 递归查找
mode(0, 0)
和mode(4, 5)
第二次递归:
mode(0, 0)
:数组为空,返回。mode(4, 5)
:选择中位数med = 3
- 分割数组:
l1 = 4
,r1 = 5
- 更新
largest = 3
,element = 2
- 递归查找
mode(4, 3)
和mode(6, 5)
第三次递归:
mode(4, 3)
:数组为空,返回。mode(6, 5)
:数组为空,返回。
最终结果
- 众数为
2
,重数为3
4. 代码关键行注释
#include <iostream>
#include <algorithm>
using namespace std;
// median()函数用于寻找中位数
int median(int a[], int l, int r) {
sort(a + l, a + r + 1); // 对数组进行排序
return a[(l + r) / 2]; // 返回中位数
}
// split()函数用中位数将数组分割为2段
void split(int a[], int med, int l, int r, int& l1, int& r1) {
int i = l, j = r;
while (i <= j) {
while (a[i] <= med) i++;
while (a[j] > med) j--;
if (i < j) swap(a[i], a[j]);
}
l1 = l;
r1 = j;
}
// mode()函数递归查找众数
void mode(int a[], int l, int r, int& largest, int& element) {
if (l > r) return; // 如果数组为空,返回
int med = median(a, l, r); // 选择中位数
int l1, r1;
split(a, med, l, r, l1, r1); // 分割数组
int size = r1 - l1 + 1; // 计算分割后数组的大小
if (size > largest) { // 如果当前大小大于最大重数
largest = size; // 更新最大重数
element = med; // 更新众数
}
if (l1 - l > largest) { // 如果左部分的大小大于最大重数
mode(a, l, l1 - 1, largest, element); // 递归查找左部分
}
if (r - r1 > largest) { // 如果右部分的大小大于最大重数
mode(a, r1 + 1, r, largest, element); // 递归查找右部分
}
}
int main() {
int n;
cin >> n; // 输入元素个数
int a[n];
for (int i = 0; i < n; i++) {
cin >> a[i]; // 输入元素
}
int largest = 0, element = -1;
mode(a, 0, n - 1, largest, element); // 调用mode()函数查找众数
cout << element << endl; // 输出众数
cout << largest << endl; // 输出重数
return 0;
}
5. 时间复杂度
- 时间复杂度:
median()
函数的时间复杂度为 O(nlogn),因为需要对数组进行排序。split()
函数的时间复杂度为 O(n)。mode()
函数的时间复杂度为O(nlogn),因为递归深度为logn,每次递归调用median()
和split()
。- 总时间复杂度为 O(nlogn)。
6. 总结
通过递归分治法,可以高效地寻找多重集合中的众数。该算法的时间复杂度为线性级别,适用于各种大小的多重集合。