采用最普通分治算法:O(N^2)
Sub:使用一个支点将数组分为两部分;Relate:该支点所在数组的位置,若为k则返回,若大于k则将前半数组作为新的问题,反之将后半数组作为新的问题。Time:对于最好的情况,直接找到k;然而最坏的情况,每次支点都找到当前数组的第一个数,而k为最后一个数,这样的时间复杂度为O(N^2)
使用最普通的分治算法甚至不如直接对数组进行排序再寻找第k大的数
为了方便讲解,设N%5==0,若不为0可以通过在数组尾部补齐解决。
采用改进过的分治算法:O(n)
其最重要的改进就在于支点的选择,使其最差情况任为O(N)
Sub:将数组根据支点分为三个新的数组(小于:array_lt、等于:array_eq、大于支点:array_gt),根据k的大小选择数组作为子问题。
Relate:1.将数组拆分为n/5组每组5个,在每组5个中选出中值并以之分为两半(每组时间复杂度为c,有N/5组所以总时间复杂度为O(N)。2.将n/5个组的中值视为新数组,然后将其视为子问题1——在n/5个数中找出中间的数(T(2N/10));3.以第2步找出的中值的中值为支点,将数组分为小于、等于、大于支点的三部分O(N);4.分析知至少有3N/10个数小于等于支点数,同理至少有3N/10个数大于等于支点数
(见下图:先将每一组中小于中位数的数放于上部,大于的放于下部,再将中位数小于中位数的中位数的组放于中位数的中位数的左边,反之放到右边。左上角小于支点,右下角大于支点)
取k所位于的部分作为新的数组,若k属于array_eq则直接返回;若属于array_lt则在其中找第k小的数;属于array_gt则在其中寻找第k-len(array_lt)-len(array_eq)小的数,作为子问题2。又由之前的分析知任何一个子问题的规模<=7N/10。
Time:最坏情况下,T(n)=T(7n/10)+T(2n/10)+cn,通过画递归树再加以数归可得:T(n)=cn*(1+Σ(9/10)^i),所以时间复杂度为O(n)。
代码如下:
from array import array
import numpy
A=[53,23,86,31,21,28,47,52,51,79,80,103,152,2,7,14,17,83,13,61,567,32,74,78,57]#,62,82,54,9,11
print('长度为:',len(A))
def my_sep(array,pivot):
array_lt=[];array_gt=[];array_eq=[]
array=array.flatten()
for i in array:
if i<pivot:
array_lt.append(i)
elif i>pivot:
array_gt.append(i)
else:
array_eq.append(i)
return array_lt,array_gt,array_eq
def my_f(a,k):
N=len(a)
#常数时间
if N<=5:
a.sort()
return a[k]
#不足5的倍数,在最后补上最大数,时间复杂度为O(N)
if N%5!=0:
ma_x=a[0]
for i in range(N):
if ma_x<a[i]:
ma_x=a[i]
for i in range(5-N%5):
a.append(ma_x)
N+=5-N%5
#将数组分为5份,寻找支点
b=numpy.array(a).reshape(N//5,5)
c=[]
for i in range(N//5):
b[i].sort() #时间复杂度为O(C),所以整个时间复杂度为O(C*N//5)=O(C*N)=O(N)
c.append(b[i][2])
_mid=my_f(c,(len(c)-1)//2)#找出中值的中值:T(N/5)
print('支点为:',_mid)
#此部分可以不用,只是为了展示数组的形状,如要展示建议将第22行改为N<=10
'''
p=0;q=N//5-1
n=N//5
while p<q:
if b[p][2]>=_mid and b[q][2]<=_mid:
b[[p,q],:]=b[[q,p],:]
else:
if b[p][2]<_mid:p+=1
if b[q][2]>_mid:q-=1
print(b)
'''
#将小于支点的数放到_lt,大于的放_gt,相等的放_eq。时间复杂度为O(N)
array_lt,array_gt,array_eq=my_sep(b,_mid)
if k<len(array_lt):
return my_f(array_lt,k)
elif k<len(array_lt)+len(array_eq):
return array_eq[0]
else:
return my_f(array_gt,k-len(array_eq)-len(array_lt))
print(my_f(A,9))
A.sort()
print(A[9])