天天看點

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

本文将采取動态圖+文字描述+正确的java代碼實作來講解以下十大排序算法:

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

0、排序算法說明

0.1 排序的定義

對一序列對象根據某個關鍵字進行排序。

0.2 術語說明

  • 穩定:如果a原本在b前面,而a=b,排序之後a仍然在b的前面;
  • 不穩定:如果a原本在b的前面,而a=b,排序之後a可能會出現在b的後面;
  • 内排序:所有排序操作都在記憶體中完成;
  • 外排序:由于資料太大,是以把資料放在磁盤中,而排序通過磁盤和記憶體的資料傳輸才能進行;
  • 時間複雜度:
  • 空間複雜度:運作完一個程式所需記憶體的大小。

0.3 算法總結

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

圖檔名詞解釋:

  • n: 資料規模
  • k: “桶”的個數
  • In-place: 占用常數記憶體,不占用額外記憶體
  • Out-place: 占用額外記憶體

0.5 算法分類

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

0.6 比較和非比較的差別

常見的快速排序、歸并排序、堆排序、冒泡排序等屬于比較排序。在排序的最終結果裡,元素之間的次序依賴于它們之間的比較。每個數都必須和其他數進行比較,才能确定自己的位置。

在冒泡排序之類的排序中,問題規模為n,又因為需要比較n次,是以平均時間複雜度為O(n²)。在歸并排序、快速排序之類的排序中,問題規模通過分治法消減為logN次,是以時間複雜度平均O(nlogn)。

比較排序的優勢是,适用于各種規模的資料,也不在乎資料的分布,都能進行排序。可以說,比較排序适用于一切需要排序的情況。

計數排序、基數排序、桶排序則屬于非比較排序。非比較排序是通過确定每個元素之前,應該有多少個元素來排序。針對數組arr,計算arr[i]之前有多少個元素,則唯一确定了arr[i]在排序後數組中的位置。

非比較排序隻要确定每個元素之前的已有的元素個數即可,所有一次周遊即可解決。算法時間複雜度O(n)。

非比較排序時間複雜度底,但由于非比較排序需要占用空間來确定唯一位置。是以對資料規模和資料分布有一定的要求。

1、冒泡排序(Bubble Sort)

冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。

1.1 算法描述

  • 比較相鄰的元素。如果第一個比第二個大,就交換它們兩個;
  • 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對,這樣在最後的元素應該會是最大的數;
  • 針對所有的元素重複以上的步驟,除了最後一個;
  • 重複步驟1~3,直到排序完成。

1.2 動圖示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

1.3 代碼實作

/**
    * 冒泡排序
    *
    * @param array
    * @return
    */
   public static int[] bubbleSort(int[] array) {
       if (array.length == 0)
           return array;
       for (int i = 0; i < array.length; i++)
           for (int j = 0; j < array.length - 1 - i; j++)
               if (array[j + 1] < array[j]) {
                   int temp = array[j + 1];
                   array[j + 1] = array[j];
                   array[j] = temp;
               }
       return array;
   }      

1.4 算法分析

最佳情況:T(n) = O(n) 最差情況:T(n) = O(n2) 平均情況:T(n) = O(n2)

2、選擇排序(Selection Sort)

表現最穩定的排序算法之一,因為無論什麼資料進去都是O(n2)的時間複雜度,是以用到它的時候,資料規模越小越好。唯一的好處可能就是不占用額外的記憶體空間了吧。理論上講,選擇排序可能也是平時排序一般人想到的最多的排序方法了吧。

選擇排序(Selection-sort)是一種簡單直覺的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。

2.1 算法描述

