领釦网算法学习笔记
本系列的算法题目来自领釦网
数组类算法第六天
题目:数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
- 你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
解题过程:
思路一:
看到这题的第一个思路是:采用快速排序,排序后在取第K个元素。
代码如下:
class Solution {
public int findKthLargest(int[] nums, int k) {
// 直接用快速排序,排完再取第 k 个值
QuestSort(nums,0,nums.length-1);
return nums[k-1];
}
public static void QuestSort(int[] nums, int _left, int _right) {
int left = _left;
int right = _right;
if(left >= right){
return;
}
int temp = nums[left];
while(left < right){
if(left < right && nums[right] <= temp){
right--;
}
nums[left] = nums[right];
if(left < right && nums[left] >= temp){
left++;
}
nums[right] = nums[left];
}
nums[right] = temp;
QuestSort(nums,_left,left-1);
QuestSort(nums,right+1,_right);
}
}
// 用时153ms
后续思考:
感觉这个效率好低,然后写完之后看了一下最佳答案,发现其实不用排序,只要采用快排的思路即可。
思路二:
快排可以使元素分成两部分,在这里前面部分是大于指定数据的,后面部分是小于指定数据的,这样的话,只需要使得前面大于它的数有K个就好了。
代码如下:
class Solution {
public int findKthLargest(int[] nums, int k) {
// 套用快速排序,使得前面有k个元素比元数据大
int left = 0;
int right = nums.length - 1;
int loopValue = QuestSortNum(nums,left,right);
k -=1;
while(true){
if(k == loopValue){
return nums[loopValue];
}else if(k < loopValue){
right = loopValue - 1;
}else{
left = loopValue + 1;
}
loopValue = QuestSortNum(nums,left,right);
}
}
public static int QuestSortNum(int[] nums, int left, int right) {
if(left >= right){
return left;
}
int temp = nums[left];
while(left < right){
if(left < right && nums[right] <= temp){
right--;
}
nums[left] = nums[right];
if(left < right && nums[left] >= temp){
left++;
}
nums[right] = nums[left];
}
nums[right] = temp;
return left;
}
}
// 第一次用时54ms,第二次用时63ms,神奇,形同代码,速度不一样......
后续思考:
之前一直想着,利用快速排序的方法,先快排一遍,判断当前排序后的参考值的位置,然后在使其中一边等于参考值的位置,导致这个死活的循环不出来,后面想通了,如果循环到后面,两个数相等,这个左右的位置就不会再变动,就会一直死循环在这个,这样会有问题,然后就抛弃当前参考值的位置,反正判断都不是那个位置。
领釦上面该题其他高质量范例:
class Solution {
public int findKthLargest(int[] nums, int k) {
if(nums.length == 1) return nums[0];
int val = qsort(nums, 0, nums.length-1);
int left = 0;
int right = nums.length-1;
k -= 1;//第k大,说明有k-1个元素比他大
if(val == k) return nums[val];
while(left < right){
if(val >= k){
right = val;
val = qsort(nums, left, right);
}
else{
left = val+1;
val = qsort(nums, left, right);
}
}
return nums[k];
}
int qsort(int[] nums,int left,int right){
int i = left;
int j = right;
int center = nums[(i+j) / 2];
do{
while(nums[i] > center) i++;
while(nums[j] < center) j--;
if(i <= j){
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
i++; j--;
}
}while(i <= j);
if(j < left) return left;//防止j越界,陷入无限循环
return j;
}
}
// 用时2ms
后续思考:
第一眼看到这个程序时,总觉得判断后直接 i++ 或者 j– ,在某种特定的情况下会产生数据越界,测试后发现,无论什么情况,都会有一种情况出现,就是数的本身不会小于或者大于它自己,所以不会再这里不会造成数据越界,同时又需要返回值,不管是left,还是right,最后遍历都有可能到数据的最右端或者最左端,所以需要判断返回值是否越界。
至于为什么比我上面那个速度要快,还没想清楚在哪,后续在思考思考,想通了再补上、。