十大排序算法--多圖預警

 

十大排序算法

 

 

簡單的排序算法

Θ(n^2)html

插入排序

  • 動畫演示

 

enter description here
enter description here

 

  • 原理java

    將數組當作兩部分,一部分爲已排序好的數組,後面的部分爲未排序數組,每次從後面的數組中取出元素與前面的有序元素一一比較,若小於則向前移動,直到找到正確的位置插入。遍歷後面的數組直到整個數組排序完成。git

  • 代碼算法

    // 準備工做,交換函數
    public static void exc(int[] a,int i, int j) {
            if (a[i]!=a[j]) {
                a[i]^=a[j];
                a[j]^=a[i];
                a[i]^=a[j];
            }
        }
    // 插入排序
    public static void insertSort(int[] a, int n) {
            for (int i = 1; i < n; i++) {
                for (int j = i; j>0&&a[j-1]>a[j]; j--) {
                    exc(a, j, j-1);
                }
            }
        }
  • 分析shell

    時間複雜度api

    • 平均: n×n/4 次比較,n×n/4 次交換
    • 最好: n-1 次比較,0次交換
    • 最壞: n×n/2 次比較, n×n/2 交換

    評價:數組

    ​ 插入排序與數組的逆序度有關,最好狀況爲 O(n),因此常常與快速排序一塊兒出現,詳見C語言的quickSort的實現數據結構

冒泡排序

  • 動畫演示app

    enter description here

  • 原理ide

    就像泡泡同樣,不斷把大的數字往上浮,遍歷完整個數組排序即完成。

  • 代碼

    public static void bubbleSort(int[] a, int n) {
            boolean flag = true;
            for (int i = 0; i < n-1&&flag; i++) {
                flag = false;
                for (int j = 0; j < n-i-1; j++) {
                    if (a[j]>a[j+1]) {
                        exc(a, j, j+1);
                        flag=true;
                    }
                }
            }
        }
  • 分析

    時間複雜度:

    • 平均狀況下:冒泡比較的次數約是插入排序的兩倍,移動次數一致。
    • 平均狀況下: 冒泡比較的次數與選擇排序是同樣的,移動次數是O(n^2)。

評價:

你們也看到上述代碼有個標記變量 flag,這是冒泡排序的一種改進,若是在第二次循環中沒有發生交換說明排序已經完成,不須要再循環下去了。

選擇排序

  • 動畫演示

    selectSort

  • 原理

    選擇排序的原理很簡單,就是從須要排序的數據中選擇最小的(從小到大排序),而後放在第一個,選擇第二小的放在第二個……

  • 代碼

    // 選擇排序,穩定
        public static void selectSort(int[] a,int n) {
            for (int i = 0; i < n; i++) {
                int min=i;
                for (int j = i+1; j < n; j++) {
                    if(a[min]>a[j]){
                        min = j;
                    }
                }
                if (min!=i) {
                    exc(a, i, min);
                }
                
            }
        }
  • 分析

    時間複雜度:

    • 比較的次數: (n-1)+(n-2)+...+1= n(n-1)/2

    • 交換的次數: n

    評價:

    • 運行時間與輸入無關,由於前一次的操做,不能爲後面提供信息
    • 數據的移動次數是最小的

高效的比較排序算法

Θ(nlog⁡n)

希爾排序

  • 圖片演示

    shellSort

  • 原理

    希爾排序是基於插入排序進行改進,又稱之爲遞減增量排序。在前面中咱們知道,插入排序是將小的元素往前挪動位置,而且每次只移動一個位置。那麼希爾排序是怎麼解決這個問題的呢?

    希爾排序的理念和梳排序的理念有點相似。在梳排序中,咱們比較距離相差爲step的兩個元素來完成交換。在希爾排序中,咱們的作法也是相似。咱們在數組中每隔h取出數組中的元素,而後進行插入排序。當h=1時,則就是前面所寫的插入排序了。

  • 代碼

    // 6. 希爾排序
        public static void shellSort(int[] a, int n) {
            int h =1;
            while (h<n/3) {
                // 數組 1,4,13,40...
                h = h*3+1;
            }
            while (h>=1) {
                for (int i = h; i < n; i++) {
                    for(int j=i;j>=h&&a[j-h]>a[j];j-=h){
                        exc(a, j, j-h);
                    }
                }
                h/=3;
            }
        }
  • 分析

    是第一個突破時間複雜度O(n^2)的算法
    思路--計算步長,對每次分組進行直接插入排序,減少逆序度
    算法時間複雜度在插入排序和快速排序之間

