这类问题是最高频K项的基础问题。问题描述如下:
在一个整数数组中,找最大的k个整数
离线算法
这离线处理中,可以使用Quick Select算法。在O(N)
的时间内找到数组的第K大的数,然后扫描一次整个数组就可以得到前K大的数。该算法基于快排思想。
Quick Select
Quick Select
算法基本思想:
- 利用快排的分治思想,求得待搜索数组的主元pivot,以主元为界分成左右两个区间
- 通过比较主元的位置,判断第K个大小的数在主元左区间还是主元右区间?还是就是主元?(还要注意边界条件的判断,有可能在边界)
- 进入子区间递归调用
import java.util.Random;
class Solution {
/*
* @param nums an integer array
* @param k an integer
* @return the top k largest numbers in array
*/
public int[] topk(int[] nums, int k) {
// Write your code here
quickSort(nums, 0, nums.length - 1, k);
int[] topk = new int[k];
for (int i = 0; i < k; ++i)
topk[i] = nums[i];
return topk;
}
private void quickSort(int[] A, int start, int end, int k) {
if (start >= k)
return;
if (start >= end) {
return;
}
int left = start, right = end;
// key point 1: pivot is the value, not the index
//创建一个随机数生成器
Random rand =new Random(end - start + 1);
//nextInt(int n)方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n
int index = rand.nextInt(end - start + 1) + start;
int pivot = A[index];
// key point 2: every time you compare left & right, it should be
// left <= right not left < right
while (left <= right) {
// key point 3: A[left] < pivot not A[left] <= pivot
while (left <= right && A[left] > pivot) {
left++;
}
// key point 3: A[right] > pivot not A[right] >= pivot
while (left <= right && A[right] < pivot) {
right--;
}
if (left <= right) {
int temp = A[left];
A[left] = A[right];
A[right] = temp;
left++;
right--;
}
}
quickSort(A, start, right, k);
quickSort(A, left, end, k);
}
}
在线算法
对于数据流的形式,没有机会从头遍历。解决办法是考虑堆。这个问题中我们不需要支持删除操作,数据只增不减,那么那些已经排到第K名之后的数据,是再也没有机会进入前k名的,因此保留这些数据的意义不大,我们可以删掉它们。这样一来,我们需要维护一个最小堆。当我们需要决定一个整数是否能加入TopK的集合的时候,决定性的因素是:当前整数是否比Top K中的最小的数要大。因此在TopK的k个整数中,我们应该维护一个最小堆,推举出最小的整数去和数据流中新出来的整数作比较,如果新来的数更大,就踢掉堆里最小的整数,把新数加进去,反之则扔掉新数。
class Solution {
public int findKthLargest(int[] nums, int k) {
if (k <= 0 || nums.length == 0 || nums == null) {
return 0;
}
PriorityQueue minHeap = new PriorityQueue();
for (int i = 0; i < nums.length; i++) {
minHeap.add(nums[i]);
if (minHeap.size() > k) {
minHeap.poll();
}
}
return minHeap.poll();
}
}
快速排序实现
# quicksort
class Solution:
def quicksort(self, A, start, end):
if A is None or len(A) == 0:
return None
if start >= end:
return None
left = start
right = end
# pivot is value not index
pivot = start + (end - start) // 2
# 确保是left <= right,防止stackoverflow
while left <= right:
while left <= right and A[left] < pivot:
left += 1
while left <= right and A[right] > pivot:
right -= 1
if left <= right:
A[left], A[right] = A[right], A[left]
left += 1
right -= 1
self.quicksort(A, start, right)
self.quicksort(A, left, end)
归并排序实现
class Solution:
def sortIntegers(self, A):
temp = [0 for _ in range len(A)]
self.merge_sert(0, len(A) - 1, A, temp)
def merge_sort(self, start, end, A, temp):
if start >= end:
return
mid = start + (end - start) // 2
self.merge_sort(start, mid, A, temp)
self.merge_sort(mid+1, end, A, temp)
self.merge(start, mid, end, A, temp)
def merge(self, start, mid, A, end, temp):
left = start
right = mid + 1
index = start
while left <= mid and right <= end:
if A[left] < A[right]:
temp[index] = A[left]
left += 1
else:
temp[index] = A[right]
right += 1
index += 1
while left <= mid:
temp[index] = A[left]
left += 1
while right <= end:
temp[index] = A[right]
right += 1
for index in range(start, end+1):
A[index] = temp[index]