题目描述
给定含有N个元素的多重集合S,每个元素在S中出现的次数称为该元素的重数,多重集合S中重数最大的元素称为多重集合S的众数,众数的重数称为多重集合S的重数,试求一个给定多重结合的重数和众数;
例如:S={1,1,2,3,3,3,3,4,4,5}的重数是4,众数是3
基本思想
1.如果数组无序,则需要先排序;
2.选择数组中间位置的元素作为中位数mid,令l=0,对数组从左向右查找第一个等于Mid的元素,然后令l等于该元素的索引;令r=len(arr-1),对数组从右向左查找第一个等于Mid的元素,然后令r等于该元素的索引;
3.现在可以得到arr[l,r]的元素都等于mid。假设数组长度为n,我们将arr分为了3部分,arr[0,l-1],arr[l,r],arr[r+1,n-1],如果左边数组的长度(即l)都要小于r-l,则没有必要搜索左边数组;同理,如果右边数组的长度(即n-r-1)都要小于r-l,则没有搜索右边数组的必要。
图片演示
第一步,确认中间数组的长度
如图所示,有这样一个有序数组arr,我们最中间的数arr[4]作为中位数mid,然后我们将i从左向右移动,直到找到第一个等于mid的数则停止。
i=0,arr[i]!=mid,i++
i=1,arr[i]!=mid,i++
i=2,arr[i]==mid
然后我们将j从右向左移动,直到找到第一个等于mid的数则停止。
j=8,arr[j]!=mid,j–
j=7,arr[j]!=mid,j–
j=6,arr[j]!=mid,j–
j=5,arr[j]!=mid,j–
j=4,arr[j]==mid
可以看到,这时我们已经将数组分成3个部分:
①arr[0,l-1]
②arr[l,r],而且该部分的所有元素都等于mid
③arr[r+1,8]
注解:
虽然我们可以一眼看出来3就是众数,但这是因为众数刚好就在数组的最中间位置,这是一种很巧合的情况,一般的情况有可能像下面这样:
在这种一般的情况下,由于我们选择最中间的元素为mid(即arr[5]),但是4并不是众数,真正的众数应该是位于左边数组的2.
所以,我想讲的意思是,当我们求出中间数组的长度后,还需要判断左右两边的数组,是否不存在比中间数组更多的众数了,如果没有,那么我们可以放心将中间数组的数作为中位数。
我们接着回到上面那个9个元素的数组来讲解。
可以看到,中间数组的长度为3,而左边数组的长度为2,那么,就算左边数组全是同一个数,众数也不可能出现在里面了,因此我们没有检测左边数组的必要了。
相反,右边数组长度为4,如果右边数组都是同一个数,那么众数就应该在右边数组。因此我们有必要检测一下右边数组。
至于怎么检测右边数组,方法就和我们上面所说的是一样的。我们将右边数组看成新的数组,然后判断其中间数组,左右数组的长度······因此我们可以采用分治的方法来解决。
代码展示
# arr有序
def split(arr,n,l,r):
# 用于在循环中l和r的计数
lCount = l - 1
# 确定中位数
mid = int(n/2)
for l in range(0,n):
# 从左到右找到索引最小且值等于arr[mid]的元素
lCount = lCount + 1
if arr[l] == arr[mid]:
break
# 从arr[mid]后一个元素到右找到索引最大且值等于arr[mid]的元素
rCount = lCount
for r in range(lCount+1,n):
rCount = rCount + 1
if arr[r] != arr[mid]:
break
l = lCount
r = rCount
return l , r
# arr[l]到arr[r]都是相等的元素
分治代码如下:
# num表示众数 maxCnt表示重数
# min表示中位数 arr是集合 n是当前集合长度
def getMaxCnt(mid,maxCnt,arr,n):
l = 0
r = 0
l,r = split(arr,n,l,r) #将数组进行切割成两端 从arr[l]到arr[r]都是相等的元素
num = int(n/2)
# 当前“众数”的重数
cnt = r - l
# 如果这个数的重数更大,则更新maxCnt
if(cnt > maxCnt):
maxCnt = cnt
mid = arr[num]
#l表示左边的个数,左边的个数必须大于中位数的个数,才有进行搜索的意义
if(l+1 > maxCnt):
getMaxCnt(mid, maxCnt, arr, l+1);
#同理,右边的个数将要大于中位数的个数才有继续搜寻的意义,同时右边数组的起始位置进行改变
if(n-r > maxCnt):
getMaxCnt(mid, maxCnt, arr[r:], n-r)
return mid,maxCnt
测试结果
# 输入集合
mySet = [4,4,2,9,10,10,10,8,8,8,8,45,23]
mySet = sorted(mySet)
n = len(mySet)
maxCnt = 0
num = 0
num,maxCnt = getMaxCnt(num ,maxCnt, mySet, n)
print('众数为',num,'重数为',maxCnt)