快速排序

  • 動畫演示

    quickSort

  • 原理

    快速排序使用了, Divide and Conquer (分治)策略,不斷地把數組分爲較大和較小的兩個子序列,而後對每一個子序列進行遞歸,直到不可再分。思路就是在拆分的同時進行排序歸併排序不一樣。

  • 步驟:

    1. 挑選基準值:從數列中挑出一個元素,稱爲「基準」(pivot),

    2. 分割:從新排序數列,全部比基準值小的元素擺放在基準前面,全部比基準值大的元素擺在基準後面(與基準值相等的數能夠到任何一邊)。在這個分割結束以後,對基準值的排序就已經完成。

    3. 遞歸排序子序列:遞歸地將小於基準值元素的子序列和大於基準值元素的子序列排序。

    遞歸到最底部的判斷條件是數列的大小是零或一,此時該數列顯然已經有序。

  • 代碼

    // 第一部分
        public static int partition(int[] a,int l,int h) {
            int mid = l+((h-l)>>1);
            int pivot = a[mid];
            exc(a, l, mid);
            int i = l;
            int j = h+1;
            while (true) {
                while (a[++i]<pivot) {
                    if(i==h) break;
                }
                while (a[--j]>pivot) {
                    if(j==l) break;
                }
                if (i>=j) {
                    break;
                }
                exc(a, i, j);
            }
            exc(a, l, j);
            return j;
        }
        public static void quickSort(int[] a, int n) {
            quickSort(a, 0, n-1);
    
        }
        // 第二部分
        public static void quickSort(int[] a, int lo, int h) {
            if (lo>=h) {
                return;
            }
            int j = partition(a, lo, h);
            quickSort(a, lo, j-1);
            quickSort(a, j+1, h);
        }
  • 分析

    快速排序的最壞時間複雜度爲O(n^2),平均時間複雜度爲 O(n logn),快速排序基本上被認爲是比較排序算法中,平均性能最好的。多種語言皆實現了快速排序的類庫。

歸併排序

  • 動畫演示

    mergeSort

  • 原理

    採用分治法:

    • 分割:遞歸地把當前序列平均分割成兩半。
    • 集成:在保持元素順序的同時將上一步獲得的子序列集成到一塊兒(歸併)。
    • 與快速排序不一樣的是,歸併是拆分完成後,在合併階段進行排序,而快速排序 是邊拆分邊排序
  • 代碼

    // 第一部分 合併
        public static void merge(int[] a, int low, int mid, int high) {
            // 第一種寫法
            int i = low;
            int j = mid + 1;
            int k = 0;
            int[] a2 = new int[high - low + 1];
            while (i <= mid && j <= high) {
                if (a[i] < a[j]) {
                    a2[k] = a[i];
                    i++;
                    k++;
                } else {
                    a2[k] = a[j];
                    j++;
                    k++;
                }
            }
            while (i <= mid) {
                a2[k] = a[i];
                i++;
                k++;
            }
            while (j <= high) {
                a2[k] = a[j];
                j++;
                k++;
            }
            for (k = 0, i = low; i <= high; k++, i++) {
                a[i] = a2[k];
            }
        }
    
    public static void mergeSort(int[] a, int n) {
            mergeSort(a, 0, n - 1);
        }
    // 第二部分 遞歸
    public static void mergeSort(int[] a, int low, int high) {
            if (low >= high)
                return;
            int mid = (high + low) / 2;
            mergeSort(a, low, mid);
            mergeSort(a, mid + 1, high);
            merge(a, low, mid, high);
        }
  • 分析

    歸併排序是一種穩定的且十分高效的排序。時間複雜度老是 O(nlogn),不論好壞,但缺點是,它不是原地排序,佔用額外的空間,空間複雜度爲 O(n)

