持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第27天,点击查看活动详情
前言
题目来源
题目介绍
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
题目分析
根据题目意思,是一个排序后的数组,寻找第k个最大的元素。划重点这个数组是排序过的,可以升序或者降序,假设[3,2,1,5,6,4] 数组按升序[1,2,3,4,5,6] 、降序[6,5,4,3,2,1] 只有2种顺序,要寻找第2个最大的元素,那么第2个可能是2 也可能是5,那么一对比当然是选择5,所以最大的是5。
题目解答
public int findKthLargest(int[] nums, int k) {
int numSize=nums.length;
int j=0;
quicksort(nums,0,numSize-1);
return nums[numSize-k];
}
public void quicksort(int[] arr, int left, int right) {
if (left < right) {
int temp = qsort(arr,left,right);
/*
分区,以基准数为中心分为左右两区
qsort方法的返回值是left,即每次排序完成后基准数的位置
*/
quicksort(arr,left,temp-1);
quicksort(arr,temp+1,right);
}
}
/**
* 排序过程
* arr 待排数组
* left 待排数组的最小下标
* right 待排数组的最大下标
*/
private static int qsort(int[] arr, int left, int right) {
int temp = arr[left];
while (right>left){
//让跳出循环时则表示该数小于基准数,所以转到if中
//如果右边的值大于左边基准,那么往前一个
while (left<right&&arr[right]>temp){
right--;
}
if (left<right) {
//如果右边的值小于左边,那么交换位置
//把在right处的值给到基准数处,然后left执行加一操作
arr[left++] = arr[right];
}
//如果左边的值小于基准
while(left<right && arr[left]<=temp) {
left++;
}
//跳出上一个循环说明当前的arr[left]的值大于基准数,需要将该值填充到右边空出的位置,然后当前位置空出。
if(left<right) {
arr[right--]=arr[left];
}
}
//将基准数填入最终的位置,这样在基准数左边都比基准数小,右边都比基准数大
arr[left] = temp;
return left;
}
}
先把这个数组进行快排,得到排序后的数组,取nums[numSize-k]位置的元素就是所要求的最大值。
不过这种方式比较耗时,并不是最优解答。
官方答案验证
class Solution {
Random random = new Random();
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, 0, nums.length - 1, nums.length - k);
}
public int quickSelect(int[] a, int l, int r, int index) {
int q = randomPartition(a, l, r);
if (q == index) {
return a[q];
} else {
return q < index ? quickSelect(a, q + 1, r, index) : quickSelect(a, l, q - 1, index);
}
}
public int randomPartition(int[] a, int l, int r) {
int i = random.nextInt(r - l + 1) + l;
swap(a, i, r);
return partition(a, l, r);
}
public int partition(int[] a, int l, int r) {
int x = a[r], i = l - 1;
for (int j = l; j < r; ++j) {
if (a[j] <= x) {
swap(a, ++i, j);
}
}
swap(a, i + 1, r);
return i + 1;
}
public void swap(int[] a, int i, int j) {
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
官方的执行效率更高,都是根据快排来做,官方改进了快速排序算法来解决这个问题:在分解的过程当中,我们会对子数组进行划分,如果划分得到的 qq 正好就是我们需要的下标,就直接返回 a[q]a[q];否则,如果 qq 比目标下标小,就递归右子区间,否则递归左子区间。这样就可以把原来递归两个区间变成只递归一个区间,提高了时间效率。
总结
这道题稍微有点难度,快速排序还是很需要逻辑性的。