n個記錄的直接選擇排序可經過n-1趟直接選擇排序得到有序結果。具體算法描述如下:

  • 初始狀态:無序區為R[1..n],有序區為空;
  • 第i趟排序(i=1,2,3…n-1)開始時,目前有序區和無序區分别為R[1..i-1]和R(i..n)。該趟排序從目前無序區中-選出關鍵字最小的記錄 R[k],将它與無序區的第1個記錄R交換,使R[1..i]和R[i+1..n)分别變為記錄個數增加1個的新有序區和記錄個數減少1個的新無序區;
  • n-1趟結束,數組有序化了。

2.2 動圖示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

2.3 代碼實作

/**
    * 選擇排序
    * @param array
    * @return
    */
   public static int[] selectionSort(int[] array) {
       if (array.length == 0)
           return array;
       for (int i = 0; i < array.length; i++) {
           int minIndex = i;
           for (int j = i; j < array.length; j++) {
               if (array[j] < array[minIndex]) //找到最小的數
                   minIndex = j; //将最小數的索引儲存
           }
           int temp = array[minIndex];
           array[minIndex] = array[i];
           array[i] = temp;
       }
       return array;
   }      

2.4 算法分析

最佳情況:T(n) = O(n2) 最差情況:T(n) = O(n2) 平均情況:T(n) = O(n2)

3、插入排序(Insertion Sort)

插入排序(Insertion-Sort)的算法描述是一種簡單直覺的排序算法。它的工作原理是通過建構有序序列,對于未排序資料,在已排序序列中從後向前掃描,找到相應位置并插入。插入排序在實作上,通常采用in-place排序(即隻需用到O(1)的額外空間的排序),因而在從後向前掃描過程中,需要反複把已排序元素逐漸向後挪位,為最新元素提供插入空間。

3.1 算法描述

一般來說,插入排序都采用in-place在數組上實作。具體算法描述如下:

  • 從第一個元素開始,該元素可以認為已經被排序;
  • 取出下一個元素,在已經排序的元素序列中從後向前掃描;
  • 如果該元素(已排序)大于新元素,将該元素移到下一位置;
  • 重複步驟3,直到找到已排序的元素小于或者等于新元素的位置;
  • 将新元素插入到該位置後;
  • 重複步驟2~5。

3.2 動圖示範

1

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

2.更直覺點

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

3.2 代碼實作

/**
    * 插入排序
    * @param array
    * @return
    */
   public static int[] insertionSort(int[] array) {
       if (array.length == 0)
           return array;
       int current;
       for (int i = 0; i < array.length - 1; i++) {
           current = array[i + 1];
           int preIndex = i;
           while (preIndex >= 0 && current < array[preIndex]) {
               array[preIndex + 1] = array[preIndex];
               preIndex--;
           }
           array[preIndex + 1] = current;
       }
       return array;
   }      

3.4 算法分析

最佳情況:T(n) = O(n) 最壞情況:T(n) = O(n2) 平均情況:T(n) = O(n2)

4、希爾排序(Shell Sort)

希爾排序是希爾(Donald Shell)于1959年提出的一種排序算法。希爾排序也是一種插入排序,它是簡單插入排序經過改進之後的一個更高效的版本,也稱為縮小增量排序,同時該算法是沖破O(n2)的第一批算法之一。它與插入排序的不同之處在于,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。

希爾排序是把記錄按下表的一定增量分組,對每組使用直接插入排序算法排序;随着增量逐漸減少,每組包含的關鍵詞越來越多,當增量減至1時,整個檔案恰被分成一組,算法便終止。

4.1 算法描述

我們來看下希爾排序的基本步驟,在此我們選擇增量gap=length/2,縮小增量繼續以gap = gap/2的方式,這種增量選擇我們可以用一個序列來表示,{n/2,(n/2)/2...1},稱為增量序列。希爾排序的增量序列的選擇與證明是個數學難題,我們選擇的這個增量序列是比較常用的,也是希爾建議的增量,稱為希爾增量,但其實這個增量序列不是最優的。此處我們做示例使用希爾增量。

先将整個待排序的記錄序列分割成為若幹子序列分别進行直接插入排序,具體算法描述:

  • 選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  • 按增量序列個數k,對序列進行k 趟排序;
  • 每趟排序,根據對應的增量ti,将待排序列分割成若幹長度為m 的子序列,分别對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。

4.2.1 動圖示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

4.2.2 過程示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

4.3 代碼實作

/**
    * 希爾排序
    *
    * @param array
    * @return
    */
   public static int[] ShellSort(int[] array) {
       int len = array.length;
       int temp, gap = len / 2;
       while (gap > 0) {
           for (int i = gap; i < len; i++) {
               temp = array[i];
               int preIndex = i - gap;
               while (preIndex >= 0 && array[preIndex] > temp) {
                   array[preIndex + gap] = array[preIndex];
                   preIndex -= gap;
               }
               array[preIndex + gap] = temp;
           }
           gap /= 2;
       }
       return array;
   }      

4.4 算法分析

最佳情況:T(n) = O(nlog2 n) 最壞情況:T(n) = O(nlog2 n) 平均情況:T(n) =O(nlog2n) 

5、歸并排序(Merge Sort)

和選擇排序一樣,歸并排序的性能不受輸入資料的影響,但表現比選擇排序好的多,因為始終都是O(n log n)的時間複雜度。代價是需要額外的記憶體空間。

歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。歸并排序是一種穩定的排序方法。将已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若将兩個有序表合并成一個有序表,稱為2-路歸并。

5.1 算法描述

  • 把長度為n的輸入序列分成兩個長度為n/2的子序列;
  • 對這兩個子序列分别采用歸并排序;
  • 将兩個排序好的子序列合并成一個最終的排序序列。

5.2 動圖示範

1

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

2

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

5.3 代碼實作

/**
    * 歸并排序
    *
    * @param array
    * @return
    */
   public static int[] MergeSort(int[] array) {
       if (array.length < 2) return array;
       int mid = array.length / 2;
       int[] left = Arrays.copyOfRange(array, 0, mid);
       int[] right = Arrays.copyOfRange(array, mid, array.length);
       return merge(MergeSort(left), MergeSort(right));
   }
   /**
    * 歸并排序——将兩段排序好的數組結合成一個排序數組
    *
    * @param left
    * @param right
    * @return
    */
   public static int[] merge(int[] left, int[] right) {
       int[] result = new int[left.length + right.length];
       for (int index = 0, i = 0, j = 0; index < result.length; index++) {
           if (i >= left.length)
               result[index] = right[j++];
           else if (j >= right.length)
               result[index] = left[i++];
           else if (left[i] > right[j])
               result[index] = right[j++];
           else
               result[index] = left[i++];
       }
       return result;
   }      

5. 4 算法分析

最佳情況:T(n) = O(n) 最差情況:T(n) = O(nlogn) 平均情況:T(n) = O(nlogn)

6、快速排序(Quick Sort)

快速排序的基本思想:通過一趟排序将待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分别對這兩部分記錄繼續進行排序,以達到整個序列有序。

6.1 算法描述

快速排序使用分治法來把一個串(list)分為兩個子串(sub-lists)。具體算法描述如下:

  • 從數列中挑出一個元素,稱為 “基準”(pivot);
  • 重新排序數列,所有元素比基準值小的擺放在基準前面,所有元素比基準值大的擺在基準的後面(相同的數可以到任一邊)。在這個分區退出之後,該基準就處于數列的中間位置。這個稱為分區(partition)操作;
  • 遞歸地(recursive)把小于基準值元素的子數列和大于基準值元素的子數列排序。

6.2 動圖示範

1 歡快點的

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

2 正經點的

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

6.3 代碼實作