堆排序

  • 動畫演示

    heapSort

  • 原理

    堆排序是藉助這一數據結構實現的排序

    heap

    咱們利用大頂堆(堆頂元素最大)實現排序,在一個堆中,位置k的結點的父元素的位置是(k+1)/2-1,而它的兩個子節點的位置分別是2k+12k+2,這樣咱們就能夠經過計算數組的索引在樹中上下移動。

    思路: 不斷把堆頂的元素與最後的元素交換位置,從新堆化,不斷獲得第k(=1,2,3...)大的元素。至關於一個將大的元素 sink(下沉) 的過程。

  • 代碼

    // 建堆
    public static void buildHeap(int[] a, int n) {
            for (int i = n / 2; i >= 0; i--) {
                heapify(a, n - 1, i);
            }
        }
    // 堆化
    public static void heapify(int[] a, int n, int i) {
            while (true) {
                int maxPos = i;
                if (i * 2 + 1 <= n && a[i] < a[2 * i + 1]) {
                    maxPos = i * 2 + 1;
                }
                if (i * 2 + 2 <= n && a[maxPos] < a[i * 2 + 2]) {
                    maxPos = i * 2 + 2;
                }
                if (i == maxPos) {
                    break;
                }
                exc(a, i, maxPos);
                i = maxPos;
            }
        }
    
    public static void heapSort(int[] a, int n) {
            buildHeap(a, n);
            int k = n - 1;
            while (k > 0) {
                // 交換堆頂元素,把第1,2,3...大元素放到底部
                exc(a, 0, k);
                --k;
                heapify(a, k, 0);
            }
        }
  • 分析

    • 時間複雜度一直都是 O(nlogn),不論最好最壞狀況。
    • 缺點:
      1. 不穩定算法
      2. 堆排序的每次排序其數組逆序度都比其餘算法高
      3. 對內存訪問不友好(不連續)

犧牲空間的線性排序算法

Θ(n)

計數排序

  • 動畫演示

    countSort

  • 原理

    計數排序使用一個額外的數組C,其中 C 中第i個元素是待排序數組A中值等於i的元素的個數。而後根據數組C 來將A中的元素排到正確的位置。

    tips:固然,若是數據比較集中的話,咱們大可沒必要建立那麼大的數組,咱們找出最小和最大的元素,以最小的元素做爲基底以減少數組的大小。

  • 代碼

    // 非比較排序
        public static void countSort(int[] a, int n) {
            int max = a[0];
            for (int i = 0; i < n; i++) {
                if (a[i] > max) {
                    max = a[i];
                }
            }
            int[] c = new int[max + 1];
            int indexArray = 0;
            for (int i = 0; i < n; i++) {
                c[a[i]]++;
            }
            for (int i = 0; i <= max; i++) {
                if (c[i] != 0) {
                    a[indexArray] = i;
                    c[i]--;
                    indexArray++;
                }
            }
        }

