深入淺出排序算法的多語言實現
作者:白寧超
2015年10月8日20:08:11
摘要:十一假期於實驗室無趣,逐研究起數據結構之排序。起初覺得就那么幾種排序,兩三天就搞定了,后來隨着研究的深入,發覺里面有不少東西。本文介紹常用的排序算法,主要從以下幾個方面:算法的介紹、算法思想、算法步驟、算法優缺點、算法實現、運行結果、算法優化等。最后對本文進行總結。本文為作者原創,程序經測試無誤。部分資料引用論文和網絡材料以及博客,后續參見參考文獻。(本文原創,轉載注明出處:深入淺出排序算法的多語言實現)
1 排序的基本概念
排序: 所謂排序,就是要整理文件中的記錄,使之按關鍵字遞增(或遞減)次序排列起來。其確切定義如下:
輸入:n個記錄R1,R2,…,Rn,其相應的關鍵字分別為K1,K2,…,Kn。
輸出:Ril,Ri2,…,Rin,使得Ki1≤Ki2≤…≤Kin。(或Ki1≥Ki2≥…≥Kin)。
排序的穩定性:當待排序記錄的關鍵字均不相同時,排序結果是惟一的,否則排序結果不唯一。在待排序的文件中,若存在多個關鍵字相同的記錄,經過排序后這些具有相同關鍵字的記錄之間的相對次序保持不變,該排序方法是穩定的;若具有相同關鍵字的記錄之間的相對次序發生變化,則稱這種排序方法是不穩定的。
注意: 排序算法的穩定性是針對所有輸入實例而言的。即在所有可能的輸入實例中,只要有一個實例使得算法不滿足穩定性要求,則該排序算法就是不穩定的。
排序方法的分類:
1.按是否涉及數據的內、外存交換分
在排序過程中,若整個文件都是放在內存中處理,排序時不涉及數據的內、外存交換,則稱之為內部排序(簡稱內排序);反之,若排序過程中要進行數據的內、外存交換,則稱之為外部排序。
注意: ① 內排序適用於記錄個數不很多的小文件 ② 外排序則適用於記錄個數太多,不能一次將其全部記錄放人內存的大文件。
2.按策略划分內部排序方法
可以分為五類:插入排序、選擇排序、交換排序、歸並排序和分配排序。
排序算法分析
1.排序算法的基本操作 :
(1) 比較兩個關鍵字的大小;
(2) 改變指向記錄的指針或移動記錄本身。
注意:第(2)種基本操作的實現依賴於待排序記錄的存儲方式。
2.待排文件的常用存儲方式
(1) 以順序表(或直接用向量)作為存儲結構
排序過程:對記錄本身進行物理重排(即通過關鍵字之間的比較判定,將記錄移到合適的位置)
(2) 以鏈表作為存儲結構
排序過程:無須移動記錄,僅需修改指針。通常將這類排序稱為鏈表(或鏈式)排序;
(3) 用順序的方式存儲待排序的記錄,但同時建立一個輔助表(如包括關鍵字和指向記錄位置的指針組成的索引表)
排序過程:只需對輔助表的表目進行物理重排(即只移動輔助表的表目,而不移動記錄本身)。適用於難於在鏈表上實現,仍需避免排序過程中移動記錄的排序方法。
3.排序算法性能評價
(1) 評價排序算法好壞的標准
① 執行時間和所需的輔助空間 ② 算法本身的復雜程度
(2) 排序算法的空間復雜度
若排序算法所需的輔助空間並不依賴於問題的規模n,即輔助空間是O(1),則稱之為就地排序(In-PlaceSou)。 非就地排序一般要求的輔助空間為O(n)。
(3) 排序算法的時間開銷
大多數排序算法的時間開銷主要是關鍵字之間的比較和記錄的移動。有的排序算法其執行時間不僅依賴於問題的規模,還取決於輸入實例中數據的狀態。
文件的順序存儲結構表示
#define n l00 //假設的文件長度,即待排序的記錄數目 typedef int KeyType; //假設的關鍵字類型 typedef struct{ //記錄類型 KeyType key; //關鍵字項 InfoType otherinfo;//其它數據項,類型InfoType依賴於具體應用而定義 }RecType; typedef RecType SeqList[n+1];//SeqList為順序表類型,表中第0個單元一般用作哨兵
2 交換排序
交換排序的基本思想是:兩兩比較待排序記錄的關鍵字,發現兩個記錄的次序相反時即進行交換,直到沒有反序的記錄為止。
應用交換排序基本思想的主要排序方法有:冒泡排序和快速排序。
2.1 冒泡排序
冒泡排序:一種簡單直觀的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
算法步驟:
1)比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
2)對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。這步做完后,最后的元素會是最大的數。
3)針對所有的元素重復以上的步驟,除了最后一個。
4)持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
排序算法特點,算法復雜度
時間復雜度為O(n^2),雖然不及堆排序、快速排序的O(nlogn,底數為2),但是有兩個優點:1.“編程復雜度”很低,很容易寫出代碼;2.具有穩定性。
其中若記錄序列的初始狀態為"正序",則冒泡排序過程只需進行一趟排序,在排序過程中只需進行n-1次比較,且不移動記錄;反之,若記錄序列的初始狀態為"逆序",則需進行n(n-1)/2次比較和記錄移動。因此冒泡排序總的時間復雜度為O(n*n)。
冒泡排序示意圖:
冒泡排序示意圖
數據結構算法的實現:
void BubbleSort(SeqList R) { //R(l..n)是待排序的文件,采用自下向上掃描,對R做冒泡排序 int i,j; Boolean exchange; //交換標志 for(i=1;i<n;i++){ //最多做n-1趟排序 exchange=FALSE; //本趟排序開始前,交換標志應為假 for(j=n-1;j>=i;j--) //對當前無序區R[i..n]自下向上掃描 if(R[j+1].key<R[j].key){//交換記錄 R[0]=R[j+1]; //R[0]不是哨兵,僅做暫存單元 R[j+1]=R[j]; R[j]=R[0]; exchange=TRUE; //發生了交換,故將交換標志置為真 } if(!exchange) //本趟排序未發生交換,提前終止算法 return; } //endfor(外循環) } //BubbleSort
排序算法的java實現

package com.multiplesort.bnc; import java.util.Arrays; /** * 各種排序算法分析比較之冒泡排序:【交換排序】 * @author bnc * @see http://www.cnblogs.com/liuling/p/2013-7-24-01.html */ public class BubbleSort { /** * 隨機生成從0-n的隨機數組 * @param n 數組的成都 * @return resultArr 數組 * @author 白寧超 */ public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } /** * 數據交換 * @param data 整數型數組 * @param i 第一層循環指針 * @param j 第二層循環指針 */ private static void swap(int[] data, int i, int j) { int temp=data[i]; data[i]=data[j]; data[j]=temp; } /** * 簡單的冒泡排序:穩定 * @deprecated :冒泡排序是一種穩定的排序方法。 •若文件初狀為正序,則一趟起泡就可完成排序,排序碼的比較次數為n-1,且沒有記錄移動,時間復雜度是O(n) •若文件初態為逆序,則需要n-1趟起泡,每趟進行n-i次排序碼的比較,且每次比較都移動三次,比較和移動次數均達到最大值∶O(n^2) •起泡排序平均時間復雜度為O(n^2) * @param data 整數數組 */ public static void BubbleSort0(int[] array){ int i,j; for(i=0;i<array.length;i++){ for(j=i+1;j<array.length;j++){ if(array[i]>array[j]) swap(array,i,j);//數據交換 } } System.out.println(); System.out.println("簡單【冒泡排序】后的結果:"); for (i = 0; i < array.length; i++) { System.out.print(array[i]+" "); } } /** * 改進后的冒泡排序:穩定 * @deprecated :冒泡排序是一種穩定的排序方法。 •若文件初狀為正序,則一趟起泡就可完成排序,排序碼的比較次數為n-1,且沒有記錄移動,時間復雜度是O(n) •若文件初態為逆序,則需要n-1趟起泡,每趟進行n-i次排序碼的比較,且每次比較都移動三次,比較和移動次數均達到最大值∶O(n^2) •起泡排序平均時間復雜度為O(n^2) * @param data 整數數組 */ public static void BubbleSort1(int[] array){ int i,j; for(i=0;i<array.length;i++){ for(j=array.length-2;j>=i;j--){ if(array[j]>array[j+1]){ // System.out.println(array[j]+"<--->"+array[j+1]);//測試結果前面排序影響后面,相當於是從緩存有序的數組中獲取 swap(array,j,j+1);//數據交換 } } } System.out.println(); System.out.println("改進后【冒泡排序】的結果:"); for (i = 0; i < array.length; i++) { System.out.print(array[i]+" "); } } /** * 當數組基本有序時,如何改進排序算法 * @param array */ public static void BubbleSort2(int[] array){ int i,j; Boolean flag=true; for(i=0;i<array.length&&flag;i++){//如果flag為flag退出循環 flag=false; for(j=array.length-2;j>=i;j--){ if(array[j]>array[j+1]){ swap(array,j,j+1);//數據交換 //System.out.println(array[j]+"<--->"+array[j+1]); flag=true;//如果有數據交換,則flag為true } } } System.out.println(); System.out.println("基本有序數組【冒泡排序】的結果:"); for (i = 0; i < array.length; i++) { System.out.print(array[i]+" "); } } public static void main(String[] args) { int[] array=randomArray(20, 100);//隨機生成0--100的20個長度的數組 int[] array1={1,3,2,4,5,6};//基本有序數組 System.out.println("冒泡排序前:"); for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); System.out.println("使用內部排序的結果:"); for(int i=0;i<array.length;i++){ //使用內部排序的結果 Arrays.sort(array);//內部排序 System.out.print(array[i]+" "); } //BubbleSort0(array); //BubbleSort1(array); BubbleSort2(array1); } }
排序算法的phthon實現
def bubble_sort(lists): # 冒泡排序 count = len(lists) for i in range(0, count): for j in range(i + 1, count): if lists[i] > lists[j]: lists[i], lists[j] = lists[j], lists[i] return lists
2.2 快速排序
快速排序:是由東尼·霍爾所發展的一種排序算法。在平均狀況下,排序 n 個項目要Ο(n log n)次比較。在最壞狀況下則需要Ο(n2)次比較,但這種狀況並不常見。事實上,快速排序通常明顯比其他Ο(n log n) 算法更快,因為它的內部循環(inner loop)可以在大部分的架構上很有效率地被實現出來。
快速排序使用分治法(Divide and conquer)策略來把一個串行(list)分為兩個子串行(sub-lists)。
算法步驟:
1 從數列中挑出一個元素,稱為 “基准”(pivot),
2 重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作。
3 遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序。
遞歸的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞歸下去,但是這個算法總會退出,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最后的位置去。
快速排序示意圖:
快速排序示意圖
數據結構算法的實現:
void QuickSort(SeqList R,int low,int high) { //對R[low..high]快速排序 int pivotpos; //划分后的基准記錄的位置 if(low<high){//僅當區間長度大於1時才須排序 pivotpos=Partition(R,low,high); //對R[low..high]做划分 QuickSort(R,low,pivotpos-1); //對左區間遞歸排序 QuickSort(R,pivotpos+1,high); //對右區間遞歸排序 } } //QuickSort
排序算法的java實現

package com.multiplesort.bnc; /** * 快速排序 * 基本思想:選擇一個基准元素,通常選擇第一個元素或者最后一個元素,通過一趟掃描,將待排序列分成兩部分,一部分比基准元素小,一部分大於等於基准元素, * 此時基准元素在其排好序后的正確位置,然后再用同樣的方法遞歸地排序划分的兩部分。 * 分析: 快速排序是不穩定的排序。 快速排序的時間復雜度為O(nlogn)。 當n較大時使用快排比較好,當序列基本有序時用快排反而不好。 * @author bnc * */ public class QuickSort { //隨機生成數組 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } private static void quickSort(int[] array, int low, int high) { if(array.length>0) { if(low<high){ //如果不加這個判斷遞歸會無法退出導致堆棧溢出異常 int middle = getMiddle(array,low,high); quickSort(array, 0, middle-1); quickSort(array, middle+1, high); } } } private static int getMiddle(int[] array, int low, int high) { //int m=low+(high-low)/2;//計算數組元素中間的下標 int temp = array[low];//基准元素 while(low<high){ //找到比基准元素小的元素位置 while(low<high && array[high]>=temp){ high--; } array[low] = array[high]; while(low<high && array[low]<=temp){ low++; } array[high] = array[low]; } array[low] = temp; return low; } //快排 public static void quickSort(int[] array){ System.out.println("快速排序前的結果"); printArray(array); quickSort(array,0,array.length-1); System.out.println("快速排序后的結果"); printArray(array); } public static void main(String[] args) { quickSort(randomArray(20, 100)); } }
排序算法的phthon實現
def quick_sort(lists, left, right): # 快速排序 if left >= right: return lists key = lists[left] low = left high = right while left < right: while left < right and lists[right] >= key: right -= 1 lists[left] = lists[right] while left < right and lists[left] <= key: left += 1 lists[right] = lists[left] lists[right] = key quick_sort(lists, low, left - 1) quick_sort(lists, left + 1, high) return lists
3 選擇排序
3.1 直接選擇排序
選擇排序(Selection sort)也是一種簡單直觀的排序算法。
算法步驟:
1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
2)再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的末尾。
3)重復第二步,直到所有元素均排序完畢。
排序算法特點,算法復雜度
選擇排序的交換操作介於0和(n-1)次之間。選擇排序的比較操作為n(n-1)/2次之間。選擇排序的賦值操作介於0和3(n-1)次之間。
比較次數O(n^2),比較次數與關鍵字的初始狀態無關,總的比較次數N=(n-1)+(n-2)+...+1=n*(n-1)/2。 交換次數O(n),最好情況是,已經有序,交換0次;最壞情況是,逆序,交換n-1次。 交換次數比冒泡排序少多了,由於交換所需CPU時間比比較所需的CPU時間多,n值較小時,選擇排序比冒泡排序快。
直接選擇排序示意圖:
選擇排序示意圖
數據結構算法的實現:
void SelectSort(SeqList R) { int i,j,k; for(i=1;i<n;i++){//做第i趟排序(1≤i≤n-1) k=i; for(j=i+1;j<=n;j++) //在當前無序區R[i..n]中選key最小的記錄R[k] if(R[j].key<R[k].key) k=j; //k記下目前找到的最小關鍵字所在的位置 if(k!=i){ //交換R[i]和R[k] R[0]=R[i];R[i]=R[k];R[k]=R[0]; //R[0]作暫存單元 } //endif } //endfor } //SeleetSort
排序算法的java實現

package com.multiplesort.bnc; /** * 選擇排序:簡單選擇排序、堆排序。 * 思想:每趟從待排序的記錄序列中選擇關鍵字最小的記錄放置到已排序表的最前位置,直到全部排完。 * 關鍵問題:在剩余的待排序記錄序列中找到最小關鍵碼記錄。 * •方法: –直接選擇排序 –堆排序 * @author bnc * */ public class SimpleSelectionSort { //隨機生成一組數 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } ////簡單的選擇排序: //基本思想:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;然后在剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最后一個數比較為止。 public static void simpleSeclectSort(int[] array){ int i,j,min; for(i=0;i<array.length;i++){ min=i; //將當前下標定義最小下標 for(j=i+1;j<array.length;j++){ //循環之后的數據 if(array[min]>array[j]) //如果有小於當前值的最小數據 min=j; //關鍵字的最小下標賦值給min } if(i!=min){ //若min!=i,說明找到最小值交換 swap(array,i,min); //最小的一個數與第i位置的數交換 } } System.out.println("排序后的數組:"); printArray(array); } ////簡單的選擇排序: //基本思想:在要排序的一組數中,選出最小的一個數與第一個位置的數交換;然后在剩下的數當中再找最小的與第二個位置的數交換,如此循環到倒數第二個數和最后一個數比較為止。 public static void simpleSeclectSort1(int[] array){ int i,j,min; for(i=0;i<array.length;i++){ min=array[i]; int n=i;//將當前下標定義最小下標 for(j=i+1;j<array.length;j++){ //循環之后的數據 if(min>array[j]){ //如果有小於當前值的最小數據 min=array[j]; n=j; //關鍵字的最小下標賦值給min } } array[n]=array[i]; array[i]=min; } System.out.println("排序后的數組:"); printArray(array); } public static void main(String[] args) { int[] array=randomArray(10, 100); System.out.println("排序前的數組:"); printArray(array); simpleSeclectSort1(array); } }
排序算法的phthon實現
def select_sort(lists): # 選擇排序 count = len(lists) for i in range(0, count): min = i for j in range(i + 1, count): if lists[min] > lists[j]: min = j lists[min], lists[i] = lists[i], lists[min] return lists
3.2 堆排序
堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
堆排序的平均時間復雜度為Ο(nlogn) 。
算法步驟:
1)創建一個堆H[0..n-1]
2)把堆首(最大值)和堆尾互換
3)把堆的尺寸縮小1,並調用shift_down(0),目的是把新的數組頂端數據調整到相應位置
4) 重復步驟2,直到堆的尺寸為1
排序算法特點,算法復雜度
堆排序的平均時間復雜度為O(nlogn),空間復雜度為O(1)。
由於它在直接選擇排序的基礎上利用了比較結果形成。效率提高很大。它完成排序的總比較次數為O(nlog2n)。它是對數據的有序性不敏感的一種算法。但堆排序將需要做兩個步驟:-是建堆,二是排序(調整堆)。所以一般在小規模的序列中不合適,但對於較大的序列,將表現出優越的性能。
堆排序示意圖:
堆排序示意圖
數據結構算法的實現:
void HeapSort(SeqIAst R) { //對R[1..n]進行堆排序,不妨用R[0]做暫存單元 int i; BuildHeap(R); //將R[1-n]建成初始堆 for(i=n;i>1;i--){ //對當前無序區R[1..i]進行堆排序,共做n-1趟。 R[0]=R[1];R[1]=R[i];R[i]=R[0]; //將堆頂和堆中最后一個記錄交換 Heapify(R,1,i-1); //將R[1..i-1]重新調整為堆,僅有R[1]可能違反堆性質 } //endfor } //HeapSort
排序算法的java實現

package com.multiplesort.bnc; import java.util.Arrays; /** * 選擇排序:堆排序 * 適用於大數據 * 穩定性:不穩定 * 堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。 * 堆的定義下:具有n個元素的序列 (h1,h2,...,hn),當且僅當滿足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)時稱之為堆。 * 在這里只討論滿足前者條件的堆。由堆的定義可以看出,堆頂元素(即第一個元素)必為最大項(大頂堆)。完全二 叉樹可以很直觀地表示堆的結構。堆頂為根,其它為左子樹、右子樹。 * 思想:初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,使之成為一個 堆,這時堆的根節點的數最大。然后將根節點與堆的最后一個節點交換。 * 然后對前面(n-1)個數重新調整使之成為堆。依此類推,直到只有兩個節點的堆,並對 它們作交換,最后得到有n個節點的有序序列。 * 從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反復調用滲透函數實現排序的函數。 * 初始序列:50,10,90,30,70,40,80,60,20 * @author bnc * */ public class HeapSort { //隨機生成一組數 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } /** *構建大頂堆 * @param array 待建堆的數據 * @param lastIndex 從lastIndex處節點(最后一個節點)的父節點開始 */ public static void buildMaxHeap(int[] array,int lastIndex){ for(int i=(lastIndex-1)/2;i>=0;i--){ int k=i; //k保存正在判斷的節點 while(k*2+1<=lastIndex){ //如果當前k節點的子節點存在 int biggerIndex=2*k+1; //k節點的左子節點的索引 if(biggerIndex<lastIndex){ //如果biggerIndex小於lastIndex,即biggerIndex+1代表的k節點的右子節點存在 if(array[biggerIndex]<array[biggerIndex+1]) //若果右子節點的值較大 biggerIndex++; //biggerIndex總是記錄較大子節點的索引 } if(array[k]<array[biggerIndex]){ //如果k節點的值小於其較大的子節點的值 swap(array,k,biggerIndex); k=biggerIndex; //將biggerIndex賦予k,開始while循環的下一次循環,重新保證k節點的值大於其左右子節點的 } else break; } } } //堆排序 public static void heapSort(int[] array){ int arrayLength=array.length; //循環建堆 for(int i=0;i<arrayLength-1;i++){ buildMaxHeap(array,arrayLength-1-i); //交換堆頂和最后一個元素 swap(array,0,arrayLength-1-i); } printArray(array); } //建大堆 public static void HeapAdjust(int[] array,int s,int m){ int temp,j; temp=array[s]; for(j=(2*s+1);j<m;j=(j*2+1)){ //System.out.println("j:"+j+" array[j]:"+array[j]); if(j<m-1&&array[j]<array[j+1]) ++j; if(temp>array[j]) break; array[s]=array[j]; s=j; } array[s]=temp; } //堆排序 public static void heapSort1(int[] array){ int i,length; length=array.length; for(i=(length-1)/2;i>=0;i--) HeapAdjust(array,i,length); printArray(array); for(i=length;i>1;i--){ swap(array,0,i-1); HeapAdjust(array,0,i-1); } printArray(array); } public static void main(String[] args) { int[] array={50,10,234,90,456,30,70,40,80,60,20,300,1000,1,30,1,45}; //heapSort(randomArray(11, 100)); int[] a = randomArray(15, 100); heapSort1(a); } }
排序算法的phthon實現
# 調整堆 def adjust_heap(lists, i, size): lchild = 2 * i + 1 rchild = 2 * i + 2 max = i if i < size / 2: if lchild < size and lists[lchild] > lists[max]: max = lchild if rchild < size and lists[rchild] > lists[max]: max = rchild if max != i: lists[max], lists[i] = lists[i], lists[max] adjust_heap(lists, max, size) # 創建堆 def build_heap(lists, size): for i in range(0, (size/2))[::-1]: adjust_heap(lists, i, size) # 堆排序 def heap_sort(lists): size = len(lists) build_heap(lists, size) for i in range(0, size)[::-1]: lists[0], lists[i] = lists[i], lists[0] adjust_heap(lists, 0, i)
4 插入排序
4.1 直接插入排序
冒泡排序:一種簡單直觀的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
算法步驟:
1)比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
2)對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。這步做完后,最后的元素會是最大的數。
3)針對所有的元素重復以上的步驟,除了最后一個。
4)持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
排序算法特點,算法復雜度
如果目標是把n個元素的序列升序排列,那么采用直接插入排序存在最好情況和最壞情況。最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。最壞情況就是,序列是降序排列,那么此時需要進行的比較共有n(n-1)/2次。直接插入排序的賦值操作是比較操作的次數減去(n-1)次。平均來說直接插入排序算法復雜度為O(n2)。因而,直接插入排序不適合對於數據量比較大的排序應用。但是,如果需要排序的數據量很小,例如,量級小於千,那么直接插入排序還是一個不錯的選擇。 插入排序在工業級庫中也有着廣泛的應用,在STL的sort算法和stdlib的qsort算法中,都將插入排序作為快速排序的補充,用於少量元素的排序(通常為8個或以下)。
插入排序示意圖:
插入排序示意圖
數據結構算法的實現:
void lnsertSort(SeqList R) { //對順序表R中的記錄R[1..n]按遞增序進行插入排序 int i,j; for(i=2;i<=n;i++) //依次插入R[2],…,R[n] if(R[i].key<R[i-1].key){//若R[i].key大於等於有序區中所有的keys,則R[i] //應在原有位置上 R[0]=R[i];j=i-1; //R[0]是哨兵,且是R[i]的副本 do{ //從右向左在有序區R[1..i-1]中查找R[i]的插入位置 R[j+1]=R[j]; //將關鍵字大於R[i].key的記錄后移 j-- ; }while(R[0].key<R[j].key); //當R[i].key≥R[j].key時終止 R[j+1]=R[0]; //R[i]插入到正確的位置上 }//endif }//InsertSort
排序算法的java實現

package com.multiplesort.bnc; /** * 插入排序: * 思想:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置,直到全部插入排序完為止。 * 關鍵問題:在前面已經排好序的序列中找到合適的插入位置。 * 優點:記錄本身基本有序,記錄數比較少。 * 基本有序的定義:小的關鍵字基本在前面,大的基本在后面,不大不小基本在中間 * @author bnc * */ public class StraightInsertionSort { //隨機生成數組 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } /** * 直接插入排序(從后向前找到合適位置后插入) * 基本思想:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置(從后向前找到合適位置后),直到全部插入排序完為止。 * 直接插入排序是穩定的排序。關於各種算法的穩定性分析可以參考http://www.cnblogs.com/Braveliu/archive/2013/01/15/2861201.html * 文件初態不同時,若文件初態為正序算法的時間復雜度為O(n),這時最好的情況。若初態為反序,則第i個待插入記錄需要比較i+1次才能找到合適位置插入,故時間復雜度為O(n2),這時最壞的情況。 * 直接插入排序的平均時間復雜度為O(n2)。 * @param array */ public static void insertSort(int[] array){ System.out.println("直接插入排序前的結果"); printArray(array); for(int i=1;i<array.length;i++){ int temp=array[i]; //待插入的元素 int j; for(j=i-1;j>=0;j--){ if(array[j]>temp) //將大於temp的元素右移 array[j+1]=array[j]; //此時array[j+1]=array[j]=兩者最大值 else break; } array[j+1]=temp; //將待插入的元素賦值較小位置 } System.out.println("直接插入排序后的結果"); printArray(array); } public static void insertSort1(int[] array){ System.out.println("直接插入排序前的結果"); printArray(array); int i,j; for(i=1;i<array.length;i++){ if(array[i]<array[i-1]){ int temp=array[i]; //設置哨兵 for(j=i-1;j>=0&&array[j]>=temp;j--){ array[j+1]=array[j]; //記錄后移 } array[j+1]=temp;//插入正確位置 } } System.out.println("直接插入排序后的結果"); printArray(array); } public static void main(String[] args) { int[] array={57,68,59,52}; insertSort(randomArray(10, 100)); insertSort1(randomArray(10, 100)); } }
排序算法的phthon實現
def insert_sort(lists): # 插入排序 count = len(lists) for i in range(1, count): key = lists[i] j = i - 1 while j >= 0: if lists[j] > key: lists[j + 1] = lists[j] lists[j] = key j -= 1 return lists
4.2 希爾排序
希爾排序,也稱遞減增量排序算法,是插入排序的一種更高效的改進版本。但希爾排序是非穩定排序算法。
希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
插入排序在對幾乎已經排好序的數據操作時, 效率高, 即可以達到線性排序的效率 但插入排序一般來說是低效的, 因為插入排序每次只能將數據移動一位 希爾排序的基本思想是:先將整個待排序的記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行依次直接插入排序。
算法步驟:
1)選擇一個增量序列t1,t2,…,tk,其中ti>tj,tk=1;
2)按增量序列個數k,對序列進行k 趟排序;
3)每趟排序,根據對應的增量ti,將待排序列分割成若干長度為m 的子序列,分別對各子表進行直接插入排序。僅增量因子為1 時,整個序列作為一個表來處理,表長度即為整個序列的長度。
排序算法特點,算法復雜度
希爾排序是基於插入排序的一種算法, 在此算法基礎之上增加了一個新的特性,提高了效率。希爾排序的時間復雜度為 O(N*(logN)2), 沒有快速排序算法快 O(N*(logN)),因此中等大小規模表現良好,對規模非常大的數據排序不是最優選擇。但是比O(N2)復雜度的算法快得多。並且希爾排序非常容易實現,算法代碼短而簡單。 此外,希爾算法在最壞的情況下和平均情況下執行效率相差不是很多,與此同時快速排序在最壞 的情況下執行的效率會非常差。專家們提倡,幾乎任何排序工作在開始時都可以用希爾排序,若在實際使用中證明它不夠快, 再改成快速排序這樣更高級的排序算法.
希爾排序是按照不同步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,所以插入排序的元素個數很少,速度很快;當元素基本有序了,步長很小,插入排序對於有序的序列效率很高。所以,希爾排序的時間復雜度會比o(n^2)好一些。由於多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最后其穩定性就會被打亂,所以shell排序是不穩定的
希爾排序示意圖:
希爾排序示意圖
數據結構算法的實現:
void ShellPass(SeqList R,int d) {//希爾排序中的一趟排序,d為當前增量 for(i=d+1;i<=n;i++) //將R[d+1..n]分別插入各組當前的有序區 if(R[i].key<R[i-d].key){ R[0]=R[i];j=i-d; //R[0]只是暫存單元,不是哨兵 do {//查找R[i]的插入位置 R[j+d];=R[j]; //后移記錄 j=j-d; //查找前一記錄 }while(j>0&&R[0].key<R[j].key); R[j+d]=R[0]; //插入R[i]到正確的位置上 } //endif } //ShellPass void ShellSort(SeqList R) { int increment=n; //增量初值,不妨設n>0 do { increment=increment/3+1; //求下一增量 ShellPass(R,increment); //一趟增量為increment的Shell插入排序 }while(increment>1) } //ShellSort
排序算法的java實現

package com.multiplesort.bnc; /** * 希爾排序 * 基本思想:先取一個小於n的整數d1作為第一個增量,把文件的全部記錄分成d1個組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序; * 然后,取第二個增量d2<d1重復上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有記錄放在同一組中進行直接插入排序為止。該方法實質上是一種分組插入方法。 * 我們知道一次插入排序是穩定的,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最后其穩定性就會被打亂,所以希爾排序是不穩定的。 * 希爾排序的時間性能優於直接插入排序,原因如下: (1)當文件初態基本有序時直接插入排序所需的比較和移動次數均較少。 (2)當n值較小時,n和n2的差別也較小,即直接插入排序的最好時間復雜度O(n)和最壞時間復雜度0(n2)差別不大。 (3)在希爾排序開始時增量較大,分組較多,每組的記錄數目少,故各組內直接插入較快,后來增量di逐漸縮小,分組數逐漸減少,而各組的記錄數目逐漸增多, 但由於已經按di-1作為距離排過序,使文件較接近於有序狀態,所以新的一趟排序過程也較快。 因此,希爾排序在效率上較直接插人排序有較大的改進。 * 希爾排序的平均時間復雜度為O(nlogn)。 * @author bnc * */ public class ShellSort { //隨機生成數組 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } //希爾排序 public static void shellSort(int[] array){ System.out.println("希爾排序前的結果"); printArray(array); int d=array.length; //步長 while(true){ d=d/2; for(int x=0;x<d;x++){ for(int i=x+d;i<array.length;i+=d){ int temp=array[i]; int j; for(j=i-d;j>=0&&array[j]>temp;j-=d){ array[j+d]=array[j]; } array[j+d]=temp; } } if(d==1) break; } System.out.println("希爾排序后的結果"); printArray(array); } //希爾排序 public static void shellSort1(int[] array){ System.out.println("希爾排序前的結果"); printArray(array); int i,j; int increment=array.length; do{ increment=increment/3+1; for(i=increment;i<array.length;i++){ if(array[i]<array[i-increment]) { int temp=array[i]; for(j=i-increment;j>=0&&temp<array[j];j-=increment){ array[j+increment]=array[j]; } array[j+increment]=temp; } } } while(increment>1); System.out.println("希爾排序后的結果"); printArray(array); } public static void main(String[] args) { int[] a={9,1,5,8,3,7,4,6,2}; //shellSort(randomArray(10, 100)); shellSort1(randomArray(20, 100)); //shellSort1(a); } }
排序算法的phthon實現
def shell_sort(lists): # 希爾排序 count = len(lists) step = 2 group = count / step while group > 0: for i in range(0, group): j = i + group while j < count: k = j - group key = lists[j] while k >= 0: if lists[k] > key: lists[k + group] = lists[k] lists[k] = key k -= group j += group group /= step return lists
5 歸並排序
歸並排序(Merge sort)是建立在歸並操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。
算法步驟:
1. 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並后的序列
2. 設定兩個指針,最初位置分別為兩個已經排序序列的起始位置
3. 比較兩個指針所指向的元素,選擇相對小的元素放入到合並空間,並移動指針到下一位置
4. 重復步驟3直到某一指針達到序列尾
5. 將另一序列剩下的所有元素直接復制到合並序列尾
快速排序是由東尼·霍爾所發展的一種排序算法。在平均狀況下,排序 n 個項目要Ο(n log n)次比較。在最壞狀況下則需要Ο(n2)次比較,但這種狀況並不常見。事實上,快速排序通常明顯比其他Ο(n log n) 算法更快,因為它的內部循環(inner loop)可以在大部分的架構上很有效率地被實現出來。
排序算法特點,算法復雜度
歸並排序是一種非就地排序,將需要與待排序序列一樣多的輔助空間。在使用它對兩個己有序的序列歸並,將有無比的優勢。其時間復雜度無論是在最好情況下還是在最壞情況下均是O(nlog2n)。對數據的有序性不敏感。若數據節點數據量大,那將不適合。
歸並排序示意圖:
歸並排序示意圖
數據結構算法的實現:
void Merge(SeqList R,int low,int m,int high) {//將兩個有序的子文件R[low..m)和R[m+1..high]歸並成一個有序的 //子文件R[low..high] int i=low,j=m+1,p=0; //置初始值 RecType *R1; //R1是局部向量,若p定義為此類型指針速度更快 R1=(ReeType *)malloc((high-low+1)*sizeof(RecType)); if(! R1) //申請空間失敗 Error("Insufficient memory available!"); while(i<=m&&j<=high) //兩子文件非空時取其小者輸出到R1[p]上 R1[p++]=(R[i].key<=R[j].key)?R[i++]:R[j++]; while(i<=m) //若第1個子文件非空,則復制剩余記錄到R1中 R1[p++]=R[i++]; while(j<=high) //若第2個子文件非空,則復制剩余記錄到R1中 R1[p++]=R[j++]; for(p=0,i=low;i<=high;p++,i++) R[i]=R1[p];//歸並完成后將結果復制回R[low..high] } //Merge
排序算法的java實現

package com.multiplesort.bnc; /** * 歸並排序 * 基本思想:歸並(Merge)排序法是將兩個(或兩個以上)有序表合並成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然后再把有序子序列合並為整體有序序列 * 分析: 歸並排序是穩定的排序方法。 歸並排序的時間復雜度為O(nlogn)。 速度僅次於快速排序,為穩定排序算法,一般用於對總體無序,但是各子項相對有序的數列。 * @author bnc * */ public class MergingSort { //隨機生成數組 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } //合並 private static void merge(int[] array, int left, int middle, int right) { int[] tempArray=new int[array.length]; int mid=middle+1;//右邊起始位置 int temp=left; int third=left; while(left<=middle&&mid<=right){ //從兩個數組中選取較小的數放入中間數組 if(array[left]<=array[mid]){ tempArray[third++] = array[left++]; } else tempArray[third++]=array[mid++]; } //將剩余的部分放入中間數組 while(left<=middle){ tempArray[third++] = array[left++]; } while(mid<=right){ tempArray[third++] = array[mid++]; } //將中間數組復制回原數組 while(temp<=right){ array[temp] = tempArray[temp++]; } } //歸並排序 private static void mergeSort(int[] array, int left, int right) { if(left<right){ int middle=(left+right)/2; //對左邊進行遞歸 mergeSort(array, left, middle); //對右邊進行遞歸 mergeSort(array, middle+1, right); //合並 merge(array,left,middle,right); } } //穩定,歸並排序(二路歸並)[遞歸] public static void mergingSort(int[] array){ System.out.println("歸並排序前的結果"); printArray(array); //歸並排序 mergeSort(array,0,array.length-1); System.out.println("歸並排序前的結果"); printArray(array); } //兩兩歸並 public static void Merge(int[] SR,int[] TR,int i,int m,int n){ int j,k,l; for(j=m+1,k=i;i<=m&&j<=n;k++){ if(SR[i]<SR[j]) TR[k]=SR[i++]; else TR[k]=SR[j++]; } if(i<=m){ for(l=0;l<=m-i;l++) TR[k+l]=SR[i+l]; } if(j<=m){ for(l=0;l<=n-j;l++) TR[k+l]=SR[j+l]; } } //歸並 public static void MergePass(int[] SR,int[] TR,int s,int n){ int i=1; int j; while(i<=n-2*s-1){ Merge(SR,TR,i,i+s-1,i+2*s-1); i=i+2*s; } if(i<n-s+1) Merge(SR, TR, i, i+s-1, n); else for(j=i;j<=n;j++) TR[j]=SR[j]; } //穩定,歸並排序(二路歸並)[非遞歸] public static void mergingSort1(int[] array){ System.out.println("歸並排序前的結果"); printArray(array); int[] tempArray=new int[array.length+1]; int k=0; while(k<array.length){ MergePass(array,tempArray,k,array.length); k=2*k+1; MergePass(tempArray,array,k,array.length); k=2*k+1; } System.out.println("歸並排序前的結果"); printArray(array); } public static void main(String[] args) { int[] array={50,10,90,30,70,40,80,60,20}; //mergingSort(randomArray(10, 100)); //mergingSort(array); mergingSort1(array); } }
排序算法的phthon實現
def merge(left, right): i, j = 0, 0 result = [] while i < len(left) and j < len(right): if left[i] <= right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result += left[i:] result += right[j:] return result def merge_sort(lists): # 歸並排序 if len(lists) <= 1: return lists num = len(lists) / 2 left = merge_sort(lists[:num]) right = merge_sort(lists[num:]) return merge(left, right)
6 分配排序
6.1 箱排序
箱排序也稱桶排序(Bucket Sort),其基本思想是:設置若干個箱子,依次掃描待排序的記錄R[0],R[1],…,R[n-1],把關鍵字等於k的記錄全都裝入到第k個箱子里(分配),然后按序號依次將各非空的箱子首尾連接起來(收集)。
【例】要將一副混洗的52張撲克牌按點數A<2<…<J<Q<K排序,需設置13個"箱子",排序時依次將每張牌按點數放入相應的箱子里,然后依次將這些箱子首尾相接,就得到了按點數遞增序排列的一副牌。
數據結構算法的實現:
偽代碼算法為: void BucketSon(R) { //對R[0..n-1]做桶排序,其中0≤R[i].key<1(0≤i<n) for(i=0,i<n;i++) //分配過程. 將R[i]插入到桶B[「n(R[i].key)」]中; //可插入表頭上 for(i=0;i<n;i++) //排序過程 當B[i]非空時用插人排序將B[i]中的記錄排序; for(i=0,i<n;i++) //收集過程 若B[i]非空,則將B[i]中的記錄依次輸出到R中; }
6.2 基數排序
基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然后按每個位數分別比較。由於整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是只能使用於整數。
說基數排序之前,我們簡單介紹桶排序:
算法思想:是將陣列分到有限數量的桶子里。每個桶子再個別排序(有可能再使用別的排序算法或是以遞回方式繼續使用桶排序進行排序)。桶排序是鴿巢排序的一種歸納結果。當要被排序的陣列內的數值是均勻分配的時候,桶排序使用線性時間(Θ(n))。但桶排序並不是 比較排序,他不受到 O(n log n) 下限的影響。 簡單來說,就是把數據分組,放在一個個的桶中,然后對每個桶里面的在進行排序。
例如要對大小為[1..1000]范圍內的n個整數A[1..n]排序
首先,可以把桶設為大小為10的范圍,具體而言,設集合B[1]存儲[1..10]的整數,集合B[2]存儲 (10..20]的整數,……集合B[i]存儲( (i-1)*10, i*10]的整數,i = 1,2,..100。總共有 100個桶。
然后,對A[1..n]從頭到尾掃描一遍,把每個A[i]放入對應的桶B[j]中。 再對這100個桶中每個桶里的數字排序,這時可用冒泡,選擇,乃至快排,一般來說任 何排序法都可以。
最后,依次輸出每個桶里面的數字,且每個桶中的數字從小到大輸出,這 樣就得到所有數字排好序的一個序列了。
假設有n個數字,有m個桶,如果數字是平均分布的,則每個桶里面平均有n/m個數字。如果對每個桶中的數字采用快速排序,那么整個算法的復雜度是
O(n + m * n/m*log(n/m)) = O(n + nlogn – nlogm)
從上式看出,當m接近n的時候,桶排序復雜度接近O(n)
當然,以上復雜度的計算是基於輸入的n個數字是平均分布這個假設的。這個假設是很強的 ,實際應用中效果並沒有這么好。如果所有的數字都落在同一個桶中,那就退化成一般的排序了。
前面說的幾大排序算法 ,大部分時間復雜度都是O(n2),也有部分排序算法時間復雜度是O(nlogn)。而桶式排序卻能實現O(n)的時間復雜度。但桶排序的缺點是:
1)首先是空間復雜度比較高,需要的額外開銷大。排序有兩個數組的空間開銷,一個存放待排序數組,一個就是所謂的桶,比如待排序值是從0到m-1,那就需要m個桶,這個桶數組就要至少m個空間。
2)其次待排序的元素都要在一定的范圍內等等。
排序算法的java實現

package com.multiplesort.bnc; import java.util.ArrayList; import java.util.List; /** * 基數排序 * 基本思想:將所有待比較數值(正整數)統一為同樣的數位長度,數位較短的數前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后,數列就變成一個有序序列。 * 基數排序是穩定的排序算法。 基數排序的時間復雜度為O(d(n+r)),d為位數,r為基數。 * @author bnc * */ public class RadixSort { //隨機生成數組 public static int[] randomArray(int arrayLength,int maxNum){ int[] array=new int[arrayLength]; for(int i=0;i<array.length;i++){ array[i]=(int)(Math.random()*maxNum); } return array; } //數據交換 public static void swap(int[] data,int i,int j){ int temp=data[i]; data[i]=data[j]; data[j]=temp; } //打印出數組 public static void printArray(int[] array){ for(int i=0;i<array.length;i++){ System.out.print(array[i]+" "); } System.out.println(); } //基數排序 public static void radixSort(int[] array){ System.out.println("基數排序前的結果"); printArray(array); //找到最大數,確定要排序幾趟 int max = 0; for (int i = 0; i < array.length; i++) { if(max<array[i]) max = array[i]; } //判斷位數 int times = 0; while(max>0){ max = max/10; times++; } //建立十個隊列 List<ArrayList> queue = new ArrayList<ArrayList>(); for (int i = 0; i < 10; i++) { ArrayList queue1 = new ArrayList(); queue.add(queue1); } //進行times次分配和收集 for (int i = 0; i < times; i++) { //分配 for (int j = 0; j < array.length; j++) { int x = array[j]%(int)Math.pow(10, i+1)/(int)Math.pow(10, i); ArrayList queue2 = queue.get(x); queue2.add(array[j]); queue.set(x,queue2); } //收集 int count = 0; for (int j = 0; j < 10; j++) { while(queue.get(j).size()>0){ ArrayList<Integer> queue3 = queue.get(j); array[count] = queue3.get(0); queue3.remove(0); count++; } } } System.out.println("基數排序后的結果"); printArray(array); } public static void main(String[] args) { radixSort(randomArray(10, 100)); } }
排序算法的phthon實現
import math def radix_sort(lists, radix=10): k = int(math.ceil(math.log(max(lists), radix))) bucket = [[] for i in range(radix)] for i in range(1, k+1): for j in lists: bucket[j/(radix**(i-1)) % (radix**i)].append(j) del lists[:] for z in bucket: lists += z del z[:] return lists
7 總結
各種排序方法比較
簡單排序中直接插入最好,快速排序最快,當文件為正序時,直接插入和冒泡均最佳。
影響排序效果的因素
因為不同的排序方法適應不同的應用環境和要求,所以選擇合適的排序方法應綜合考慮下列因素:
①待排序的記錄數目n; ②記錄的大小(規模); ③關鍵字的結構及其初始狀態;
④對穩定性的要求; ⑤語言工具的條件; ⑥存儲結構; ⑦時間和輔助空間復雜度等。
不同條件下,排序方法的選擇
(1)若n較小(如n≤50),可采用直接插入或直接選擇排序。當記錄規模較小時,直接插入排序較好;否則因為直接選擇移動的記錄數少於直接插人,應選直接選擇排序為宜。
(2)若文件初始狀態基本有序(指正序),則應選用直接插人、冒泡或隨機的快速排序為宜;
(3)若n較大,則應采用時間復雜度為O(nlgn)的排序方法:快速排序、堆排序或歸並排序。
快速排序是目前基於比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分布時,快速排序的平均時間最短;
堆排序所需的輔助空間少於快速排序,並且不會出現快速排序可能出現的最壞情況。這兩種排序都是不穩定的。 若要求排序穩定,則可選用歸並排序。但本章介紹的從單個記錄起進行兩兩歸並的 排序算法並不值得提倡,通常可以將它和直接插入排序結合在一起使用。先利用直接插入排序求得較長的有序子文件,然后再兩兩歸並之。因為直接插入排序是穩定的,所以改進后的歸並排序仍是穩定的。
(4)在基於比較的排序方法中,每次比較兩個關鍵字的大小之后,僅僅出現兩種可能的轉移,因此可以用一棵二叉樹來描述比較判定過程。
(5)有的語言(如Fortran,Cobol或Basic等)沒有提供指針及遞歸,導致實現歸並、快速(它們用遞歸實現較簡單)和基數(使用了指針)等排序算法變得復雜。此時可考慮用其它排序。
排序對比:
8 參考文獻
- 大話數據結構(程傑)
- 排序算法可視化
- 數據結構自考網
- 排序算法匯總
- 各種排序算法的分析以及java實現