1. 程式人生 > >領釦網演算法學習筆記 - 215

領釦網演算法學習筆記 - 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,最後遍歷都有可能到資料的最右端或者最左端,所以需要判斷返回值是否越界。

​ 至於為什麼比我上面那個速度要快,還沒想清楚在哪,後續在思考思考,想通了再補上、。