桶排序

  • 圖片演示

    bucket

  • 原理

    桶排序的基本思想是假設數據在[min,max]之間均勻分佈,其中min、max分別指數據中的最小值和最大值。那麼將區間[min,max]等分紅n份,這n個區間便稱爲n個桶。將數據加入對應的桶中,而後每一個桶內單獨排序。因爲桶之間有大小關係,所以能夠從大到小(或從小到大)將桶中元素放入到數組中。

  • 代碼

    public static void bucketSort(int[] a, int n, int bucketSize) {
            int max = a[0];
            int min = a[1];
            for (int v : a) {
                if (v > max) {
                    max = v;
                } else if (v < min) {
                    min = v;
                }
            }
    
            // 桶的大小
            int bucketCount = (max - min) / bucketSize + 1;
            int bucket[][] = new int[bucketCount][bucketSize];
            int indexArr[] = new int[bucketCount];
            // 將數字放到對應的桶中
            for (int v : a) {
                int j = (v - min) / bucketSize;
                if (indexArr[j] == bucket[j].length) {
                    ensureCapacity(bucket, j);
                }
                bucket[j][indexArr[j]++] = v;
            }
            // 每一個桶快排
        	// 也可使用插入保證穩定性
            int k = 0;
            for (int i = 0; i < bucketCount; i++) {
                if (indexArr[i] == 0) {
                    continue;
                }
                quickSort(bucket[i], indexArr[i]);
                for (int j = 0; j < indexArr[i]; j++) {
                    a[k++] = bucket[i][j];
                }
            }
    
        }
    
        // 擴容函數
        private static void ensureCapacity(int[][] bucket, int j) {
            int[] tempArr = bucket[j];
            int[] newArr = new int[tempArr.length * 2];
            for (int k = 0; k < tempArr.length; k++) {
                newArr[k] = tempArr[k];
            }
            bucket[j] = newArr;
        }
  • 分析

    桶排序是線性排序的一種,桶排序的核心就是根據數據的範圍 (m) ,把數據 (大小爲n),儘量均勻得放到 K個桶裏,每一個桶再各自實現排序,而後把桶從小到大的列出來,即完成排序。

    • 時間複雜度 O(N+C),其中C=N*(logN-logK),空間複雜度爲 O(N+K)
    • 更適用於外部排序,尤爲是當 N很大,而M較小時,好比高考排名,分數是固定的,從 0-750分,考生人數不少,用桶排序就能很快得出排名。

基數排序

  • 動畫演示

    radixSort

  • 原理

    在平常的使用中,咱們接觸基數排序比較少,它也是桶排序的一種變形

    它的具體實現分爲 LSD (Least sgnificant digital) , MSD (Most sgnificant digital) 兩種方法,上面的演示是第一種(LSD),從低位到高位,根據每一位上的數字將元素放入桶中,再按順序取出,直到比較完最高位,完成排序。

  • 代碼

    /** * * @param x 每一位上的值 * @param d 第d位 * @param dg 輔助數組 * @return 對應的桶的標號 */
        public static int getDigit(int x, int d, int[] dg) {
            return (x / dg[d - 1] % 10);
        }
    
        /** * * @param a 待排序數組 * @param n 數組長度 */
        public static void radixSort(int[] a, int n) {
            // 最大的數
            int max = 0;
            int j = 0, i = 0;
            // 默認十進制
            final int radix = 10;
            for (int val : a) {
                if (val > max) {
                    max = val;
                }
            }
            // 求最大位數
            int N;
            if (max == 0) {
                N = 1;
            } else {
                N = (int) Math.log10(max) + 1;
            }
            // 設置輔助數組
            int dg[] = new int[N + 1];
            for (i = 1, dg[0] = 1; i < N + 1; i++) {
                dg[i] = 10 * dg[i - 1];
            }
            // 初始化桶
            int bucket[][] = new int[radix][n];
            int indexArr[] = new int[radix];
            for (int d = 1; d <= N; d++) {
                for (int var : a) {
                    j = getDigit(var, d, dg);
                    bucket[j][indexArr[j]++] = var;
                }
                int count = 0;
                for (i = 0; i < radix; i++) {
                    if (indexArr[i] != 0) {
                        for (int k = 0; k < indexArr[i]; k++) {
                            a[count++] = bucket[i][k];
                        }
                        indexArr[i] = 0;
                    }
                }
            }
        }
  • 分析

    時間複雜度爲 O(k*n),空間複雜度爲O(n),當處理較大(位數多)的數字排序時,比計數排序更好用。

綜合分析

  1. 咱們能夠看出基於比較的排序算法,他的時間複雜度的最好上界是逼近 O(nlogn) 的,這是由於,比較排序能夠當作是決策樹,而數組共有 n! 種排列方式,根據 斯特林公式 比較排序的時間複雜度的最好上界是接近於 nlogn的

  2. 咱們能夠看出基於非比較排序的線性時間排序的思路,大體相同,都是找到與元素匹配的桶,完成排序。都是空間換時間的思想。

    文章最後,感謝你們的閱讀,文中如有錯漏之處,請在留言區積極指出,十分歡迎你們一塊兒交流討論!

    另外感謝朋友們的支持友情連接

相關文章
相關標籤/搜索