[LeetCode]第k大数
最近自己在电话面试中,经常被问到,“找出数组中的第k个最大的数”或者“或者找出数组中的前k个最大的数”,都是找出特定的数。首先,可以简单的用快排然后,第k位就是的,但是这样时间复杂度会很高。
一、快速选择算法
这个名字是y总起的,原理很简单。快排是找一个标志位,小于它的放左边,大于它的放右边,这样是不是确定了标志位是多少大的数。比如[2, 4, 3, 5, 1],第一轮取3做标志位,排序后变成[2, 1, 3, 5, 4],这是不是确定了3这个元素,在数组中是第3大数。这样的话,对快排进行改进,从而不需要排序整个数组。
原始快排
public void quickSort(int[] nums, int l, int r) {
int i = l - 1, j = r + 1, x = nums[(l + r) >> 1];
while (i < j) {
do {
i ++;
} while (nums[i] < x);
do {
j --;
} while (nums[j] > x);
if (i < j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
quickSort(nums, l, j), quickSort(nums, j + 1, r);
}
快速选择算法
//引入了一个k,这个k值得是排序后的数组(从小到大),第k位
//题目中的第k大,要经过转换,quickSort(nums, 0, nums.length, nums.length - k + 1)
public int quickSort(int[] nums, int l, int r, int k) {
if(l >= r) {
return nums[l];
}
int i = l - 1, j = r + 1, x = nums[(l + r) >> 1];
while (i < j) {
do {
i ++;
} while (nums[i] < x);
do {
j --;
} while (nums[j] > x);
if (i < j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
int leftSize = j - l + 1;
if (k <= leftSize) {
return quickSort(nums, l, j, k);
} else {
return quickSort(nums, j + 1, r, k - leftSize)
}
}
二、堆排序
我们数据结构中使用的堆,构造一个小顶堆,具有一定的容积限制为k。当容积到大k时,则将新的数和堆顶做比较,决定是否替换。思路比较简单,代码也很简单。
public int getAns(int nums, int k) {
PriorityQueue<Integer> heap = new PriorityQueue();
for (int num : nums) {
if (heap.size() == k) {
if (heap.peek() < num) {
heap.poll();
heap.offer(num);
}
} else {
heap.offer(num);
}
}
return heap.peek();
}