排序的複雜度分析

各排序的時間複雜度分析

插入排序——直接插入排序

  • 在最好的狀況下,序列已是有序的,每次插入元素最多隻須要與有序表中最後一個元素進行比較,時間複雜度爲O(n)。在最壞的狀況下,每次插入元素須要與前面全部的元素進行比較,時間複雜度爲O(n^2),平均時間複雜度爲O(n^2)。算法

  • 代碼分析數組

    public static <T extends Comparable<T>> void insertionSort(T[] data)
        {
        //外層循環次數爲n,時間複雜度爲O(n)
        for (int index = 1; index < data.length; index++)
        {
            T key = data[index];
            int position = index;
    
            //內層循環次數爲n,時間複雜度爲O(n)
            while (position > 0 && data[position-1].compareTo(key) > 0)
            {
                data[position] = data[position-1];
                position--;
            }
    
            data[position] = key;
        }
        }

插入排序——希爾排序

  • 它的基本思想是:假設序列中有n個元素,首先選擇一個間隔gap,將所有的序列分爲gap個子序列,而後分別在子序列內部進行簡單插入排序,獲得一個新的主序列;然後縮小gap,再獲得子序列,對子序列進行簡單插入排序,又再次獲得新的主序列,直到gap=1爲止。在算法中,排序前期,因爲gap值比較大,插入排序的元素個數少,排序快,到了排序後期,因爲前面的排序致使序列已經基本有序,插入排序對於有序的序列效率很高。因此說希爾排序最好的狀況:縮小增量的插入排序,待排序已經有序。時間複雜度O(n),通常狀況爲下平均時間複雜度o(n^1.3),最差也是時間複雜度o(n^1.3)。希爾排序的時間複雜度與gap的選擇有很大的關係,通常時間複雜度是低於O(n^2)。函數

  • 須要注意的是:希爾排序的時間複雜度依賴於所取希爾序列的函數,可是到目前爲止尚未一個最好的希爾序列。有人在大量的實驗後得出結論:當n在某個特定的範圍後希爾排序的比較和移動次數減小至n^1.3 無論增量序列如何取值,都應該知足最後一個增量值爲1.ui

選擇排序——簡單選擇排序

  • 簡單選擇排序不管是否序列已經有序每一個數都須要進行n-1次最小數選擇,因此它的最好、最壞以及平均時間複雜度都是O(n^2)。.net

  • 代碼分析code

    public static <T extends Comparable<T>> void selectionSort(T[] data)
        {
        int min;
        T temp;
        //外層循環次數爲n-1,時間複雜度爲O(n)
        for (int index = 0; index < data.length-1; index++)
        {
            min = index;
            //內層循環次數爲n,時間複雜度爲O(n)
            for (int scan = index+1; scan < data.length; scan++) {
                if (data[scan].compareTo(data[min])<0) {
                    min = scan;
                }
            }
            swap(data, min, index);
            }
        }

選擇排序——堆排序

  • 把待排序的元素按照大小在二叉樹位置上排列,排序好的元素要知足:父節點的元素要大於等於其子節點;這個過程叫作堆化過程,若是根節點存放的是最大的數,則叫作大根堆;若是是最小的數,天然就叫作小根堆了。根據這個特性(大根堆根最大,小根堆根最小),就能夠把根節點拿出來,而後再堆化下,再把根節點拿出來,,,,循環到最後一個節點,就排序好了。整個排序主要核心就是堆化過程,堆化過程通常是用父節點和他的孩子節點進行比較,取最大的孩子節點和其進行交換;可是要注意這應該是個逆序的,先排序好子樹的順序,而後再一步步往上,到排序根節點上。而後又相反(由於根節點也多是很小的)的,從根節點往子樹上排序。最後才能把全部元素排序好。對象

  • 時間複雜度在任何狀況下都爲O(nlogn)
    堆排序的時間複雜度爲O(nlogn),須要一個臨時空間用於交換元素,因此空間複雜度爲O(1)。blog

  • 排序包括兩個階段,初始化建堆和重建堆。因此堆排序的時間複雜度由這兩方面組成。排序

    • 初始化堆:假設高度爲k,則從倒數第二層右邊的節點開始,這一層的節點都要執行子節點比較而後交換(若是順序是對的就不用交換);倒數第三層,則會選擇其子節點進行比較和交換,若是沒交換就能夠不用再執行下去了。若是交換了,那麼又要選擇一支子樹進行比較和交換;高層也是這樣逐漸遞歸。
        
      那麼總的時間計算爲:s = 2^( i - 1 ) * ( k - i );其中 i 表示第幾層,2^( i - 1) 表示該層上有多少個元素,( k - i) 表示子樹上要比較的次數。
        S = 2^(k-2) * 1 + 2^(k-3)2…..+2(k-2)+2^(0)*(k-1) ===> 由於葉子層不用交換,因此i從 k-1 開始到 1;
        S = 2^k -k -1;又由於k爲徹底二叉樹的深度,而log(n) =k,把此式帶入;
        獲得:S = n - log(n) -1,因此時間複雜度爲:O(n)遞歸

    • 排序重建堆:在每次重建時,隨着堆的容量的減少,層數會降低,函數時間複雜度會變化。重建堆一共須要n-1次循環,每次循環的比較次數爲log(i),相加約爲nlog(n)。

    • 因此總的時間複雜度爲O(n+nlogn)=O(nlogn)。

交換排序——冒泡排序

  • 在最好的狀況下,序列已是有序的,只進行了第一趟冒泡比較,此時算法的時間複雜度爲O(n)。在最壞的狀況下,執行了n-1次冒泡,時間複雜度爲O(n^2)。

  • 代碼分析:

    public static <T extends Comparable<T>> void bubbleSort(T[] data)
        {
        int position, scan;
        T temp;
        //循環次數爲n-1,時間複雜度爲O(n)
        for (position =  data.length - 1; position >= 0; position--)
        {
            //循環次數爲n,時間複雜度爲O(n)
            for (scan = 0; scan <= position - 1; scan++)
            {
                if (data[scan].compareTo(data[scan+1]) > 0) {
                    swap(data, scan, scan + 1);
                }
            }
        }
        }

交換排序——快速排序

  • 時間複雜度分析:快速排序每次要將列表分紅兩個分區,遞歸的次數取決於元素的數目,最理想的狀況下,每次劃分左右兩部分的長度相等,須要遞歸次nlog2n次,平均次數也爲nlog2n次,而每次分區後要進行n次比較操做,所以平均時間複雜度爲O(nlogn)。快速排序比大部分排序算法都要快,但快速排序是一個很是不穩定的排序,由於若初始序列按關鍵碼有序或基本有序時,快速排序反而蛻化爲冒泡排序,此時它的時間複雜度就爲O(n^2)了。

歸併排序

  • 歸併排序是先遞歸的把數組劃分爲兩個子數組,一直遞歸到數組中只有一個元素,而後再調用函數把兩個子數組排好序,由於該函數在遞歸劃分數組時會被壓入棧,因此這個函數真正的做用是對兩個有序的子數組進行排序。

  • 時間複雜度分析:每次歸併時要將待排序列表中的全部元素遍歷一遍,因次時間複雜度爲O(n)。與快速排序相似,歸併排序也先將列表不斷分區直至每一個列表只剩餘一個元素,這個過程須要進行log2n次分區。所以歸併排序的平均時間複雜度爲O(nlogn)。由於無論元素在什麼狀況下都要作這些步驟,因此花銷的時間是不變的,因此該算法的最優時間複雜度和最差時間複雜度及平均時間複雜度都是同樣的爲:O( nlogn )。

  • 複雜公式分析:總時間=分解時間+解決問題時間+合併時間。分解時間就是把一個待排序序列分解成兩序列,時間爲一常數,時間複雜度o(1)。解決問題時間是兩個遞歸式,元素長度爲n的歸併排序所消耗的時間T[n],把一個規模爲n的問題分紅兩個規模分別爲n/2的子問題,時間爲2T(n/2)。合併時間複雜度爲o(n)。總時間T(n)=2T(n/2)+o(n),因此得出的結果爲:T[n] = O( nlogn )。參考: http://blog.csdn.net/yuzhihui_no1/article/details/44198701#t2

基數排序

  • 基本思想就是把元素從個位排好序,而後再從十位排好序,,,,一直到元素中最大數的最高位排好序,那麼整個元素就排好序了。

  • 時間複雜度分析:對於有n個元素的序列,對每一位數放置和收集的時間爲O(n+r),則其時間複雜度爲 O(d(n+r))。(r爲基數,d爲位數)

  • 既然基數排序的時間複雜度這麼低,爲何不是全部的排序都使用基數排序法呢?
    首先,基數排序沒法創造出一個使用於全部對象類型的泛型基數排序,由於在排序過程當中要進行關鍵字取值的切分,所以關鍵字的類型必須是肯定的。
    其次,當基數排序中的基數大小與列表中的元素數目很是接近時,基數排序法的實際時間複雜度接近於O(n^2)。

八大排序方法分析

相關文章
相關標籤/搜索