题目:
所谓“第(前)k大数问题”指的是在长度为n(n>=k)的乱序数组中S找出从大到小顺序的第(前)k个数的问题。
解法1:堆排序
采用元素下沉法,维护一个k大小的最小堆,对于数组中的每一个元素判断与堆顶的大小,若堆顶较大,则不管,否则,弹出堆顶,将当前值插入到堆中,继续调整最小堆。时间复杂度O(n * logk)
注意:heap和array的关系;Find_heap_kth函数里面range的索引范围;
def heap_build(parent,heap):
child = 2*parent+1
while child<len(heap):
if child+1<len(heap) and heap[child+1]<heap[child]:
child = child+1
if heap[parent]<= heap[child]:
break
heap[parent],heap[child] = heap[child],heap[parent]
parent,child = child,2*child+1
return heap
def Find_heap_kth(array,k):
if k > len(array):
return None
heap = array[:k]
for i in range(k,-1,-1):
heap_build(i,heap)
for j in range(k,len(array)):
if array[j]>heap[0]:
heap[0] = array[j]
heap_build(0,heap)
return heap[0]
print(Find_heap_kth([2,1,4,3,5,9,8,0,1,3,2,5],6))
解法2:插入排序
由于是要找 k 个最大的数,所以没有必要对所有数进行完整的排序。每次只保留 k 个当前最大的数就可以,然后每次对新来的元素跟当前 k 个树中最小的数比较,新元素大的话则插入到数组中,否则跳过。循环结束后数组中最小的数即是我们要找到第 k 大的数。 时间复杂度 (n-k)logk
注意:嵌套for循环里面,比较的对象;以及range的范围
def Find_Kth_max(array,k):
for i in range(1,k):
for j in range(i,0,-1):
if array[j] > array[j-1]:
array[j],array[j-1] = array[j-1],array[j]
else:
pass
for i in range(k,len(array)):
if array[i] > array[k-1]:
array[k-1] = array[i]
for j in range(k-1,0,-1):
if array[j] > array[j-1]:
array[j],array[j-1] = array[j-1],array[j]
else:
pass
return array[k-1]
print(Find_Kth_max([2,1,4,3,5,9,8,0,1,3,2,5],3))
解法3: 利用快速排序的思想
从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:
1. Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
2. Sa中元素的个数大于等于k,则返回Sa中的第k大数。时间复杂度近似为O(n)
def partition(num, low, high):
pivot = num[low]
while (low < high):
while (low < high and num[high] > pivot):
high -= 1
while (low < high and num[low] < pivot):
low += 1
num[low],num[high] = num[high],num[low]
num[high] = pivot
return high,num
def findkth(num,low,high,k): #找到数组里第k个数
index=(partition(num,low,high))[0]
print((partition(num,low,high))[1])
if index==k:return num[index]
if index<k:
return findkth(num,index+1,high,k)
else:
return findkth(num,low,index-1,k)
print(findkth([6,1,3,9,2],0,len([6,1,3,9,2])-1,4))
总觉得这个快排实现的有点问题。。
解法四:利用选择排序的思路
选择排序:
在N个元素中进行第一轮遍历:获取最大的元素,将最大的元素和最后一位元素互换;
在N-1个元素中进行第二轮遍历:获取第二大的元素,将第二大的元素和倒数第二位元素互换;
......
遍历每一轮当前无序数组中的最大值,那我们只需要找K轮,就能找到第K大元素了
实现方法是:
def Select_MaxVal_Pos(arr,n):
'''
当前长度的数组中,遍历N个元素,获取最大的元素值,及元素所在位置
:param arr:数组
:param n: 遍历元素个数
:return:
'''
MaxVal = arr[0]
Pos = 0
for i in range(1,n):
if arr[i]>MaxVal:
MaxVal = arr[i]
Pos = i
return MaxVal,Pos
def Find_TopK(arr,k):
n = len(arr)
for i in range(n):
MaxVal,Pos = Select_MaxVal_Pos(arr,n-i)
arr[n-i-1],arr[Pos] = arr[Pos],arr[n-i-1] #将当前轮次找出的最大的元素和出去右边已经排好序的元素最近的位置互换
if i+1 == k: #因为i是从0开始的,所以第i+1轮找出的是第i大的元素
print(MaxVal,Pos)
Find_TopK([4,2,5,7,1,9,0,20,45,100],3)
# 20 7
附加
1 求中位数实际上是第k大数的特例。
2 如果需要找出N个数中最大的K个不同的浮点数呢?比如,含有10个浮点数的数组(1.5,1.5,2.5,3.5,3.5,5,0,- 1.5,3.5)中最大的3个不同的浮点数是(5,3.5,2.5)。解答:上面的解法均适用
3 如果是找第k到第m(0<k<=m<=n)大的数呢?解答:如果把问题看做m-k+1个第k大问题,则前面解法均适用。