领釦网算法学习笔记 - 215

领釦网算法学习笔记

本系列的算法题目来自领釦网

数组类算法第六天

题目:数组中的第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

说明:

  1. 你可以假设 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,最后遍历都有可能到数据的最右端或者最左端,所以需要判断返回值是否越界。

​ 至于为什么比我上面那个速度要快,还没想清楚在哪,后续在思考思考,想通了再补上、。

点赞