有趣算法之众数问题

2-1 众数问题

20201004-20201010

 

参考链接:

http://ddrv.cn/a/70561

https://www.cnblogs.com/program-ai-cv-ml-se-fighting/p/11944099.html

https://blog.csdn.net/hasfun/article/details/82688428

写文件:https://blog.csdn.net/u010925447/article/details/75046810

 

 

1.问题描述

给定含有n个元素的多重集合s,每个元素在S中出现的次数称为该元素的重数。多重集S中重数最大的元素称为众数。

 

例如:S={1,2,2,2,3,5}。多重集S的众数的2,其重数为3.

  • 算法设计:对于给定的由N个自然数组成的多重集S,计算S的众数及其重数。
  • 数据输入:输入数据有文件名为input.txt的问问文件内容提供。文件的第一行为多重集S中元素个数n留在接下来的n行中,每行有一个自然数。
  • 结果输出:将计算结果输出到文件output.txt。输出文件有2行,第1行是众数,第2行数重数。

 

image.png

 

2.问题分析

 

2.1.分治分析

由于最近在学分治算法,所以就先拿分治来开个刀吧!

回顾题目:给定N个元素,求出元素中的众数以及众数重数。例如:2 2 2 1 3 5

思路:将元素集合进行排序(1  2 2 2 3 5),假定此时的中位数为众数(中位数2),指针左右滑动求出中位数的次数,依据中位数为中间集合进行划分,分为三个集合区域,如果左边、右边集合总长度 > 中位数次数,那么按照上述方法依次划分;否则(即总长度 < 中位数次数),那么此时中位数便是众数了,没有必要再继续划分了!

(如下图,m为随机取的中位数,空白部分为快速排序的基准值,i,j为分治快速排序时的左右下标,当ij在3处相遇时,,众数即中位数重数=1,左边部分长度=3》1,继续划分,右边=1,不划分)

image.png

 

2.2.非分治算法

这个是在偶然看见的一个算法,觉得很是巧妙,所以就记录下来了,但是这个损耗空间资源较大!

 

思路:先定义一个长度为1000的数组a(假设输入的数据大小 < 1000),输入的元素对应数组的下标,数组元素的值对应数组的重数,(假设输入2,a[2]++,所以下标 2-->元素,a[2]=1-->2的重数为1)输入时求出最大的重数。然后在数组中寻找元素值 = 最大重数的下标,即求出众数

 

image.png

 

3.代码实现

 

3.1.分治算法

// mode_function.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <iostream.h>
#include<fstream>//用于文件输入输出
#include <time.h>
#include <stdlib.h>
using namespace std;
#define N 1024

/*---交换函数*/

inline void swap(int &a,int &b)
{
    int temp=a;a=b;b=temp;
}

/*---快速排序算法*/
//含有重复元素时候,例如:2 2 2 1 3 5
int Partition_sort(int a[],int low,int high)
{
    int x=a[low];
    int i=low,j=high;
    while(i!=j){
        while(x<=a[j] && i<j) j--;
        if(i<j) {
            int temp=a[i];
            a[i]=a[j];
            a[j]=temp;
            i++;
        }
        while(x>=a[i] && i<j) i++;
        if(i<j) {
            int temp=a[i];
            a[i]=a[j];
            a[j]=temp;
            j--;
        }
    }
//  cout<<"low="<<low<<endl;------0
//  cout<<"high="<<high<<endl;------5
//  cout<<"i="<<i<<endl;------3
    return i;
/*
    for(int m=0;m<6;m++){
        cout<<"第"<<m<<"个元素为"<<a[m]<<endl;
    }
*/
    
}
//随取一个数作为基准值
int RandomizedPartition(int a[],int l,int r)
{   
    srand(time(NULL));
    int i=rand()%(r-1)+1;
    swap(a[i],a[l]);
    return Partition_sort(a,l,r);
}

/*---按中位数划分成两段*/
void split(int a[],int m,int l,int r,int *left,int *rigth)
{
    int med=a[m];
    /*---从中位数开始向左右划分,知道遇见!=中位数*/
    while(a[--(*left)] == med);
    while(a[++(*rigth)] == med);
    (*left)++;
    (*rigth)--;
}



/*---分治求解*/
void mode(int *a,int l,int r,int *largest,int *count)//largest和count为地址
{
    int m = RandomizedPartition(a,l,r);//--返回中位数
    //cout<<"m="<<m<<endl;
    int left=m,rigth=m;
    split(a,m,l,r,&left,&rigth);//将数组a划分为3部分
    
    if(*largest < rigth-left+1){//--保留众数个数最大值,以及众数下标
        *largest=rigth-left+1;
        *count=m;
    }
    if(left-1 > *largest) mode(a,l,left-1,largest,count);//左边的元素大于众数的个数,继续分治
    if(r-rigth > *largest ) mode(a,rigth+1,r,largest,count);//右边元素大于众数个数,则继续分治
//  cout<<"众数是:"<<count<<"\t重数是:"<<largest<<endl;
}



int main()  
{
    ifstream infile("input.txt",ios::in);
    /*-------------读取元素的个数*/
    int n;
    infile>>n;
    //cout<<n;
    
    //元素读取
    int a[N];
    for(int i=0; i<n;i++)infile>>a[i];
    infile.close();
    //for(i=0; i<n;i++) cout<<a[i];

    int largest=0,count=0;//large:重数,count:众数下标
    mode(a,0,n-1,&largest,&count);
    cout<<"众数是:"<<a[count]<<"\t重数是:"<<largest<<endl;
    
    ofstream outfile;
    outfile.open("out.txt");
    cout << "Writing to the file" << endl;
    outfile<<a[count]<< endl;
    outfile<<largest<< endl;
    outfile.close();
    return 0;  
}  

输出结果:

image.png

 

 

 

3.2.非分治算法

#include "stdafx.h"
#include <iostream>
#include<fstream>//用于文件输入输出
using namespace std;
#define N 1000



/*----------main()*/
int main()
{
    ifstream infile("input.txt",ios::in);
    /*-------------读取元素的个数*/
    int n;
    infile>>n;
    //cout<<n;
    
    //元素读取
    int a[N]={0};
    int elem,count=0;
    //元素值对应数组下标,数组值对应重数
    for(int i=0; i<n;i++)
    {
        infile>>elem;
        a[elem]++;
        if(a[elem] > count) count=a[elem];
    }
    infile.close();
    //for(i=0; i<n;i++) cout<<a[i];
    
    int b[N]={0},j=0;//存放众数的数组和下标
    for(i=0;i<N;i++)
    {
        if(a[i] == count && i<N)
        {
            b[j]=i,j++;//i就是众数
        }
    }
    

    //结果写入
    ofstream outfile;
    outfile.open("out.txt");
    cout << "Writing to the file" << endl;
    for(i=0;i<j;i++)
    {
        cout<<"众数为"<<b[i]<<"\t"<<"重数为"<<count<<endl;
        outfile<<b[i]<<"\t"<<count<<endl;
    }
    outfile.close();
    return 0;
}

输出结果:

image.png

 

tips

看代码如果看不太懂的话,建议读者按照代码把实际的数代入进去,对于代码理解很好哦!下面是我自己在理解代码时的手稿,字迹潦草,仅供参考!

 

分值算法:

众数分治01.jpg

众数分治02.jpg

 

非分治算法:

 

  • 6
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值