找众数(可能有多个)

发布于:2024-11-04 ⋅ 阅读:(116) ⋅ 点赞:(0)

题目:在一个序列中出现次数最多的元素称为众数。请设计算法寻找众数,并分析算法的时间复杂度。

答:问题分析:考虑序列中出现次数最多的数有多个,即出现多个众数,因此需要一个一维数组mode[]存储这些众数。使用分治法找出所有的众数,首先将输入元素从小到大进行排序,然后找到与a[mid]相等的数的范围a[mid_l..mid_r]。如果l~mid_l的长度大于等于mid_l~mid_r的长度,需要在l~mid_l范围内继续找,如果mid_r~r的长度大于等于mid_l~mid_r的长度,需要在mid_r~r中继续找。

算法设计:使用sort()对输入序列从小到大进行排序,找到与a[mid]相等的数的范围a[mid_l..mid_r],然后进行判断(因为可能有多个众数,所以是大于等于):

        if(mid_l-l>=cnt_max){

             findMode(l,mid_l-1,a);

        }

        if(r-mid_r>=cnt_max){

             findMode(mid_r+1,r,a);

        }

设当前找到的众数的出现次数为cnt_max,众数个数为mode_cnt。如果出现了新的最大值,则重新计数,新出现的最大值赋给cnt_max,mode_cnt为1;如果出现了和当前最大值一样的数,mode[mode_cnt++]=a[mid]。

        int cnt=mid_r-mid_l+1;//a[mid]的出现次数

        if(cnt>cnt_max){

             cnt_max=cnt;

             mode_cnt=0;//原来的众数清零

              mode[mode_cnt++]=a[mid];//重新放入

        }else if(cnt==cnt_max){

             mode[mode_cnt++]=a[mid];

        }

时间复杂度分析: sort()排序的时间复杂度为O(nlogn),findMode()函数的时间复杂度递推公式为T(n)=2T(n/2)+O(n)。

T(n)=2T(n/2)+nO(1)=22T(n/22)+n+n=23T(n/23)+n+n+n=…=2kT(n/2k)+kn

令n=2k,T(n)=nT(1)+nlogn=n+nlogn。

时间复杂度也为O(nlogn),所以该算法的平均时间复杂度为O(nlogn)。

具体代码

#include<iostream>
#include<algorithm>
using namespace std;
int mode[10];//众数
int mode_cnt;//众数的个数
 
int cnt_max;//众数出现次数

void findMode(int l,int r,int a[]){//找众数(众数只有一个) 
	 int mid=(l+r)>>1;
	 int mid_l,mid_r;//和a[mid]相等的数的范围a[mid_l..mid_r] 
	 for(int i=l;i<=mid;i++){
	 	if(a[i]==a[mid]){
	 		mid_l=i;
			break; 
		}
	 }
	 for(int i=mid;i<=r;i++){
	 	if(a[i]==a[mid]){
	 		mid_r=i;
		}else{
			break;
		}
	 }
	 int cnt=mid_r-mid_l+1;//a[mid]的出现次数 
	 if(cnt>cnt_max){
	 	cnt_max=cnt;
	 	mode_cnt=0;//原来的众数清零
		mode[mode_cnt++]=a[mid];//重新放入 
	 }else if(cnt==cnt_max){ 
	 	mode[mode_cnt++]=a[mid];
	 } 
	 if(mid_l-l>=cnt_max){
	 	findMode(l,mid_l-1,a);
	 }
	 if(r-mid_r>=cnt_max){
	 	findMode(mid_r+1,r,a);
	 }
}
int main(){
	int n;
	cout<<"请输入元素个数:"; 
	cin>>n;
	cout<<"请输入各个元素:";
	int a[n]; 
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	sort(a,a+n);
	findMode(0,n-1,a);
	cout<<"众数为:"<<endl; 
	for(int i=0;i<mode_cnt;i++){
		cout<<mode[i]<<" ";
	} 
	return 0;
}

思路2:不进行排序,通过快排将小于等于基准数的数全部放在左边,同时统计与基准数相等的数的个数,将大于基准数的数全部放在右边。一次快排结束后可以得到基准数的下标pos以及基准数出现的个数cnt,将cnt与cnt_max比较来判断该基准数是否为众数。分治:如果pos-l大于等于cnt_max(依然考虑有多个众数),则继续在左边找,右边同理。

具体代码

ream>
#include<algorithm>
using namespace std;

int mode[10];//众数
int mode_cnt;//众数的个数
 
int cnt_max;//众数出现次数的最大值 

void findMode(int l,int r,int a[]){
	if(l>r) return;
	int cnt=1;
	int b=a[l];
	//一次快排 
	int low=l,high=r;
	while(low<high){
		while(low<high&&a[high]>b) high--;
		a[low]=a[high];
		while(low<high&&a[low]<=b){
			if(a[low]==b) cnt++;
			low++;
		}
		a[high]=a[low];
	}
	a[low]=b;
	
	int pos=low;
	if(cnt>cnt_max){
	 	cnt_max=cnt;
	 	mode_cnt=0;//原来的众数清零
		mode[mode_cnt++]=a[pos];//重新放入 
	 }else if(cnt==cnt_max){ 
	 	mode[mode_cnt++]=a[pos];
	 } 
	 if(pos-l>=cnt_max){
	 	findMode(l,pos-1,a);
	 }
	 if(r-pos>=cnt_max){
	 	findMode(pos+1,r,a);
	 }
} 
int main(){
	int n;
	cout<<"请输入元素个数:"; 
	cin>>n;
	cout<<"请输入各个元素:";
	int a[n]; 
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	findMode(0,n-1,a);
	cout<<"众数为:"<<endl; 
	for(int i=0;i<mode_cnt;i++){
		cout<<mode[i]<<" ";
	} 
	return 0;
}