[內排序]八大經典排序合集

1 排序的基本概念

  假定排序的數據是由一組元素組成的表,而元素由若干個數據項組成,其中有一項可用來標識該元素,稱爲關鍵字項,其值稱爲關鍵字。關鍵字可用做排序運算的依據。算法

  1.1 什麼是排序

  所謂排序,就是整理表中的元素,使之按關鍵字遞增或遞減的順序排列。shell

  1.2 排序的穩定性

  當待排序元素的關鍵字各不相同時,排序的結果是惟一的,不然排序的結果不必定惟一。數組

  若是待排序的表中,存在多關鍵字相同的元素,通過排序後這些具備相同關鍵字的元素之間的相對次序保持不變,則稱這種排序方法是穩定的;反之,若具備相同關鍵字的元素之間的相對次序發生變化,則稱這種排序方法是不穩定的工具

  注意,排序算法的穩定性是針對全部輸入實例而言的。也就是說,在全部可能的輸入實例中,只要有一個實例使得算法不知足穩定性要求,則該排序算法就是不穩定的。性能

  1.3 內排序和外排序

  在排序過程當中,若整個表都是放在內存中處理,排序時不涉及內、外存數據的交換,則稱之爲內排序;反之,若排序過程當中要進行內。外存數據的交換,則稱之爲外排序ui

  內排序適用於元素個數不是不少的小表,外排序則適用於元素個數不少,不能一次將所有元素放入內存的大表。spa

  按所用的策略不一樣,排序方法還能夠分爲須要關鍵字比較和不須要關鍵字比較兩類。須要關鍵字比較的排序方法有插入排序。選擇排序。交換排序和歸併排序;不須要關鍵字比較的排序方法有基數排序。code

 

圖1.1 排序的分類blog