/**
    * 快速排序方法
    * @param array
    * @param start
    * @param end
    * @return
    */
   public static int[] QuickSort(int[] array, int start, int end) {
       if (array.length < 1 || start < 0 || end >= array.length || start > end) return null;
       int smallIndex = partition(array, start, end);
       if (smallIndex > start)
           QuickSort(array, start, smallIndex - 1);
       if (smallIndex < end)
           QuickSort(array, smallIndex + 1, end);
       return array;
   }
   /**
    * 快速排序算法——partition
    * @param array
    * @param start
    * @param end
    * @return
    */
   public static int partition(int[] array, int start, int end) {
       int pivot = (int) (start + Math.random() * (end - start + 1));
       int smallIndex = start - 1;
       swap(array, pivot, end);
       for (int i = start; i <= end; i++)
           if (array[i] <= array[end]) {
               smallIndex++;
               if (i > smallIndex)
                   swap(array, i, smallIndex);
           }
       return smallIndex;
   }

   /**
    * 交換數組内兩個元素
    * @param array
    * @param i
    * @param j
    */
   public static void swap(int[] array, int i, int j) {
       int temp = array[i];
       array[i] = array[j];
       array[j] = temp;
   }      

6.4 算法分析

最佳情況:T(n) = O(nlogn) 最差情況:T(n) = O(n2) 平均情況:T(n) = O(nlogn) 

7、堆排序(Heap Sort)

堆排序(Heapsort)是指利用堆這種資料結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。

7.1 算法描述

  • 将初始待排序關鍵字序列(R1,R2….Rn)建構成大頂堆,此堆為初始的無序區;
  • 将堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,……Rn-1)和新的有序區(Rn),且滿足R[1,2…n-1]<=R[n];
  • 由于交換後新的堆頂R[1]可能違反堆的性質,是以需要對目前無序區(R1,R2,……Rn-1)調整為新堆,然後再次将R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2….Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數為n-1,則整個排序過程完成。

7.2 動圖示範

1 歡樂點的

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

2 正經點的

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

7.3 代碼實作

注意:這裡用到了完全二叉樹的部分性質。

//聲明全局變量,用于記錄數組array的長度;
static int len;
   /**
    * 堆排序算法
    *
    * @param array
    * @return
    */
   public static int[] HeapSort(int[] array) {
       len = array.length;
       if (len < 1) return array;
       //1.建構一個最大堆
       buildMaxHeap(array);
       //2.循環将堆首位(最大值)與末位交換,然後在重新調整最大堆
       while (len > 0) {
           swap(array, 0, len - 1);
           len--;
           adjustHeap(array, 0);
       }
       return array;
   }
   /**
    * 建立最大堆
    *
    * @param array
    */
   public static void buildMaxHeap(int[] array) {
       //從最後一個非葉子節點開始向上構造最大堆
       for (int i = (len - 1) / 2; i >= 0; i--) {
           adjustHeap(array, i);
       }
   }
   /**
    * 調整使之成為最大堆
    *
    * @param array
    * @param i
    */
   public static void adjustHeap(int[] array, int i) {
       int maxIndex = i;
       //如果有左子樹,且左子樹大于父節點,則将最大指針指向左子樹
       if (i * 2 < len && array[i * 2] > array[maxIndex])
           maxIndex = i * 2;
       //如果有右子樹,且右子樹大于父節點,則将最大指針指向右子樹
       if (i * 2 + 1 < len && array[i * 2 + 1] > array[maxIndex])
           maxIndex = i * 2 + 1;
       //如果父節點不是最大值,則将父節點與最大值交換,并且遞歸調整與父節點交換的位置。
       if (maxIndex != i) {
           swap(array, maxIndex, i);
           adjustHeap(array, maxIndex);
       }
   }      

7.4 算法分析

最佳情況:T(n) = O(nlogn) 最差情況:T(n) = O(nlogn) 平均情況:T(n) = O(nlogn)

8、計數排序(Counting Sort)

計數排序的核心在于将輸入的資料值轉化為鍵存儲在額外開辟的數組空間中。作為一種線性時間複雜度的排序,計數排序要求輸入的資料必須是有确定範圍的整數。

計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等于i的元素的個數。然後根據數組C來将A中的元素排到正确的位置。它隻能對整數進行排序。

8.1 算法描述

  • 找出待排序的數組中最大和最小的元素;
  • 統計數組中每個值為i的元素出現的次數,存入數組C的第i項;
  • 對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加);
  • 反向填充目标數組:将每個元素i放在新數組的第C(i)項,每放一個元素就将C(i)減去1。

8.2 動圖示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

8.3 代碼實作

/**
    * 計數排序
    *
    * @param array
    * @return
    */
   public static int[] CountingSort(int[] array) {
       if (array.length == 0) return array;
       int bias, min = array[0], max = array[0];
       for (int i = 1; i < array.length; i++) {
           if (array[i] > max)
               max = array[i];
           if (array[i] < min)
               min = array[i];
       }
       bias = 0 - min;
       int[] bucket = new int[max - min + 1];
       Arrays.fill(bucket, 0);
       for (int i = 0; i < array.length; i++) {
           bucket[array[i] + bias]++;
       }
       int index = 0, i = 0;
       while (index < array.length) {
           if (bucket[i] != 0) {
               array[index] = i - bias;
               bucket[i]--;
               index++;
           } else
               i++;
       }
       return array;
   }      

8.4 算法分析

當輸入的元素是n 個0到k之間的整數時,它的運作時間是 O(n + k)。計數排序不是比較排序,排序的速度快于任何比較排序算法。由于用來計數的數組C的長度取決于待排序數組中資料的範圍(等于待排序數組的最大值與最小值的差加上1),這使得計數排序對于資料範圍很大的數組,需要大量時間和記憶體。

最佳情況:T(n) = O(n+k) 最差情況:T(n) = O(n+k) 平均情況:T(n) = O(n+k)

9、桶排序(Bucket Sort)

桶排序是計數排序的更新版。它利用了函數的映射關系,高效與否的關鍵就在于這個映射函數的确定。

桶排序 (Bucket sort)的工作的原理:假設輸入資料服從均勻分布,将資料分到有限數量的桶裡,每個桶再分别排序(有可能再使用别的排序算法或是以遞歸方式繼續使用桶排序進行排

9.1 算法描述

  • 人為設定一個BucketSize,作為每個桶所能放置多少個不同數值(例如當BucketSize==5時,該桶可以存放{1,2,3,4,5}這幾種數字,但是容量不限,即可以存放100個3);
  • 周遊輸入資料,并且把資料一個一個放到對應的桶裡去;
  • 對每個不是空的桶進行排序,可以使用其它排序方法,也可以遞歸使用桶排序;
  • 從不是空的桶裡把排好序的資料拼接起來。

注意,如果遞歸使用桶排序為各個桶排序,則當桶數量為1時要手動減小BucketSize增加下一循環桶的數量,否則會陷入死循環,導緻記憶體溢出。

9.2 圖檔示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

9.3 代碼實作

/**
    * 桶排序
    *
    * @param array
    * @param bucketSize
    * @return
    */
   public static ArrayList<Integer> BucketSort(ArrayList<Integer> array, int bucketSize) {
       if (array == null || array.size() < 2)
           return array;
       int max = array.get(0), min = array.get(0);
       // 找到最大值最小值
       for (int i = 0; i < array.size(); i++) {
           if (array.get(i) > max)
               max = array.get(i);
           if (array.get(i) < min)
               min = array.get(i);
       }
       int bucketCount = (max - min) / bucketSize + 1;
       ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketCount);
       ArrayList<Integer> resultArr = new ArrayList<>();
       for (int i = 0; i < bucketCount; i++) {
           bucketArr.add(new ArrayList<Integer>());
       }
       for (int i = 0; i < array.size(); i++) {
           bucketArr.get((array.get(i) - min) / bucketSize).add(array.get(i));
       }
       for (int i = 0; i < bucketCount; i++) {
           if (bucketCount == 1)
               bucketSize--;
           ArrayList<Integer> temp = BucketSort(bucketArr.get(i), bucketSize);
           for (int j = 0; j < temp.size(); j++)
               resultArr.add(temp.get(j));
       }
       return resultArr;
   }      

