線性時間選擇算法(Java)

線性時間選擇算法

最近算法課的知識點之一,自己以前沒遇見過(是我菜了沒錯),這次寫一篇。

那麼什麼是線性時間選擇呢???一句話,在線性時間內完成選擇

一般情況下是這樣的,我們想要找出一個數組中的最大值或最小值,那就只需要一次排列,然後輸出第一個或最後一個元素就行了,但如果是要找出一個數組中的第k小的元素呢?

  1. 在某些特殊情況下,很容易設計出解選擇問題的線性時間算法。如:當要選擇最大元素或最小元素時,顯然可以在O(n)時間完成。(遍歷一次)

  2. 一般的選擇問題,特別是中位數的選擇問題似乎比最小(大)元素要難。但實際上,從漸近階的意義上,它們是一樣的。也可以在O(n)時間完成。

之前我記得我更過一篇,裏面說到就是先排個序,然後通過下標去得到,因此這個問題的解決效率取決於排序算法的效率。

但是現在不一樣了,我們有了更好的辦法。

隨機劃分線性選擇

分析

隨機劃分線性選擇。模仿快速排序算法,對數組在left——right範圍內進行一次劃分(partition方法,我們這裏默認採用隨機元素爲基準),得到基準下標i,不大於基準的在左邊,比基準大的在右邊。計算出left——i(包含i)中有多少個元素(j),與k進行比較。

  1. 如果k<j,說明當前left——right中第k小的在左邊序列中,那麼從left——i-1繼續上述步驟尋找第k小。
  2. 如果k=j,說明基準i就是left——right中第k小,返回a[j]。(因爲前面的都不大於它,後面的都大於它,不管左邊是序列怎麼排的,整體第k(j)小就是他)
  3. 如果k>j,說明第k小在右邊序列,大於基準a[i],那麼從i+1——right繼續劃分,並將k改爲k-j。(整體序列提出了前j個元素,那麼第k小在剩下的序列中就是第k-j小了)

一直到left=right的時候就可以返回a[left]了(其實我覺得如果有上述2的情況的話就不需要這個了)

這是課堂上PPT給的思路

這是算法導論的(就是我剛剛說的)
在這裏插入圖片描述

代碼

    /*
     * @Title randomizedSelect
     * @Description 基於隨機元素爲基準的線性時間選擇
     * @author 滑技工廠
     * @Date 2020/3/26
     * @param [a, L, R, k -> L---R中的第k小]
     * @return int
     * @throws
     */
    public static int randomizedSelect(int[] a, int L, int R, int k) {
        if (L == R)
            return a[L];
        //獲取爲基準的隨機元素的下標
        int i = randomizedPartition(a, L, R);
        //j爲劃分後左序列到基準(包含基準)的元素個數
        int j = i - L + 1;
        if (k < j)//如果k小於j,說明在基準i的左邊
            return randomizedSelect(a, L, i - 1, k);
        else if (k == j)//
            return a[i];
        else//k大於j 說明在i的右邊序列
            return randomizedSelect(a, i + 1, R, k - j);
    }

利用中位數線性時間選擇

分析

算法的思路:如果能在線性時間內找到一個劃分基準使得按這個基準所劃分出的2個子數組的長度都至少爲原數組長度的ε倍(0<ε<1),那麼就可以在最壞情況下用O(n)時間完成選擇任務。例如,當ε=9/10,算法遞歸調用所產生的子數組的長度至少縮短1/10。所以,在最壞情況下,算法所需的計算時間T(n)滿足遞推式T(n)<=T(9n/10)+O(n)。由此可得T(n)=O(n)。

先描述下過程。
將n個輸入元素劃分成n/5個組,每組5個元素,最只可能有一個組不是5個元素。用任意一種排序算法,將每組中元素排好序,並取出中位數,共n/5個。

遞歸調用Select來找出這n/5個元素中的中位數。如果n/5是個偶數,就找它兩個中位數中較大的一個。以該元素作爲劃分基準。

這種情況下,找出的基準x至少比3(n-5)/10個元素大,同理也比3(n-5)/10個元素小。(下圖中箭頭指向是從大到小,紅+藍的數量爲3(n-5)/10)而當n>=75時,3(n-5)/10>=n/4所以按此基準劃分所得的兩個子數組的長度都至少縮短1/4
在這裏插入圖片描述
得到劃分基準後,後面就和第一個一樣,計算左序列個數,和k進行比較。判斷在左序列還是右序列,在遞歸調用該方法(k在右序列仍要減j),直到k=j爲止,返回那個基準。

例題

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

代碼

    /*
     * @Title select
     * @Description 利用中位數線性時間選擇
     * @author 滑技工廠
     * @Date 2020/3/27
     * @param [a, l, r, k]
     * @return int
     * @throws
     */
    public static int select(int[] a, int l, int r, int k) {
        if (r - l < 75) {
            insertSort(a, l, r);    //用插入排序進行排序
            return a[l + k - 1];
        }
        int group = (r - l + 5) / 5;
        for (int i = 0; i < group; i++) {
            int left = l + 5 * i;
            int right = (l + i * 5 + 4) > r ? r : l + i * 5 + 4;  //如果超出右邊界就用右邊界賦值
            int mid = (left + right) / 2;
            insertSort(a, left, right);
            swap(a, l + i, mid);     // 將各組中位數與前i個
        }
        int pivot = select(a, l, l + group - 1, (group + 1) / 2);  //找出中位數的中位數
        int p = partition(a, l, r, pivot);    //用中位數的中位數作爲基準的位置
        int j = p - l + 1;       //leftNum用來記錄基準位置的前邊的元素個數
        if (k == j)
            return a[p];
        else if (k < j)
            return select(a, l, p - 1, k);
        else                    //若k在基準位子的後邊,則要從基準位置的後邊數起,即第(k - leftNum - 1)個
            return select(a, p + 1, r, k - j - 1);
    }

詳細代碼去我的G站

作業又完成了一個 (^-^)V

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章