快速尋找第k大元素

一個好的算法往往是在樸素的想法上進行加工改造,我們要求數組中的第k大元素,往往最先想到的想法是先進行降序排序,可以很快的達到答案。但排序在序列元素很多時,無疑是一個浩大的工程。要至少耗費O(logn)的時間複雜度,如果想在線性時間O(n)就想做到求解應該怎麼做呢?

我們先思考排序爲什麼可以解決該問題:

如果我們隨便選定一個元素,假想的認爲它就是我們要找的第k大元素,我們最終要考察,證明的是這個元素在序列降序排序後到底應該處於哪個位置,如果是在第k位,那自然就是第k大元素。說到這裏似乎漏出了點端倪,是啊,我們這樣證明的話並不需要將序列完整的排序,我們只需要將比這個選定元素小的元素排在他的前面,比他大的開到後面去,這時他是不是第k大的元素同樣一目瞭然。

這樣子搞,運氣好了,選的元素正好就是第k大的,那賊舒服,美滋滋。

但人生不如意事十之八九,如果有這個運氣,似乎買彩票都能中獎了。

別慌,雖然我們可能一下子猜不對,但是我們每次猜測都是對歷史進程有幫助的!

假如我們有亂序序列 5 8 3 2 1,我們想求它的第3大元素,我們最開始隨便一猜,認定5就是第k大元素,這個時候我們把比5大的放到5的左邊,比比5小的放到5的右邊

序列變成了 8 5 3 2 1. 

額,第一次沒猜中,不過真的不遺憾,我們猜的5,最終位置是2,比3要小,我們至少可以判斷第3大元素肯定不在2及的左邊

我們考察的範圍可以縮小至近乎原來的一半。我們只需要考察3 2 1即可,接下來,我們猜的是3,比他大的放到左邊,比他小的放到右邊,序列沒變!仍然是 8 5 3 2 1 。這時,我們確定了3就是我們猜的第3大元素,進行輸出

for(int lo=0 , hi = n-1; lo<hi;){
        int i = lo, j = hi; int pivot = a[lo];
        //在區間[lo,hi)裏,比p小的放到p後面,比p大的放在前面
        while( i < j){
            while( i < j && a[j] <=pivot )j--;a[i] = a[j];//右邊小於pivot的都跳過,大於的提到前面去
            while( i < j && a[i] >= pivot)i++;a[j] = a[i];//左邊大於pivot的都跳過,小於的提到後面去
        }
        a[i] = pivot;//撞車的部分不是i發現不對勁就是j發現了不對勁,元素已經被轉移了
        
        
        
        if( i == k){cout<<a[k-1];return 0;}//k-1是因爲下標是從0開始的
        else if( k > i)lo = i + 1;
        else if( k < i)hi = i - 1;
    }

 

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