O(NlogN)复杂度选取出现次数超过一半的元素(递归版本)
来源:互联网 发布:淘宝联盟自己买自己的 编辑:程序博客网 时间:2024/04/30 04:10
这个问题以前在书上碰到过,书上采取的办法是从相邻的两个数开始挑选,保证该元素一定会进入下一次的候选集合,且仍然满足出现次数超过一半,具体实现采用的C++.这一次我重新想了一个办法,利用分治算法递归的去实现:
主要思路是获取两个子区间内的主元(出现次数超过一半的元素)。如果两个主元一致则就是该区间的主元。否则出现次数较大的那一个主元有可能是主元。那么就要遍历另一半区间计算可能的主元出现的总次数。最后判断是否是真正主元。
若两个主元不同且出现次数一样多,就要判断是不是区间长度是不是奇数,如果是主元只可能是在稍多一个数的区间里面,则也需要遍历来判断.
O(k)=2O(k2)+k2
容易计算的O(k)是klogk。
下面进行证明,两个候选主元一致则无需判断,主要说明两个主元不一致的情况。假设左右子区间主元分别是A,B ,在左右子区间出现的次数分别是A0,B1和A1,B0 ,总的出现次数是A0+A1,B0+B1, 其中A0和B0 是已经获取的数据,A1,B1 是在另外一个区间出现的次数(未知).
[1]假设A0>B0,则B0不可能是主元(A0也不一定是),采取反证法,假设B0是主元 ,那么有:A0+B1>B0+B1>n2=>A0+B1>=n2+2 ,A0+B1是左子区间A,B的出现次数,不可能达到n2+2。所以原命题获证。
[2]假设A0=B0 ,采取同[1]的思路,可以得到A0+B1>=n2+1(候选主元是B) ,A1+B0>=n2+1(候选主元是A) ,当n=2k ,左右子区间的长度均为k,所以不可能达到k+1,
当n=2k+1.两个子区间是k,k+1 ,所以较长的那一个区间可能会含有真正主元的另一部分,那么显然候选主元来自较短的那一个区间!因此需要对较长区间进行遍历判断候选主元是否是真正主元。
以下代码可以获取到主元是否出现和出现的次数。相比较以前的算法可能速度上慢一些但是能获取更多的信息。
def Find_main_elem(array,beg,end):#recursion version def find_most(beg,end,obj): num = 0 for each in array[beg:end+1]: if each == obj:num+=1 return num length = end - beg + 1 if (beg == end):return array[beg],1 if not length%2:mid = (beg+end)//2+1#偶数 else:mid = (beg+end)//2 first_elem,num1 = Find_main_elem(array,beg,mid-1) second_elem,num2 = Find_main_elem(array,mid,end) if(num1 or num2): if first_elem == second_elem:return first_elem,num1+num2 elif num2 > num1 : obj = second_elem;num = num2 + find_most(beg,mid-1,obj) elif num2 < num1 : obj = first_elem;num = num1 + find_most(mid,end,obj) elif length%2 : obj = first_elem;num = num1 + find_most(mid,end,obj) # num1 = num2,奇数,那么只可能是first else: return (None,0) return (obj,num) if (num > length/2) else (None,0) return (None,0)def Find(array): x,num = Find_main_elem(array,0,len(array)-1) if num!=0:return(x,num) else:return 'no'
进行检验:如果不需要考虑内存的情况,那么最快的办法应该是下面的思路:
def Fast_Find(array): cache = {} max,num = None,0 for each in array: if each in cache:cache[each]+=1 else:cache[each] = 1 if cache[each] > num:num = cache[each];max = each if num > len(array)/2:return(max,num) return 'no'
利用上面的函数我们可以检验第一种算法的正确性:
import randomdef Test(): for case in range(150): length = 50 rnd_array = [random.randint(0,1) for x in range(length)] if Fast_Find(rnd_array)!=Find(rnd_array): print(rnd_array);breakTest()
PS:我在进行检验的时候发现了一个小错误,这个错误花费了我半个小时才搞明白原因:在python里面0==False的结果是True,所以如果主元是0可能就会进入某一个处理没有主元的分支造成逻辑错误。因此我将没有主元的情况返回None而不是False。
0 0
- O(NlogN)复杂度选取出现次数超过一半的元素(递归版本)
- 在时间复杂度为 O(n) 内找出数组中出现次数超过一半的数
- 找出数组中出现次数超过一半的数(时间复杂度O(n))
- 已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数。
- 已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数
- 已知一个数出现的次数超过了一半,请用O(n)的复杂度的算法找出这个数
- 如何在O(n)的时间复杂度内找出数组中出现次数超过一半的数
- 在O(n)的时间复杂度内找出数组中出现次数超过了一半的数
- 如何在O(N)的时间复杂度内找出数组中出现次数超过了一半的数
- 如何在O(n)的时间复杂度内找出数组中出现次数超过了一半的数
- 微软笔试题 求出现次数超过一半的元素
- 求数组中出现次数超过一半的元素
- 找出一个数组中出现次数超过一半的元素
- 查找数组中出现次数超过一半的元素
- 求数组中出现次数超过一半的元素
- 找出出现次数超过数组一半元素的数
- 寻找数组中出现次数超过一半的元素
- 获取数组中出现次数超过一半的元素
- iOS应用支持IPV6,就那点事儿
- 使控件和MFC窗口一起最大最小化
- 听韦东山老师公开课的理解
- web.xml详解
- Flume的体系结构介绍以及Flume入门案例(往HDFS上传数据)
- O(NlogN)复杂度选取出现次数超过一半的元素(递归版本)
- 华为OJ——删除字符串中出现次数最少的字符
- 268. Missing Number 找出缺失的数
- Vitamio视频播放器(一)
- css切图
- Android项目中引用本地aar文件的方法
- iOS 第三方框架 - ReactiveCocoa学习1
- 第十五周项目 补充阅读程序(4)
- memcached 常见面试题