求有N个元素的数组中前k个最大的数?(N>=k)
方法一:排序法
可以先将数组排序,然后再截取前k个最大的数,利用归并排序或者快速排序等排序方式,该方法平均时间复杂度为O(N*logN)
方法二:部分排序法
由于只需要找出前k大的数,因此没必要对数组中所有的元素排序,可以采用部分排序的方式。具体思路为:第一次先遍历数组找到最大的数,第二次遍历从剩下的数组中找到最大的数(在整个数组中第二大的数)…共需遍历k次,这种方法的时间复杂度为O(N*k)
方法三:综合法
该方法思路是:
(1)维护一个大小为k的小顶堆(降序排列,堆顶元素最小),用来存储前k个最大的数,堆顶保存了堆中最小的数;
(2)每次遍历一个数m,如果m比堆顶的数大,则将堆顶的数替换为m;
(3)重新调整堆为小顶堆;
(4)重复(2)、(3)步骤,直至遍历完数组中所有的数;
(5)最终,小顶堆即为原数组中前k个最大的数。
Python代码实现思路:
(1)取数组中前k个数,利用快速排序,将其调整大小为k的小顶堆(这里在python中用列表简单实现),时间复杂度O(k*logk);
(2)从数组第k+1个数开始,往后遍历,如果遍历到的数比堆顶元素大,则替换掉堆顶的数为此数,然后利用二分法重新调整小顶堆(即将此数插入到大小为k-1的已降序排列的堆中),每次调整的时间复杂度O(logk),共需调整N-k次,故调整操作的时间复杂度O((N-k)*logk);
(3)遍历完数组中所有的数后,最终的堆即为前k个最大的数。完成上述操作的总体时间复杂度为:
O(k*logk) + O((N-k) *logk) = O(N *logk)
代码如下:
# 快速排序算法
def quickSort(arr, ascending=True):
"""
利用双指针实现快排,不需要额外的存储空间,只是在原数组中修改元素顺序
"""
def partition(arr, first, last):
base_num = arr[first]
leftmark = first + 1
rightmark = last
done = False
while not done:
if ascending:
while leftmark <= rightmark and arr[leftmark] <= base_num:
leftmark += 1
while leftmark <= rightmark and arr[rightmark] >= base_num:
rightmark -= 1
else:
while leftmark <= rightmark and arr[leftmark] >= base_num:
leftmark += 1
while leftmark <= rightmark and arr[rightmark] <= base_num:
rightmark -= 1
if rightmark < leftmark:
done = True
else:
arr[leftmark], arr[rightmark] = arr[rightmark], arr[leftmark]
arr[first], arr[rightmark] = arr[rightmark], arr[first]
return rightmark
def quickSortHelper(arr, first, last):
if first < last:
splitpoint = partition(arr, first, last)
quickSortHelper(arr, first, splitpoint - 1)
quickSortHelper(arr, splitpoint + 1, last)
return arr
return quickSortHelper(arr, 0, len(arr) - 1)
# 重新调整堆为小顶堆
def insert(arr):
if len(arr) == 1:
return arr
if len(arr) == 2:
if arr[0] > arr[1]:
return arr
else:
return [arr[1], arr[0]]
low = 0
high = len(arr) - 2
mid = (low + high) // 2
if arr[-1] == arr[mid]:
return arr[:mid + 1] + [arr[-1]] + arr[mid + 1:-1]
elif arr[-1] > arr[mid]:
return insert(arr[:mid + 1] + [arr[-1]]) + arr[mid + 1:-1]
else:
return arr[:mid + 1] + insert(arr[mid + 1:])
# 遍历数组中的元素
def search_k(arr, k):
alist = quickSort(arr[0:k], ascending=False)
for i in range(k, len(arr)):
if alist[-1] < arr[i]:
alist[-1] = arr[i]
alist = insert(alist)
return alist
alist = [4, 7, 1, 2, 3, 5, 3, 6, 3, 2]
k = 3
print(search_k(alist,k))
最终输出结果如下:
[7, 6, 5]
参考书籍:
《python程序员面试算法宝典》 猿媛之家/组编