2 插入排序

  插入排序基本思想:每次將一個待排序的元素,按其關鍵字大小插入到已經排好序的子表中的適當位置,直到所有元素插入完成爲止。 排序

  2.1 直接插入排序

  1. 排序思路

  假設待排序的元素存放在數組 Data[0..n-1] 中,排序過程當中的某一時刻,Data 被劃分紅兩個子區間 Data[0..i-1] 和 Data[i..n-1](剛開始時 i=1,有序區只有 Data[0] 一個元素),其中,前一個子區間是已排好序的有序區,後一個子區間則是當前未排序的部分,稱其爲無序區。直接插入排序的一趟操做是將當前無序區的開頭元素 Data[i] (1 <= i <= n-1)插入到有序區 Data[0..i-1] 中適當的位置上,使 Data[0..i] 變爲新的有序區,如圖2.1所示。這種方法一般稱爲增量法,由於它每趟操做使有序區增長一個元素。

  

  說明:直接插入排序每趟產生的有序區並必定是全局有序區,也就是說有序區中的元素並不必定放在其最終的位置上。當一個元素在整個排序結束前就已經放在其最終的位置上,稱爲歸位。

  2. 直接插入排序實例

  設待排序的表有 10 個元素,其關鍵字爲 {9,8,7,6,5,4,3,2,1,0}。如下爲直接插入排序的排序過程:

 

  圖中用方括號表示每趟操做後的有序區。每趟向有序區中插入一個元素(用方框表示),並保持有序區中的元素仍有序。

  3. 排序算法

 1 public class InsertSort {
 2     public static void insertSort(int[] Data) {
 3         int temp , j;
 4         for(int i = 0; i < Data.length; i++) {
 5             temp = Data[i];
 6             j = i-1;                        //從右向左在有序區Data[0..i-1]中找Data[i]的插入位置
 7             while(j >= 0 && temp < Data[j]) {
 8                 Data[j+1] = Data[j];        //將大於Data[i]的元素後移
 9                 j--;
10             }
11             Data[j+1] = temp;                //在j+1處插入Data[i]
12         }
13     }
14     
15     public static void main(String[] args) {
16         int[] Data = {9,8,7,6,5,4,3,2,1,0};
17         
18         insertSort(Data);
19         
20         System.out.println("直接插入排序的結果:");
21         for(int i = 0; i < Data.length; i++) {
22             System.out.print(Data[i]+" ");
23         }
24     }
25 }

  4. 算法分析

  時間複雜度:O(n2);

  空間複雜度:O(1);

  穩定性:穩定;

  複雜性:簡單。

  5. 折半插入排序 / 二分插入排序

  5.1 排序思路

  直接插入排序將無序區中開頭元素 Data[i](1 <= i <= n-1)插入到有序區 Data[0..i-1]中,此時能夠採用折半查找方法先在 Data[0..i-1] 中找到插入的位置,再經過移動元素進行插入。

  在Data[low..high](初始時 low=0,high=i-1)中採用折半查找插入 Data[i] 的位置爲 Data[high+1],再將 Data[high+1..i-1] 中的元素後移一個位置,並置 Data[high+1] = Data[i]。

  說明:和直接插入排序同樣,折半插入排序每趟產生的有序區並不必定是全局有序區。

  5.2 排序算法

 1 public class HalfInsertSort {
 2     public static void halfInsertSort(int[] Data) {
 3         int low , high , mid;
 4         int temp;
 5         for(int i = 1; i < Data.length; i++) {
 6             temp = Data[i];                //將Data[i]保存在temp中
 7             low = 0;
 8             high = i-1;
 9             while( low <= high) {        //在Data[low..high]中折半查找有序插入的位置
10                 mid = (low+high) / 2;    //取中間位置
11                 if(temp < Data[mid]) {
12                     high = high-1;        //插入點在左半區
13                 }else {
14                     low = mid + 1;        //插入點在右半區
15                 }
16             }
17             for(int j = i-1; j >= high+1; j--) {
18                 Data[j+1] = Data[j];    //元素後移
19             }
20             Data[high+1] = temp;        //插入在Data[high+1]位置
21         }
22     }
23     
24     public static void main(String[] args) {
25         int[] Data = {9,8,7,6,5,4,3,2,1,0};
26         
27         halfInsertSort(Data);
28         
29         System.out.println("折半插入排序的結果:");
30         for(int i = 0; i < Data.length; i++) {
31             System.out.print(Data[i]+" ");
32         }
33     }
34 }

   5.3 算法分析

  從上述算法中看到,當初始數據序列爲正序時,關鍵字的比較次數並不能減小;當初始數據序列爲反序時,關鍵字的比較次數也不會增長。

  折半插入排序的元素移動次數與直接插入排序相同,不一樣的僅是變分散移動爲集合移動。

  就平均性能而言,折半查找優於順序查找,因此折半插入排序優於直接插入排序。

  時間複雜度爲:O(n2);

  空間複雜度爲:O(1); 

  穩定性:穩定;

  複雜性:簡單。

  2.2 希爾排序

   1. 排序思路

  先取定一個小於數組長度 Data.length 的整數 gap(gap = Data.length / 2)做爲第一個增量,把表的所有元素分紅 gap 個組,全部相互之間距離爲 gap 的倍數的元素放在同一個組中,在各組內進行直接插入排序;而後,減少增量 gap(gap = gap /2),重複上述的分組和排序過程,直至增量減少爲 0,即全部元素放在同一組中進行直接插入排序。

  說明:希爾排序每趟並不產生有序區,在最後一趟排序結束前,全部元素並不必定歸位。可是每趟排序完成後,數據愈來愈接近有序。

   2. 希爾排序實例

  設待排序的表有 10 個元素,其關鍵字爲 {9,8,7,6,5,4,3,2,1,0}。如下爲直接插入排序的排序過程:

  第一趟排序時,d = 5 ,整個表被分紅 5 組:(9,4),(8,3),(7,2),(6,1),(5,0),各組採用直接插入排序方法變成有序的,即結果分別爲(4,9),(3,8),(2,7),(1,6)(0,5)。

  第二趟排序時,d = 2,整個表分爲兩組:(4,2,0,8,6)和(3,1,9,7,5),各組採用直接插入排序方法變成有序的,即結果分別爲(0,2,4,6,8)和(1,3,5,7,9)。

  第三趟排序時,d = 1,整個表爲一組,採用直接插入排序方法使整個數列有序,最終結果爲(0,1,2,3,4,5,6,7,8,9)。

   3. 排序算法

 1 public class ShellSort {
 2     public static void shellSort(int[] Data) {
 3         int gap = Data.length / 2;                //增量置初值
 4         int temp = 0 , j = 0;
 5         while(gap > 0) {
 6             for(int i = gap; i < Data.length; i++) {    //對全部相隔gap位置的元素組採用直接插入排序
 7                 temp = Data[i];
 8                 j = i - gap;
 9                 while(j >= 0 && temp < Data[j]) {        //對相隔gap位置的元素組進行排序
10                     Data[j+gap] = Data[j];
11                     j -= gap;
12                 }
13                 Data[j+gap] = temp;
14             }
15             gap /= 2;                            //減少增量
16         }
17     }
18     
19     public static void main(String[] args) {
20         int[] Data = {9,8,7,6,5,4,3,2,1,0};
21         
22         shellSort(Data);
23         System.out.println("希爾排序的結果:");
24         for(int i = 0; i < Data.length; i++) {
25             System.out.print(Data[i]+" ");
26         }
27     }
28 }

   4. 算法分析

  時間複雜度:O(n1.3);

  空間複雜度:O(1);

  穩定性:不穩定;

  複雜性:較複雜。

 3 交換排序

  3.1 冒泡排序

   1. 排序思路

  經過無序區中相鄰元素間關鍵字的比較和位置的交換,使關鍵字最小的元素如氣泡通常逐漸往上「漂浮」直至「水面」。整個算法是從最下面的元素開始,對每兩個相鄰的關鍵字進行比較,且使關鍵字較小的元素換相當鍵字較大的元素之上,使得通過一趟冒泡排序後,關鍵字最小的元素到達最上端。接着,再在剩下的元素中找關鍵字次小的元素,並把它交換到第二個位置上。依次類推,一直到全部元素都有序爲止。

  說明:冒泡排序每趟產生的有序區必定是全局有序區,也就是說每趟產生的有序區中全部元素都歸位了。

   2. 冒泡排序實例

  設待排序的表有 10 個元素,其關鍵字爲 {9,8,7,6,5,4,3,2,1,0}。如下爲冒泡排序的排序過程:

  每次從無序區中冒出一個關鍵字最小的元素(用方框表示)並將其定位。

   3. 排序算法

 1 public class BubbleSort {
 2     public static void bubbleSort(int[] Data) {
 3         int temp = 0;
 4         for(int i = 0; i < Data.length-1; i++) {
 5             for(int j = Data.length-1 ; j > i; j--) {    //比較,找出本趟關鍵字最小的元素
 6                 if(Data[j] < Data[j-1]) {
 7                     temp = Data[j];                //Data[j-1]與Data[j]進行交換,將關鍵字最小的元素前移
 8                     Data[j] = Data[j-1];
 9                     Data[j-1] = temp;
10                 }
11             }
12         }
13     }
14     
15     public static void main(String[] args) {
16         int[] Data = {9,8,7,6,5,4,3,2,1,0};
17         
18         bubbleSort(Data);
19         System.out.println("冒泡排序的結果:");
20         for(int i = 0; i < Data.length; i++) {
21             System.out.print(Data[i]+" ");
22         }
23     }
24 
25 }

  4. 算法分析

  時間複雜度爲:O(n2);

  空間複雜度爲:O(1);

  穩定性:穩定;

  複雜性:簡單。

   5. 改進的冒泡排序算法

  在有些狀況下,在第 i (i < n-1)趟時就已經排好序了,但算法仍執行後面幾趟的比較。實際上,一旦算法中某一趟比較時沒有出現任何元素交換,說明已排好序了,就能夠結束本算法。爲此,改進冒泡排序算法以下:

 

 1 public class ImproveBubbleSort {    
 2     public static void improveBubbleSort(int[] Data) {
 3         boolean exchange;
 4         int temp = 0;
 5         for(int i = 0; i < Data.length-1; i++) {
 6             exchange = false;
 7             for(int j = Data.length-1; j > i; j--) {    //比較,找出本趟關鍵字最小的元素
 8                 if(Data[j] < Data[j-1]) {
 9                     temp = Data[j];                //Data[j-1]與Data[j]進行交換,將關鍵字最小的元素前移
10                     Data[j] = Data[j-1];
11                     Data[j-1] = temp;
12                     exchange = true;
13                 }
14             }
15             if(!exchange)                        //本趟沒有發生交換,中途結束算法
16                 return;
17         }
18     }
19     
20     public static void main(String[] args) {
21         int[] Data = {9,8,7,6,5,4,3,2,1,0};
22         
23         improveBubbleSort(Data);
24         System.out.println("改進冒泡排序的結果:");
25         for(int i = 0; i < Data.length; i++) {
26             System.out.print(Data[i]+" ");
27         }
28     }
29 }

  3.2 快速排序

   1. 排序思路

  在待排序的 n 個元素中任取一個元素(一般取第一個元素)做爲基準,把該元素放入適當的位置後,數據序列被此元素分紅兩部分,全部關鍵字比該元素關鍵字小的元素放置在前一部分,全部關鍵字比該元素關鍵字大的元素放置在後一部分,並把該元素排在這兩部分的中間(稱該元素歸位),這個過程稱作一趟快速排序,以後對全部劃分出來的兩部分分別重複上述過程,直至每部份內只有一個元素或爲空爲止。

  說明:快速排序每趟僅將一個元素歸位。

   2. 快速排序實例

  設待排序的表有 10 個元素,其關鍵字爲 {6,8,7,9,0,1,3,2,4,5}。如下爲快速排序的排序過程:

  其排序過程如圖 3.4 所示(最後結果圖(g)稱爲快速排序遞歸樹)。第 1 趟是以 6 爲基準將整個區間分爲(5,4,2,3,0,1)和(9,7,8)兩個子區間,並將 6 歸位;對於每一個子區間,又進行一樣的排序,直到該子區間只有一個元素或不存在元素爲止。

   3. 排序算法

 1 public class QuickSort {
 2     public static void quickSort(int[] Data , int s , int t) {
 3         int i = s , j = t;                        //對Data[s]至Data[t]的元素進行快速排序
 4         int temp = 0;
 5         if(s < t) {                                //區間內至少存在兩個元素的狀況
 6             temp = Data[s];                        //用區間的第1個元素做爲基準
 7             while(i != j) {                        //從區間兩端交替向中間掃描,直至i = j爲止
 8                 while(j > i && Data[j] >= temp)
 9                     j--;                        //從右向左掃描,找第1個小於temp的元素Data[j]
10                 Data[i] = Data[j];                //找到這樣的Data[j],Data[i]與Data[j]交換
11                 while(i < j && Data[i] <= temp)
12                     i++;                        //從左向右掃描,找第1個大於temp的元素Data[j]
13                 Data[j] = Data[i];                //找到這樣的Data[i],Data[i]與Data[j]交換
14             }
15             Data[i] = temp;
16             quickSort(Data, s, i-1);            //對左區間遞歸排序
17             quickSort(Data, i+1, t);            //對右區間遞歸排序
18         }
19     }
20 
21     public static void main(String[] args) {
22         int[] Data = {6,8,7,9,0,1,3,2,4,5};
23         
24         quickSort(Data, 0, Data.length-1);
25         System.out.println("快速排序的結果:");
26         for(int i = 0; i < Data.length; i++) {
27             System.out.print(Data[i]+" ");
28         }
29     }
30 }

   4. 算法分析

  時間複雜度:O(nlog2n);

  空間複雜度:O(log2n);

  穩定性:不穩定;

  複雜性:較複雜。

