版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址

http://www.cnblogs.com/Colin-Cai/p/12664044.html

作者:窗户

QQ/微信:6679072

E-mail:6679072@qq.com

所谓众数,源于这样的一个题目:一个长度为len的数组,其中有个数出现的次数大于len/2,如何找出这个数。

基于排序

排序是第一感觉,就是把这个数组排序一下,再遍历一遍得到结果。

C语言来写基本如下:

int find(int a, int len)

{

sort(a, len);

return traverse(a, len);

}

排序有时间复杂度为O(nlogn)的算法,比如快速排序、归并排序、堆排序,而遍历一遍排序后的数组得到结果是时间线性复杂度,也就是O(n)。所以整个算法时间复杂度是O(nlogn)。

寻找更好的算法

上面的算法实在太简单,很多时候我们第一感就可以出来的东西未必靠谱。

我们是不是可以找到一种线性时间级别的算法,也就是Θ(n)时间级别的算法?Θ是上下界一致的符号。其实我们很容易证明,不存在比线性时间级别低的算法,也就是时间o(n),小o不同于大O,是指低阶无穷大。证明大致如下:

如果一个算法可以以o(n)的时间复杂度解决上述问题。因为是比n更低阶的无穷大,那么一定存在一个长度为N的数组,在完成这个算法之后,数组内被检测的元素小于N/2。假设算法运算的结果为a,然后,我们把这个数组在运算该算法时没有检测的元素全部替换为成同一个不是算法所得结果a的数b。然后新的数组,再通过算法去运算,因为没有检测的数不会影响其算法结果,结果自然还是a,可实际上,数组超过N/2次出现的数是b。从而导致矛盾,所以针对该问题的o(n)时间算法不存在。

我们现在可以开始想点更加深入点的东西。

我们首先会发现,如果一个数组中有两个不同的数,将数组去掉这两个数,得到一个新数组,那么这个新数组依然和老数组一样存在相同的众数。这一条很容易证明:

假设数组为a,长度为len,众数为x,出现的次数为t,当然满足t>len/2。假设其中有两个数y和z,y≠z。去掉这两个数,剩下的数组长度为len-2。如果这两个数都不等于众数x,也就是x≠y且x≠z,那么x在新的数组中出现的次数依然是t,t>len/2>(len-2)/2,所以t依然是新的数组里的众数。而如果这两个数中存在x,那么自然只有一个x,则剩下的数组中x出现的次数是t-1,t-1>len/2-1=(len-2)/2,所以x依然是新数组的众数。

有了上述的思路,我们会去想,如何找到这一对对的不同的数呢。

我们可以记录数字num和其重复的次数times,遍历一遍数组,按照以下流程图来。

num/times一直记录着数字和其重复次数,times加1和减1都是随着数组新来的数是否和num相同来决定,减1的情况其实就取决于上面证明的那个命题,找到一对不相同的数字,去掉这两个,剩下的数组的众数不变。

关于在于证明最后的结果是所求的众数。如果后面的结果不是众数,那么众数每出现一次,就得与一个不是众数的数一起“抵消”,所以数组中不是众数的数的数量不会少于众数的数量,然而这不是现实。于是上述算法成立,它有着线性时间复杂度O(n),常数空间复杂度O(1)。

C语言代码基本如下:

int find(int *a, int len)

{

int i, num = 0, times = 0;for(i=0;i

if(times > 0) {

if(num == a[i])

times++;

else

times--;

} else {

num = a[i];

times = 1;

}

}

return num;

}

如果用Scheme编写,程序可以简洁如下:

(define (find s)

(car

(fold-right

(lambda (n r)

(if (zero? (cdr r))

(cons n 1)

(cons (car r) ((if (eq? n (car r)) + -) (cdr r) 1))))

'(() . 0) s)))

升级之后的问题

上面的众数是出现次数大于数组长度的1/2的,如果将这里的1/2改成1/3,要找出来怎么做呢?

例如,数组是[1, 1, 2, 3, 4],那么要找出的众数为1。

再升华一下,如果是1/m,这里的m是一个参数,该怎么找出来呢?这个问题要去之前那个问题要复杂一些,另外我们要意识到,问题升级之后,众数是有可能不止一个的,比如[1, 1, 2, 2, 3]长度为5,1和2都大于5/3。最多有m-1个众数。

思路

如果依然是排序之后再遍历,依然是有效的,可是时间复杂度还是O(nlogn)级别,我们还是期待有着线性时间复杂度的算法。

对于第一个问题,成立的前提是去掉数组里两个不一样的数,众数依然不变。那么对于升级之后的问题,是不是依然有类似的结果。不同于之前,我们现在来看在众数从1/2以上变成1/m以上,我们来看去掉长度为len的数组a里m个互不相同的数,会发生什么。证明过程如下:

同样,我们假设a里有一个众数x,x出现的次数为t,看看去掉m个不一样的数之后x还是不是众数。去掉m个数之后,新的数组长度为len-m。x是众数,所以x的出现次数t > len/m,如果去掉的m个数中没有x,则x在剩余的数组中的出现次数依然是t,t > len/m > (len-m)/m,所以这种情况下x还是众数;如果去掉的m个数中存在x,因为m个数互不相同,所以其中x只有一个,所以x在剩余的数组中的出现次数是t-1,t > len/m,从而t-1 > len/m-1 = (len-m)/m,所以x在剩余的数组里依然是众数。以上对于数组中所有的众数都成立。同理可证,对于数组中不是众数的数,剩余的数组中依然不是众数,实际上,把上面所有的>替换为≤即可。

有了上面的理解,我们可以仿照之前的算法,只是这里改成了长度最多为n-1的链表。比如对于数组[1, 2, 1, 3],众数1超过数组长度4的1/3,过程如下

初始时,空链表[]

检索第一个元素1,发现链表中没有记录num=1的表元,链表的长度没有达到2,所以插入到链表,得到[(num=1,times=1)]

检索第二个元素2,发现链表中没有记录num=2的表元,链表的长度没有达到2,插入到链表,得到[(num=1,times=1), (num=2,times=1)]

检索第三个元素1,发现链表中已经存在num=1的表元,则把该表元times加1,得到[(num=1,times=2), (num=2,times=1)]

检索第四个元素3,发现链表中没有num=3的表元,链表长度已经达到最大,等于2,于是执行消去,也就是每个表元的times减1,并把减为0的表元移出链表,得到[(num=1,times=1)]

以上就是过程,最终得到众数为1。

以上过程最终得到的链表的确包含了所有的众数,这一点很容易证明,因为任何一个众数的times都不可能被完全抵消掉。但是,以上过程实际并不保证最后得到的链表里全都是众数,比如[1,1,2,3,4]最终得到[(num=1,times=1), (num=4,times=1)],但4并不是众数。

所以我们需要得到这个链表之后,再遍历一遍数组,将重复次数记载于链表之中。

Python下使用map/reduce高阶函数来取代过程式下的循环,上述的算法也需要如下这么多的代码。

from functools import reduce

def find(a, m):

def find_index(arr, test):

for i in range(len(arr)):

if test(arr[i]):

return i

return -1

def check(r, n):

index = find_index(r, lambda x : x[0]==n)

if index >= 0:

r[index][1] += 1

return r

if len(r) < m-1:

return r+[[n,1]]

return reduce(lambda arr,x : arr if x[1]==1 else arr+[[x[0],x[1]-1]], r, [])

def count(r, n):

index = find_index(r, lambda x : x[0]==n)

if index < 0:

return r

r[index][1] += 1

return r

return reduce(lambda r,x : r+[x[0]] if x[1]>len(a)//m else r, \

reduce(count, a, \

list(map(lambda x : [x[0],0], reduce(check, a, [])))), [])

如果用C语言编写代码会更多一些,不过可以不用链表,改用固定长度的数组效率会高很多,times=0的情况代表着元素不被占用。此处就不实现了,交给有兴趣的读者自己来实现吧。

关于找一找教程网

本站文章仅代表作者观点,不代表本站立场,所有文章非营利性免费分享。

本站提供了软件编程、网站开发技术、服务器运维、人工智能等等IT技术文章,希望广大程序员努力学习,让我们用科技改变世界。

[众数的算法分析]http://www.zyiz.net/tech/detail-126392.html

java 众数算法_众数的算法分析相关推荐

  1. java python算法_用Python,Java和C ++示例解释的排序算法

    java python算法 什么是排序算法? (What is a Sorting Algorithm?) Sorting algorithms are a set of instructions t ...

  2. java 线性回归算法_线性搜索或顺序搜索算法在Java中如何工作? 示例教程

    java 线性回归算法 大家好,之前,我讨论了二进制搜索算法的工作原理,并分享了在Java中实现二进制搜索的代码. 在那篇文章中,有人问我是否还有其他搜索算法? 如果数组中的元素未排序,又该如何使用它 ...

  3. java python算法_用Java,Python和C ++示例解释的搜索算法

    java python算法 什么是搜索算法? (What is a Search Algorithm?) This kind of algorithm looks at the problem of ...

  4. java轮训算法_负载均衡算法WeightedRoundRobin(加权轮询)简介及算法实现

    Nginx的负载均衡默认算法是加权轮询算法,本文简单介绍算法的逻辑,并给出算法的Java实现版本. 算法简介 有三个节点{a, b, c},他们的权重分别是{a=5, b=1, c=1}.发送7次请求 ...

  5. java排队算法_贪心算法-排队问题-JAVA

    自己最近在学一些算法,试着把网上的贪心算法的例题编出来,JAVA版. [题目描述] 在一个医院B 超室,有n个人要做不同身体部位的B超,已知每个人需要处理的时间为ti,(0 输入数据:第1行一个正整数 ...

  6. java图像识别算法_图像算法原理与实践——绪论

    本系列文章是写给程序源的数字图像处理教程,从最基础的知识来讲解数字图像处理专业知识,通过最基本的编码方式来实践相应的处理算法,从而使得大家掌握基础的图像处理知识. 关于图像处理知识,在高校课程中,比较 ...

  7. java 概率 算法_使用概率算法优化快速排序(JAVA)

    前言 前面一篇文章系统介绍了快速排序算法,提到快速排序虽然平均时间复杂度为o(n*log2(n)),效率相对比较高.但是其在特殊情况下,比如降序的情况下,效率和冒泡排序一致,这就削弱了快速排序给人的好 ...

  8. java 电梯算法_编程之美之小飞的电梯调度算法(多种解法)---Java语言

    1.题目情景 我们假设都是从一楼上电梯的,而至于讯电梯停在其中的某一层.即所有的乘客都从一楼上电梯,到达某层之后,电梯停下来,所有乘客再从这里爬楼梯到自己的目的层.在一楼的时候,每个乘客选择自己的目的 ...

  9. java random算法_负载均衡--随机算法(Random)

    随机算法是指:从服务器列表中,随机选取一台服务器进行访问.由概率论可以得知,随着客户端调用服务端的次数增多,其实际效果趋近于平均分配请求到服务端的每一台服务器,也就是达到轮询的效果. 一.算法描述 假 ...

  10. java不规则算法_分布式id生成算法 snowflake 详解

    背景 在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识.如在支付流水号.订单号等,随者业务数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库的自增ID显然不能满足需 ...

最新文章

  1. EasyUI权限系统
  2. 学计算机的男孩子怎么追女孩子,男孩子追女孩子的套路,原来有这么多,快来学一学...
  3. 逆向映射是干嘛的anon_vma, vma, anon_vma_chain
  4. 【CRMEB知识付费系统v1.4.4】
  5. mybatis-generator-gui 如何exe化
  6. scrapy离线调试本地HTML文件
  7. [ 转载 ] Java基础10--关于Object类下所有方法的简单解析
  8. js获取用户实时地理位置
  9. 阿里云上运行Docker版本的PostgreSQL
  10. php 获取TZ时间格式
  11. 怎么是phpadmin连接mysql_phpmyadmin连接远程mysql
  12. Ubuntu 14.04 设置静态IP地址全攻略
  13. e4a浏览框本地HTML,E4A浏览器 · E4A易安卓中文开发教程-E4A社区 · 看云
  14. 移动端是如何做适配的?
  15. https://www.npmjs.com/一个下载库的网址
  16. 苹果11蓝牙配对不成功怎么办_苹果手机蓝牙怎么连不上的解决步骤
  17. 访傲林科技董事长刘震:用企业级数字孪生掘金工业互联网
  18. ubuntu 部署ATX
  19. matlab绘制世界地图(含国界)、中国地图(含省界),可下载m_map和shp文件
  20. RPA场景案例〡赋能“人力资源”,工作效率一键提升数倍

热门文章

  1. 开心--开始--开发--开心
  2. 软件设计和开发规范(国标)
  3. 蔡勒(Zeller)公式求星期几模板
  4. AMiner会议论文推荐第三十二期
  5. 线性回归分析步骤总结
  6. 直播预告|年营业额百亿的企业都在如何做数字化转型
  7. 如何建立线上问题快速响应机制
  8. Win10应用设计的那些事儿
  9. 读书笔记之稻盛和夫自传
  10. 花生壳实现内网穿透教程