1. 比較排序算法的下界算法
到目前爲止,咱們已經介紹了幾種能在O(nlgn)時間內排序n個數的算法:歸併排序和堆排序達到了最壞狀況下的上界;快速排序在平均狀況下達到該上界。數組
若是仔細觀察,咱們會發現:在排序的最終結果中,各元素之間的次序依賴於它們之間的比較。咱們把這類排序算法統稱爲比較排序。到目前爲止咱們介紹的排序算法都是比較排序。下面咱們來論證一個事實:任何比較排序算法在最壞狀況下都要通過Ω(n lgn)次比較。spa
在證實以前,咱們先介紹一種由比較排序抽象而來的決策樹模型。決策樹是一棵徹底二叉樹,它能夠表示在給定輸入規模狀況下,某一特定排序算法對全部元素的比較操做,而控制,數據移動等操做都被忽略了。以下圖,它顯示的是插入排序算法做用於包含三個元素的輸入序列的決策樹狀況。3d
從決策樹中咱們能夠看出:從根結點到任意一個可到達葉結點之間的最長簡單路徑的長度,表示的就是對應排序算法中最壞狀況下的比較次數。所以,一個比較排序算法中的最壞狀況的排序次數就等於決策樹的高度。而且,當決策樹中全部排列都是以可到達的葉結點的形式出現時,該決策樹高度的下界也就是比較排序算法運行時間的下界。下面咱們正式給出證實。blog
考慮一棵高度爲h,具備l個可到達葉結點的決策樹。它對應一個對n個元素進行的比較排序。由於輸入數據有n!種可能的排列都是葉結點,因此n!≤l。因爲在一棵高度爲h的二叉樹中,葉結點的數目很少於2^h,咱們獲得:排序
n! ≤ l ≤ 2^h,ci
兩邊取對數得:get
h ≥ lg(n!) = Ω(nlgn)it
2. 計數排序table
咱們先假設待排序序列各元素均在區間[0, k]上。
計數排序的思想是:在待排序序列中,若是咱們能統計出有多少元素小於或等於某一個元素,咱們也就知道了該元素的正確位置。例如,對於待排序序列{2,5,3,0,2,3,0,3},咱們統計出有8個元素小於等於5(包括5本身),那麼5這個元素就應該被排序到第8位。
下面給出算法的僞代碼描述:
其中數組A[1~n]是待排序數組;數組B[1~n]用來存放已排好序的元素。C[0~k]用來存放上面所說的統計數(具體的說C[i]就表示在數組A中,小於或等於i的元素的總個數)。
下面這幅圖描述的是對序列{2,5,3,0,2,3,0,3}排序的過程:
下面咱們給出算法的Java實現代碼:
public static void main(String[] args) { int[] array = { 2, 5, 3, 0, 2, 3, 0, 3 }; printArray(countingSort(array, 5)); } /** * 計數排序 * * @param array * 待排序數組(假定各元素的範圍是0~max,包括0和max) * @param max * 待排序數組中的最大值 */ public static int[] countingSort(int[] array, int max) { int[] result = new int[array.length]; int[] temp = new int[max + 1]; // 如下循環操做完成後,temp的第i個位置保存着array中,值爲i的元素的總個數 for (int i : array) { temp[i]++; } // 如下循環操做完成後,temp的第i個位置保存着array中,值小於或等於i的元素的總個數 for (int i = 1; i < temp.length; i++) { temp[i] += temp[i - 1]; } for (int i = array.length - 1; i > -1; i--) { result[temp[array[i]] - 1] = array[i]; temp[array[i]]--; } return result; } /** * 打印數組 */ public static void printArray(int[] array) { for (int i : array) { System.out.print(i + " "); } System.out.println(); }
3. 算法分析
咱們如今來分析計數排序的時間代價。
在僞代碼中,第2~3行時間代價θ(k);第4~5行時間爲θ(n);第7~8行時間爲θ(k),第10~12行時間爲θ(n)。所以,總的運行時間是θ(k+n)。當k= O(n)時,運行時間爲θ(n)。
能夠看出,計數排序的下界優於咱們上面論證的比較排序算法的下界時間Ω(nlgn)。這是由於計數排序並非比較排序算法。事實上,在代碼中從未出現比較某兩個元素大小的代碼。相反,計數排序是使用輸入元素的實際值來肯定其在數組中的位置。此時,比較排序算法的模型對計數排序再也不適用。