4 選擇排序

  4.1 直接選擇排序

   1. 排序思路

  第 i 趟排序開始時,當前有序區和無序區分別爲 Data[0..i-1] 和 Data[i..n-1](0 <= i < n-1),該趟排序是從當前無序區中選出關鍵字最小的元素 Data[k],將它與無序區的第 1 個元素 Data[i] 交換,使 Data[0..i] 和 Data[i+1..n-1] 分別變爲新的有序區和新的無序區,如圖4.1所示。由於每趟排序均使有序區中增長一個元素,且有序區中的元素關鍵字均不大於無序區中元素的關鍵字,即第 i 趟排序以後,Data[0..i] 中的關鍵字小於 Data[i+1..n-1] 中的全部關鍵字,因此進行 n-1 趟排序以後有 Data[0..n-2] 中的全部關鍵字小於等於 Data[n-1],也就是說,通過 n-1 趟排序以後,整個表 Data[0..n-1] 遞增有序。

  說明:直接選擇排序每趟產生的有序區必定是全局有序區,也就是說每趟產生的有序區中全部元素都歸位了。

     2. 直接選擇排序實例

      設待排序的表有 10 個元素,其關鍵字爲 {6,8,7,9,0,1,3,2,4,5}。如下爲直接選擇排序的排序過程:

      每趟選擇出一個元素(帶方框者)。

   3. 排序算法

 1 public class SelectSort {
 2     public static void selectSort(int[] Data) {
 3         int k = 0;
 4         int temp = 0;
 5         for(int i = 0; i < Data.length-1; i++) {     //作第i趟排序
 6             k = i;
 7             for(int j = i+1; j < Data.length; j++) { //在當前無序區Data[i..n-1]中選最小的Data[k]
 8                 if(Data[j] < Data[k])
 9                     k = j;                             //k記下目前找到的最小關鍵字所在的位置
10             }
11             if(k != i) {                             //交換Data[i]和Data[k]
12                 temp = Data[i];
13                 Data[i] = Data[k];
14                 Data[k] = temp;
15             }
16         }
17     }
18     
19     public static void main(String[] args) {
20         int[] Data = {6,8,7,9,0,1,3,2,4,5};
21         
22         selectSort(Data);
23         System.out.println("直接選擇排序的結果:");
24         for(int i = 0; i < Data.length; i++) {
25             System.out.print(Data[i]+" ");
26         }
27     }
28 
29 }

     4.算法分析

  時間複雜度:O(n2);

  空間複雜度:O(1);

  穩定性:不穩定;

  複雜性:簡單。

  4.2 堆排序

     1. 排序思路

  堆排序是一種樹形選擇排序方法,它的特色是,在排序過程當中,將 Data[0..n-1] 當作是一棵徹底二叉樹的順序存儲結構,利用徹底二叉樹中雙親節點和孩子節點之間的內在關係,在當前無序區中選擇關鍵字最大(或最小)的元素。

      堆排序的排序過程當中每次挑選最大元素歸位,如圖4.3所示。挑選最大元素的方法是將數組中存儲的數據當作是一棵徹底二叉樹,利用徹底二叉樹中雙親節點和孩子節點之間的內在關係來選擇關鍵字最大的元素。具體的作法是:把待排序的表的關鍵字存放在數組Data[0..n-1] 之中,將 Data 看作一棵二叉樹,每一個節點表示一個元素,源表的第一個元素Data[0] 做爲二叉樹的根,如下各元素 Data[1..n-1] 依次逐層從左到右順序排列,構成一棵徹底二叉樹,節點 Data[i] 的左孩子是 Data[2i+1],右孩子是 Data[2i+2],雙親是 Data[(i+1)/2-1]。

 

      說明:堆排序每趟產生的有序區必定是全局有序區,也就是說每趟產生的有序區中全部元素都歸位了。

     2. 直接選擇排序實例

      設待排序的表有 10 個元素,其關鍵字爲 {6,8,7,9,0,1,3,2,4,5}。如下爲堆排序的排序過程:

  其初始狀態如圖4.4(a)所示,經過第一個 for 循環調用 sift() 產生的初始堆如圖4.4(b)所示,這時 Data 中關鍵字序列爲{9,8,7,6,5,1,3,2,4,0}。

 

      堆排序的排序過程如圖4.5所示,每輸出一個元素,就對堆進行一次篩選調整。

   3. 排序算法

 1 public class HeapSort {
 2     public static void heapSort(int[] Data , int n) {
 3         int temp = 0;
 4         for(int i = (n+1)/2-1; i >= 0; i--)    //循環創建初始堆,第一個雙親節點的位置序號爲(n+1)/2-1
 5             sift(Data , i , n-1);
 6         for(int i = n-1; i >= 1; i--) {        //進行n-1趟堆排序,每一趟堆排序的元素個數減1
 7             temp= Data[0];                    //將最後一個元素同當前區間內Data[0]對換
 8             Data[0] = Data[i];
 9             Data[i] = temp;
10             sift(Data , 0 , i-1);            //篩選Data[0]節點,獲得i個節點的堆
11         }
12     }
13     
14     // 調整堆
15     private static void sift(int[] Data, int low, int high) {
16         int place = 2 * low + 1;            //該節點的左孩子在數組中的位置序號
17         int temp = Data[low];                //保存當前節點
18         while(place <= high) {
19             if(place+1 <= high && Data[place] < Data[place+1])    //若右孩子較大,則把place指向右孩子
20                 place++; 
21             if(temp < Data[place]) {
22                 Data[low] = Data[place];    //將Data[place]調整到雙親節點位置上
23                 low = place;                //修改low和place值,以便繼續向下篩選
24                 place = 2 * low+1;
25             } else
26                 break;                        //篩選結束
27         }
28         Data[low] = temp;                    //被篩選節點的值放入最終位置
29     }
30     
31     public static void main(String[] args) {
32         int[] Data = {6,8,7,9,0,1,3,2,4,5};
33         
34         heapSort(Data,Data.length);
35         System.out.println("堆排序的結果:");
36         for(int i = 0; i < Data.length; i++) {
37             System.out.print(Data[i]+" ");
38         }
39     }
40 
41 }

     4.算法分析

  時間複雜度:O(nlog2n);

  空間複雜度:O(1);

  穩定性:不穩定;

  複雜性:較複雜。

