本章繼續講一些關於奇淫技巧(算法啦)的做法,對於一個無序數組,我們如何找到其中位數呢?
首先回顧一下中位數的概念:是按順序排列的一組數據中居於中間位置的數。
1,當前的先決條件是無序數組,那根據原理可以很快想到一種解法,對數組進行遍歷,每次找出其最大值、最小值,最終殘留的一位或兩位即為中位數(兩位則取平均值),時間復雜度 O(N) * N;當然,一次遍歷中我們可以同時獲取到最大值和最小值,將遍歷的次數降低一半到 O(N)*N/2,但同樣難以改變其時間復雜度為 O(N2) 的事實(這里有想法的同學先不要着急否定,后面一步步迭代)。
2,很明顯,上述的方法無法達到我們的想要的一種狀態,那反觀概念,如果是排好序的數組,我們完全可以在一次計算中得到其中位數,那就可以對數組先進行一次快排,使其達到有序的狀態再返回中位數,時間復雜度就是快排的復雜度 O(N * logN)
3,換一種思路,我們知道數組的個數,那中位數無非就是整個數組中第 (n)/2(偶數則包含 n/2-1)大的數,所以我們也可以采用堆排的方案,找出第 i 位大的值即中位數,時間復雜度就是堆排的復雜度 O(N * logN)
4,本章重點解法,我們假設每次可以將數組分成兩個部分,時刻保證前半部分 A 的任何元素大於后半部分 B 的任何元素,那只需要知道數組的中位數是在前半部分還是后半部分既可遞歸查找,另一半便可以拋棄不需要再次遍歷排序。大致思路便是這樣,具體流程如下:
1,按快速排序的第一部分流程,將第一個數據進行遍歷,找出其最終位置 p,這是左邊 A 均小於當前數值,后面 B 均大於當前數值
2,如果 p - start + 1 == i,則即可返回當前數值
3,如果 p - start + 1 < i,則中位數在 B 部分,遞歸修改 start, i;反之中位數在 A 部分,遞歸修改 end,i
上述的方法其實最壞情況下(比如完全倒序或完全正序)的時間復雜度也會達到最差的 O(N2),所以這種方法的僅期望情況下(數次切割即可找到中位數),才可以線性時間內找到中位數,而且這種方法也會比傳統的快排快一部分(因為丟棄了一部分)。
ps:這一張本來是想寫成私有日志,但感覺第四種算法也有一些巧妙的點,所以列出來,有興趣的同學可以參考一下,另外《算法導論第三版》123p 提出一種最壞情況線性的選擇算法,有興趣的同學可以去研究一下。這一篇很短,僅個人喜好記錄^_^