分治算法地思想就是将复杂问题分解为简单的子问题,然后寻求子问题的地归结,并组合各个子问题的解一起得到最终复杂问题的解。
针对求解列表中第k小的数,暴力拆解法可以将列表排序然后根据索引求出列表中第k小的数,显然这种算法的时间复杂度较大,下面介绍一下分治算法求列表中第k小的数,时间复杂度为O(n).
Blum对于这个问题的解法就是通过找到一个被称为支点的数来对输入序列划分。支点左边的数都比支点数小,支点右边的数都比它大。如果k等于支点数的索引,这是可以立刻返回结果;如果k小于支点数索引,说明第k小的数在支点数左边,需要递归调用函数;如果k大于支点数索引,说明第k小的数在支点数右边,需要递归调用函数。
代码如下:
#寻找第K小的数 def select_fct(array,k): if len(array)<=10:#边界条件 array=sorted(array) return array[k] pivot=get_pivot(array) #得到支点数 array_lt,array_gt,array_eq=patition_array(array,pivot)#按照支点数划分 if k<len(array_lt):#所求数在支点数的左边 return select_fct(array_lt,k)#递归执行,排序为k elif k<len(array_lt)+len(array_eq):#所求数为支点数 return array_eq[0] else:#所求数在支点数的右边 normalized_k=k-(len(array_lt)+len(array_eq))#此时排序发生变化,不是k,而需要经过变化 return select_fct(array_gt,normalized_k) #得到支点数 def get_pivot(array): subset_size = 5#每一组有5个元素 subsets = []#用于记录各组元素 num_medians = len(array) / subset_size if (len(array) % subset_size) > 0: num_medians += 1 #不能被5整除 for i in range(num_medians):#划分为若干个组,每组5个元素 beg = i * subset_size end = min(len(array), beg + subset_size) subset = array[beg:end] subsets.append(subset)#subsets列表格式如下[[],[],[]......] medians = [] for subset in subsets: median = select_fct(subset, len(subset) // 2) medians.append(median)#格式为[a,b,c],构成中间数列表 pivot = select_fct(medians, len(subset) // 2)#再从中间数列表中找出中间数 return pivot # 按照支点数划分数组 def patition_array(array, pivot): array_lt = [] array_gt = [] array_eq = [] for item in array: if item < pivot: array_lt.append(item)#小于中间数的放左边 elif item > pivot: array_gt.append(item)#大于中间数的放右边 else: array_eq.append(item)#等于中间数的储存一个列表 return array_lt, array_gt, array_eq # 主函数 import random if __name__ == "__main__": num = 100 array = [random.randint(1, 1000) for i in range(num)] random.shuffle(array)#打散数据 random.shuffle(array)#再次打散数据 k = 7 kval = select_fct(array, k)#调用函数 print(kval) sorted_array = sorted(array) assert sorted_array[k] == kval#进行验证