5 歸併排序

   1. 排序思路

  歸併排序是屢次將兩個或兩個以上的有序表合併成一個新的有序表。最簡單的歸併排是直接將兩個有序的子表合併成一個有序的表即二路歸併。

      將 Data[0..n-1] 當作是 n 個長度爲 1 的有序序列,而後進行亮亮歸併,獲得 [n/2] 個長度爲 2(最後一個有序序列的長度可能爲 1)的有序序列,再進行兩兩歸併,獲得 [n/4] 個長度爲 4(最後一個有序序列的長度可能小於 4)的有序序列,…… ,直到獲得一個長度爲 n 的有序序列。

      說明:歸併排序每趟產生的有序區只是局部有序的,也就是說在最後一趟排序結束前,全部元素並不必定歸位了。

   2. 歸併排序實例

  設待排序的表有 10 個元素,其關鍵字爲 {18,2,20,34,12,32,6,16}。如下爲歸併排序的排序過程:

  採用二路歸併排序時,須要進行 3 趟歸併排序,其過程如圖5.1所示。第 1 趟將每兩個各含有一個元素的子表歸併成一個新表,如將 {18} 和 {2} 排好序變爲 {2,18}。第 2 趟將每兩個各含有兩個元素的子表歸併成一個新表,如將 {2,18} 和 {20,34} 歸併爲{2,18,20,34}。第 3 趟將每兩個各含有 4 個元素的子表歸併成一個新表,產生最終有序表。

   3. 排序算法

 1 public class MergeSort {
 2     /**
 3      * 自底向上的二路歸併算法
 4      * @param Data
 5      */
 6     public static void mergeSort(int[] Data) {
 7         for(int i = 1; i < Data.length; i = 2*i) {    //進行log2n趟歸併
 8             mergePass(Data , i , Data.length);
 9         }
10     }
11     /**
12      * 對整個表進行一趟歸併
13      * @param Data
14      * @param len    子表的長度
15      * @param n        整個表的長度
16      */
17     private static void mergePass(int[] Data, int len, int n) {
18         int  i = 0;
19         for(i = 0; i + 2*len - 1 < n; i += 2*len) {        //歸併len長的兩個相鄰子表
20             merge(Data , i , i+len-1 , i+2*len-1);
21         }
22         if(i+len-1 < n) {                                //餘下兩個子表,後者長度小於len
23             merge(Data , i , i+len-1 , n-1);            //歸併這兩個子表
24         }
25     }
26     /**
27      * 實現兩個子表在同一個表中的一次歸併
28      * @param Data
29      * @param low    第一個子表的開始
30      * @param mid    第一個子表的結尾
31      * @param high    第二個子表的結尾,第二個子表的開始爲mid+1
32      */
33     private static void merge(int[] Data, int low, int mid, int high) {
34         int[] num = new int[Data.length];    //暫存數組
35         int i = low;    //第一個子表的開始下標
36         int j = mid+1;    //第二個子表的開始下標
37         int k = 0;        //暫存表的下標
38         while( i <= mid && j <= high) {    //在第一個子表和第二個子表均爲掃描完時循環
39             if(Data[i] <= Data[j]) {    //將第一個子表中的元素放入暫存表num
40                 num[k] = Data[i];
41                 i++;
42                 k++;
43             } else {                    //將第二個子表中的元素放入暫存表num
44                 num[k] = Data[j];
45                 j++;
46                 k++;
47             }
48         }
49         while(i <= mid) {                //將第一個子表剩餘部分複製到暫存表num
50             num[k] = Data[i];
51             i++;
52             k++;
53         }
54         while(j <= high) {                //將第二個子表剩餘部分複製到暫存表num
55             num[k] = Data[j];
56             j++;
57             k++;
58         }
59         for(k = 0 , i = low; i <= high; k++ , i++) {    //將暫存表複製到源表中
60             Data[i] = num[k];
61         }
62     }
63     
64     public static void main(String[] args) {
65         int[] Data = {18,2,20,34,12,32,6,16};
66         
67         mergeSort(Data);
68         System.out.println("歸併排序的結果:");
69         for(int i = 0; i < Data.length; i++) {
70             System.out.print(Data[i]+" ");
71         }
72     }
73 }

   4.    算法分析

  時間複雜度:O(nlog2n);

  空間複雜度:O(n);

  穩定性:穩定;

  複雜性:較複雜。

6 基數排序

   1. 排序思路

  基數排序有兩種:最低位優先(LSD)和最高位優先(MSD)。最低位優先的過程是:先按最低位的值對元素進行排序,在此基礎上,再按次低位進行排序,以此類推,由低位向高位,每趟都是根據關鍵字的一位並在前一趟的基礎上對全部元素進行排序,直至最高位。

  說明:基數排序每趟並不產生有序區,也就是說在最後一趟排序結束前,全部的元素並不必定歸位了。

   2. 基數排序實例

  設待排序的表有 10 個元素,其關鍵字爲 {18,2,20,34,12,32,6,16}。如下爲堆排序的排序過程:

  先按個位數進行排序,再按十位數進行排序。

 

  3. 排序算法

 1 public class RadixSort {     
 2     public static void radixSort(int[] Data){  
 3         int[][] gather = new int[10][Data.length];    //存放某位上數相同值的那一些數,如15 ,25  
 4         int[] sameNumSize = new int[10];               //存放某位相同值的那一些數的個數
 5         int some=0;        //總的收集次數
 6         int sub = 0;      //數組Data的下標
 7         int n=1;          //取低位上的值時的除數,如取個數時n=1,取十位時n=10
 8      
 9         while(some < maxSize(Data)) {  
10             for(int i = 0; i < Data.length; i++) {    //從個位開始分配
11                 int remain = (Data[i] / n) % 10;    //取某位上的數  
12                 gather[remain][sameNumSize[remain]] = Data[i];    //將某位上數相同值的那一些數存放在二維數組gather中
13                 sameNumSize[remain]++;                //相同值的那一些數的個數加1
14             }  
15             
16             for(int i = 0; i < 10; i++) {    //將分配結束的數收集到源數組中 
17                 if(sameNumSize[i] != 0) {
18                     for(int j = 0; j < sameNumSize[i]; j++) {
19                         Data[sub] = gather[i][j];  
20                         sub++; 
21                     }  
22                     sameNumSize[i] = 0;        //置空,以便下次分配時使用  
23                 }  
24             }  
25             sub = 0;    //下標置0
26             n *= 10;      //取次低位
27             some++;      //收集次數加1
28         }  
29           
30     } 
31     /**
32      * 求數組中最大數的長度
33      * @param arr
34      * @return 最大長度
35      */
36     public static int maxSize(int[] arr) {  
37         int temp = arr[0];  
38         for(int i = 1;i < arr.length; i++) { 
39             if(temp < arr[i]) {  
40                 temp = arr[i];  
41             }  
42         }  
43         return String.valueOf(temp).length();  
44     } 
45     
46     public static void main(String[] args) {
47         int[] Data = {75,23,98,44,57,12,29,64,38,82};
48         
49         radixSort(Data);
50         System.out.println("基數排序的結果:");
51         for(int i = 0; i < Data.length; i++) {
52             System.out.print(Data[i]+" ");
53         }
54     }
55 }

   4. 算法分析

  時間複雜度:O(d(n+r));

  空間複雜度:O(r);

  穩定性:穩定;

  複雜性:較複雜。

7 各類內排序方法的比較和選擇

   1. 各類排序方法的性能比較

 

圖7.1 各類排序方法的性能比較

   2. 選擇排序方法應綜合考慮下列因素:

  (1)待排序的元素數目 n(問題規模);

  (2)元素的大小(每一個元素的規模);

  (3)關鍵字的結構及其初始狀態;

  (4)對穩定性的要求;

  (5)語言工具的條件;

  (6)存儲結構;

  (7)時間和空間複雜度。

   3. 總結

  (1)若 n 較小(如 n  <= 50),可採用直接插入和直接選擇排序;

  (2)若文件初始狀態基本有序(指正序),則應選用直接插入、冒泡或隨機的快速排序;

  (3)若 n 較大,則應採用時間複雜度爲O(nlog2n)的排序方法:快速排序、堆排序或歸併排序;

  (4)若要將兩個有序表組合成一個新的有序表,最好的方法是歸併排序方法;

  (5)若 n 很大,元素的關鍵字位數較少且能夠分解時,採用基數排序較好。

相關文章
相關標籤/搜索