1.问题描述:
给定含有n个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数。重数最大的元素为该集合的众数。
例如,S={1,2,2,2,3,5},众数是2,其重数是3。
对于给定的多重集S,计算S的众数与重数。
2.思路:
1).首先假设中间的元素是众数
2). 然后由两边向中间遍历,直到左右都出现值等于众数的数,记录下众数和重数;
3). 这样就将一个数组分为三部分,我们再对左右部分执行上述步骤;若左边数组的个数大于中位数的个数,则递归左边数组,同理右边相同;
4). 注意:使用分治策略解决众数问题需要原集合有序,在原集合无序的情况下需要对其排序,建议数据输入之后先进行排序
ps:如果出现多组众数,以最后一组为最终答案。
3.Code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int n,m,T;
int a[N];
int ans = 0; //众数的重数
int idx = 0; //众数的下标
void split(int l,int r) {//分治算法
if(l > r) return;
int ll = l; //记录原来的l位置
int rr = r; //记录原来的r位置
int mid = (l + r) >> 1;
for(; l < mid && a[l] != a[mid]; l ++ ); //寻找众数的最左边(即左边开始第一个众数)
for(; r > mid && a[r] != a[mid]; r -- ); //寻找众数的最右边(即左边开始最后一个众数)
//经过两个for循环后,众数个数就是r - l + 1
if(ans <= r - l + 1) {//递归过程中发现重数更大的众数,就及时更新答案
if(ans == r - l + 1)
idx = min(mid,idx);//此步操作保证若得到多组重数相等的众数,则选择下标小的
else
idx = mid;
ans = r - l + 1;
}
if((l - 1) - ll + 1 >= ans) //若左边数组的个数大于中位数的个数,则递归左边数组(若不大于则没必要,因为即使有众数也不可能重数大于当前重数)
split(ll,l - 1);
if(rr - (r + 1) + 1 >= ans) //若右边数组的个数大于中位数的个数,则递归右边数组
split(r + 1,rr);
}
int main(){
scanf("%d",&n);
for(int i = 0; i < n; i ++ )
scanf("%d",a + i);
sort(a,a + n); //排序(后续操作均是基于升序)
int l = 0;
int r = n - 1;
split(l,r);
printf("%d\n%d\n",a[idx],ans);
return 0;
}
4.代码运行截图: