經典算法之快速選擇算法


相信快速排序算法這種經典的算法大家並不陌生。但是基於快速算法的各種變形,你了解嗎?

 

其中很重要的一種變形就是快速選擇算法,  通常用來在未排序的數組中尋找第k小/第k大的元素。快速選擇及其變種是實際應用中最常使用的高效選擇算法。

 

快速選擇的總體思路與快速排序一致,選擇一個元素作為基准來對元素進行分區,將小於和大於基准的元素分在基准左邊和右邊的兩個區域。不同的是,快速選擇並不遞歸訪問雙邊,而是只遞歸進入一邊的元素中繼續尋找。這降低了平均時間復雜度,從O(n log n)至O(n),不過最壞情況仍然是O(n2)。

 

lc上一道題:

Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.

 

下面的解法注釋詳細,能很好的理解快速選擇算法,這個可以當作模版記下來。面試時能一次ac不出錯嗎?其實不是那么容易。記住的關鍵在於深刻的理解。

 

class Solution {
    /**
 *  解法0. 堆 O(klogn)
 *  解法1. 快速選擇: O(n)
 */

    public int findKthLargest(int[] nums, int k) {
        if (nums.length == 0 || nums == null) return 0;
        int left = 0, right = nums.length - 1;
        while (true) {
            int position = partition(nums, left, right);
            if (position == k - 1) return nums[position]; //每一輪返回當前pivot的最終位置,它的位置就是第幾大的,如果剛好是第K大的數
            else if (position > k - 1) right = position - 1; //二分的思想
            else left = position + 1;
        }
    }

    private int partition(int[] nums, int left, int right) {
        int pivot = left;
        int l = left + 1; //記住這里l是left + 1
        int r = right;
        while (l <= r) {
            while (l <= r && nums[l] >= nums[pivot]) l++; //從左邊找到第一個小於nums[pivot]的數
            while (l <= r && nums[r] <= nums[pivot]) r--; //從右邊找到第一個大於nums[pivot]的數
            if (l <= r && nums[l] < nums[pivot] && nums[r] > nums[pivot]) {
                swap(nums, l++, r--);
            }
        }
        swap(nums, pivot, r); //交換pivot到它所屬的最終位置,也就是在r的位置,因為此時r的左邊都比r大,右邊都比r小
        return r; //返回最終pivot的位置
    }

    private void swap(int[] nums, int l, int r) {
        int tmp = nums[l];
        nums[l] = nums[r];
        nums[r] = tmp;
    }

    
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM