题目描述

给定含有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,则没有搜索右边数组的必要。

图片演示

第一步,确认中间数组的长度

python 用众数填充数值型类 python众数问题_数据结构


如图所示,有这样一个有序数组arr,我们最中间的数arr[4]作为中位数mid,然后我们将i从左向右移动,直到找到第一个等于mid的数则停止。

i=0,arr[i]!=mid,i++
 i=1,arr[i]!=mid,i++
 i=2,arr[i]==mid

python 用众数填充数值型类 python众数问题_python_02


然后我们将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

python 用众数填充数值型类 python众数问题_数据结构_03


可以看到,这时我们已经将数组分成3个部分:

①arr[0,l-1]

②arr[l,r],而且该部分的所有元素都等于mid

③arr[r+1,8]


注解:

虽然我们可以一眼看出来3就是众数,但这是因为众数刚好就在数组的最中间位置,这是一种很巧合的情况,一般的情况有可能像下面这样:

python 用众数填充数值型类 python众数问题_python 用众数填充数值型类_04


在这种一般的情况下,由于我们选择最中间的元素为mid(即arr[5]),但是4并不是众数,真正的众数应该是位于左边数组的2.

所以,我想讲的意思是,当我们求出中间数组的长度后,还需要判断左右两边的数组,是否不存在比中间数组更多的众数了,如果没有,那么我们可以放心将中间数组的数作为中位数。


我们接着回到上面那个9个元素的数组来讲解。

python 用众数填充数值型类 python众数问题_python_05


可以看到,中间数组的长度为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)

python 用众数填充数值型类 python众数问题_python_06