算法之众数问题 | 8月更文挑战

433 阅读2分钟

要求:

1.在一个包含n个元素的多重集合S中,每个元素在S中出现的次数称为该元素的重数, 多重集合S中重数最大的元素称为众数。

2.现要求对随机生成的由n个自然数组成的多重集合S, 编程计算S的众数及其重数。

实验过程:

一、借助额外一个数组

思路:

用额外一个数组来保存数组中数字对应的重数。关键代码如下:

  public static void main(String args[]){
    	Scanner scanner= new Scanner(System.in);
    	int n=scanner.nextInt();//输入数组长度
    	scanner.close();
    	int num[]=new int[n];
    	Random r =new Random();
        int key=0;
    	for(int i=0;i<n;i++){
    		num[i]=r.nextInt(n);//生成随机数
    	}
    	System.out.println(Arrays.toString(num));
    	int temp[]=new int[n];
    	for(int i=0;i<n;i++){
    		key=num[i];
    		temp[key]++;//将下标对应为数组中的数字长度加1
    		System.out.println(Arrays.toString(temp));
    	}
    	int max=0;
    	int number=0;
    	for(int i=0;i<n;i++){
    		if(temp[i]>max){
    			max=temp[i];//max保存最大重数
    		    number=i;//number保存对应的众数
    		}
    	}
    	System.out.println("该数组众数为:"+number);
    	System.out.println("且它的的重数为:"+max);
     }
缺点:

数组中数可能很分散,所以需要一个较大的临时数组来保存重数,浪费大量的空间。

二、分治法求解

思路:

假设我们先求出中位数的重数,再求出中位数第一次出现的位置(中位数个数不止一个),如果发现这个位置左边的数小于这个重数那么我们就不需要在往左边寻找,因为左边已经没有比这个中位数重数更大的数了,右边同理。

语言换成c++,据说c++写算法更便于掌握算法。

//用分治法求解众数
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<ctime>
#include <time.h>

using namespace std;

map<int,int> m;

//确定众数左右两边边界
void split(int s[],int n,int &l,int &r)
{
    int mid = n/2;
    //确定第一个与s[mid]相同的数组位置
    for (l=0;l<n;l++)
    {
        if(s[l]==s[mid])
        break;
    }
     //确定右边第一个与s[mid]不同的数组位置
    for(r=l+1;r<n;r++)
    {
        if(s[r]!=s[mid])
        break;
    }
}

//mid为众数,maxCnt为对应的重数
void getMaxCnt(int & mid,int & maxCnt,int s[],int n)
{
    int l,r;
    split(s,n,l,r);
    int num =n/2;
    int cnt = r-l;//s[mid]的重数

    //更新出现次数最多的数
    if(cnt > maxCnt)
    {
        maxCnt = cnt;
        mid = s[num];
        m.clear();
        m[mid] = maxCnt;
    }
    else if(cnt == maxCnt)//重数相同的情况也会被保存到map数组中
    {
        mid = s[num];
        m[mid]=maxCnt;
    }

    //l为众数左边个数,当l<众数的个数时,没有必要再往左边递归,这是个递归终止条件
    if(l>maxCnt)
    {
        getMaxCnt(mid,maxCnt,s,n-r);
    }
     // n-r为众数右边数组个数,同样的,当众数右边数字个数小于众数个数,右边也不需要再继续递归了
    if (n-r >= maxCnt)
    {
        getMaxCnt(mid, maxCnt, s+r, n-r);
    }
}

int main(void)
{
    int size;
    cout <<"请输入数组的大小:"<<endl;
    cin>>size;
    int *s=new int[size];
    srand((unsigned)time(NULL));
    for(int i=0;i<size;i++)
    {
        s[i]=rand()%10+1;
        cout<<s[i]<<endl;
    }
    sort(s, s+size);//数组排序

    int maxCnt = 0;
    int num = 0;
    getMaxCnt(num, maxCnt, s, size);

    map<int, int>::iterator it;
    int sum = 0;
    for(it = m.begin(); it != m.end(); it++)
    {
        sum += it->second;
    }
    if(sum == size) // 没有众数时
    {
        cout << "没有众数" << endl;
    }
    else
    {
        for(it = m.begin(); it != m.end(); it++)
        {
            cout << "众数:" << it->first << "   "<<"出现的次数" << it->second << endl;
        }
    }

    system("pause");
    return 0;

}

为了便于观察,将数组中数大小设计在1到10以内,数组大小输入10000,所得结果为:

image-20201111173753961

缺点:

采用分治法需要先排序。