思路
-
首先想到的是快速排序(快速排序思路见第三条),将给定的输入数组nums按照从小到大的顺序排序,然后直接返回数组的第 nums.length - k + 1个元素(也就是返回nums[nums.length - k + 1])
-
但是我们知道在快速排序中我们一般使用两个指针去遍历这个数组,遍历一次数组,就确定一个x值的位置,然后确保x左边的元素都小于x,x右边的元素都大于x,然后通过分治的思想不断划分数组,也就是将x值的左右两边进行划分,分别再去这两个划分的子数组的x1,x2,确保x1左边的元素都小于x1,x1右边的元素都大于x1,x2亦是如此。这样平均时间复杂度是 O(nlogn),但其实我们可以做的更快,因为在最坏的情况下,比如数组按照从大到小的顺序排列好了,我们再去使用快排,将它从小到大进行排列,这时候就属于最坏情况,时间复杂度有n^2,于是,我们可以根据题目要求适当的优化一下快速排序算法,我们只需要得到下标为 nums.length - k 的元素,由于快速排序每次递归我们都可以确定一个x的位置,而x的值也在每次递归的前期就确定好了的,所以在递归期间,两个前后指针相等将要结束递归前,我们做个判断,判断这两个指针代表的下标值是否等于 nums.length - k ,含义是,这两个指针代表的值是否是数组中的第k个最大元素,如果等于 nums.length - k,那么就直接返回nums[ nums.length - k],如果指针小于 nums.length - k ,说明我们要找的第k的最大元素在本次递归时确定的Xn的右边,那Xn左边的的数列就不需要再递归的遍历的,左边肯定没有我们要找的值,如果大于 nums.length - k, 说明我们要找的第k的最大元素在本次递归时确定的Xn的左边,那右边就不需要再找了,大大减少遍历和递归的成本。
-
快速排序
-
定义两个指针,初始设置为0和数组的末尾下标,设置base值为startIndex所指向的值
-
首先从后往前,如果nums[endIndex] > base,那么久endIndex--,知道从后往前找到第一个小于base的,将endIndex所指向的这个小于base值和当前startIndex指向的值交换(目的是确保最终startIndex和endIndex所指向的共同的x的左边的数都小于x,右边的数都大于x),交换完之后,startIndex++,直到strartIndex找到第一个大于base的值,这之后将startIndex所指向的这个大于base值和当前endIndex指向的值交换,交换完之后,再将endIndex--,也就将endIndex向前移动
- 从代码上看,endIndex存在一直往后移动的操作,直到某个值小于base,所以这个移动操作需要一个循环A来做,startIndex存在一直往前移动的操作,直到某个值大于base,所以这个移动操作需要一个循环B,然后endexIndex和startIndex的移动存在交替,startIndex移动到某点交换元素后,轮到endIndex移动,endIndex移动完交换好元素后轮到startIndex移动,这也可以用一个循环来做:只要startIndex < endIndex,就一直在一个大循环里面,不断做A,B两个循环,见下面伪代码
-
一轮递归结束之后,我们找到了最开始的base值在最终的有序数组中的位置,也就是说,其实这个确认的x值就是这个base,我们确认的是这个x也就是base值的位置下标,确认好后,对这个值的左右两边的元素进行递归,直到这个递归的子数组为单个元素为止。这里需要注意,我们递归子数组并不是创建一个又一个子数组,我们只需要传递给定的输入的原始数组,添加我们希望访问的属于原数组的子数组下标,也就是通过startIndex和endIndex来访问原数组的子数组即可。
-
while (startIndex < endIndex) {
while (arr[endIndex] >= base && startIndex < endIndex) {
endIndex--;
}
swap(arr, startIndex, endIndex)
while (arr[startIndex] <= base && startIndex < endIndex) {
startIndex++;
}
swap(arr, startIndex, endIndex)
}
if (startIndex === (nums.length - k)) {
return arr[startIndex]
} else if (startIndex < (nums.length - k)) {
return partion(arr, startIndex + 1, endIndex2);
} else if (startIndex > (nums.length - k)) {
return partion(arr, startIndex2, startIndex - 1);
}
题目
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
提示:
1 <= k <= nums.length <= 105-104 <= nums[i] <= 104
代码
var findKthLargest = function (nums, k) {
let startIndex = 0;
let endIndex = nums.length - 1;
let res = null;
const partion = (arr, startIndex, endIndex) => {
if (startIndex > endIndex) return null;
let base = arr[startIndex];
let endIndex2 = endIndex;
let startIndex2 = startIndex;
const swap = (arr, i, j) => {
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
while (startIndex < endIndex) {
while (arr[endIndex] >= base && startIndex < endIndex) {
endIndex--;
}
swap(arr, startIndex, endIndex)
while (arr[startIndex] <= base && startIndex < endIndex) {
startIndex++;
}
swap(arr, startIndex, endIndex)
}
if (startIndex === (nums.length - k)) {
return arr[startIndex]
} else if (startIndex < (nums.length - k)) {
return partion(arr, startIndex + 1, endIndex2);
} else if (startIndex > (nums.length - k)) {
return partion(arr, startIndex2, startIndex - 1);
}
}
return el = partion(nums, startIndex, endIndex)
};