寻找第K大

题目

有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。

给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在。

要求:时间复杂度 O(nlogn),空间复杂度 O(1)

数据范围:10000≤n≤1000, n1≤K≤n,数组中每个元素满足 100000000≤val≤10000000
示例1

1
2
输入:[1,3,5,2,2],5,3
返回值:2

题解

解题关键

  • 利用快速排序的思想寻找数组中的第k大元素
  • 有重复数字,不用去重,也不用管稳定性与否

思路

方法:快排+二分查找

知识点:分治

分治即“分而治之”,“分”指的是将一个大而复杂的问题划分成多个性质相同但是规模更小的子问题,子问题继续按照这样划分,直到问题可以被轻易解决;“治”指的是将子问题单独进行处理。经过分治后的子问题,需要将解进行合并才能得到原问题的解,因此整个分治过程经常用递归来实现。

思路:

本题需要使用快速排序的思想,快速排序:每次移动,可以找到一个标杆元素,然后将大于它的移到左边,小于它的移到右边,由此整个数组划分成为两部分,然后分别对左边和右边使用同样的方法进行排序,不断划分左右子段,直到整个数组有序。这也是分治的思想,将数组分化成为子段,分而治之。

放到这道题中,如果标杆元素左边刚好有k−1个比它大的,那么该元素就是第k大:

1
2
3
4
//若从0开始,刚好是第K个点,则找到
if(K == j + 1)
return a[p];

如果它左边的元素比k−1少,说明第k大在其右边,直接二分法进入右边,不用管标杆元素左边:

1
2
if(j + 1 < k)
return partition(a, j + 1, high, k);

同理如果它右边的元素比k−1少,那第k大在其左边,右边不用管。

1
return partition(a, low, j - 1, k);

具体做法:

step 1:进行一次快排,大元素在左,小元素在右,得到的标杆j点.在此之前要使用随机数获取标杆元素,防止数据分布导致每次划分不能均衡。
step 2:如果 j + 1 = k ,那么j点就是第K大。
step 3:如果 j + 1 > k,则第k大的元素在左半段,更新high = j - 1,执行step 1。
step 4:如果 j + 1 < k,则第k大的元素在右半段,更新low = j + 1, 再执行step 1.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.util.*;
public class Solution {
//交换函数
Random r = new Random();
public static void swap(int arr[], int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
public int partition(int[] a, int low, int high, int k){
//随机快排划分
int x = Math.abs(r.nextInt()) % (high - low + 1) + low;
swap(a, low, x);
int v = a[low];
int i = low + 1;
int j = high;
while(true){
//小于标杆的在右
while(j >= low + 1 && a[j] < v)
j--;
//大于标杆的在左
while(i <= high && a[i] > v)
i++;
if(i > j)
break;
swap(a, i, j);
i++;
j--;
}
swap(a, low, j);
//从0开始,所以为第j+1大
if(j + 1 == k)
return a[j];
//继续划分
else if(j + 1 < k)
return partition(a, j + 1, high, k);
else
return partition(a, low, j - 1, k);
}
public int findKth(int[] a, int n, int K) {
return partition(a, 0, n - 1, K);
}
}

寻找第K大
http://lhystutest.top/2022/10/20/算法/堆/栈/寻找第K大/
作者
lhy
发布于
2022年10月20日
许可协议