9.4 算法分析

桶排序最好情況下使用線性時間O(n),桶排序的時間複雜度,取決與對各個桶之間資料進行排序的時間複雜度,因為其它部分的時間複雜度都為O(n)。很顯然,桶劃分的越小,各個桶之間的資料越少,排序所用的時間也會越少。但相應的空間消耗就會增大。

最佳情況:T(n) = O(n+k) 最差情況:T(n) = O(n+k) 平均情況:T(n) = O(n2) 

10、基數排序(Radix Sort)

基數排序也是非比較的排序算法,對每一位進行排序,從最低位開始排序,複雜度為O(kn),為數組長度,k為數組中的數的最大的位數;

基數排序是按照低位先排序,然後收集;再按照高位排序,然後再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最後的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。基數排序基于分别排序,分别收集,是以是穩定的。

10.1 算法描述

  • 取得數組中的最大數,并取得位數;
  • arr為原始數組,從最低位開始取每個位組成radix數組;
  • 對radix進行計數排序(利用計數排序适用于小範圍數的特點);

10.2 動圖示範

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

10.3 代碼實作

/**
    * 基數排序
    * @param array
    * @return
    */
   public static int[] RadixSort(int[] array) {
       if (array == null || array.length < 2)
           return array;
       // 1.先算出最大數的位數;
       int max = array[0];
       for (int i = 1; i < array.length; i++) {
           max = Math.max(max, array[i]);
       }
       int maxDigit = 0;
       while (max != 0) {
           max /= 10;
           maxDigit++;
       }
       int mod = 10, div = 1;
       ArrayList<ArrayList<Integer>> bucketList = new ArrayList<ArrayList<Integer>>();
       for (int i = 0; i < 10; i++)
           bucketList.add(new ArrayList<Integer>());
       for (int i = 0; i < maxDigit; i++, mod *= 10, div *= 10) {
           for (int j = 0; j < array.length; j++) {
               int num = (array[j] % mod) / div;
               bucketList.get(num).add(array[j]);
           }
           int index = 0;
           for (int j = 0; j < bucketList.size(); j++) {
               for (int k = 0; k < bucketList.get(j).size(); k++)
                   array[index++] = bucketList.get(j).get(k);
               bucketList.get(j).clear();
           }
       }
       return array;
   }      

10.4 算法分析

最佳情況:T(n) = O(n * k) 最差情況:T(n) = O(n * k) 平均情況:T(n) = O(n * k)

基數排序有兩種方法:

MSD 從高位開始進行排序 LSD 從低位開始進行排序

基數排序 vs 計數排序 vs 桶排序

這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差異:

  • 基數排序:根據鍵值的每位數字來配置設定桶
  • 計數排序:每個桶隻存儲單一鍵值
  • 桶排序:每個桶存儲一定範圍的數值

總結

各種排序的穩定性,時間複雜度、空間複雜度、穩定性總結如下圖:

資料結構與算法:動态圖解十大經典排序算法(含JAVA代碼實作)

關于時間複雜度:

(1)平方階(O(n2))排序

各類簡單排序:直接插入、直接選擇和冒泡排序;

(2)線性對數階(O(nlog2n))排序

快速排序、堆排序和歸并排序;

(3)O(n1+§))排序,§是介于0和1之間的常數。

希爾排序

(4)線性階(O(n))排序

基數排序,此外還有桶、箱排序。

關于穩定性:

穩定的排序算法:冒泡排序、插入排序、歸并排序和基數排序

不是穩定的排序算法:選擇排序、快速排序、希爾排序、堆排序