排序大的分類能夠分爲兩種:內排序和外排序。
java
在排序過程當中,所有記錄存放在內存,則稱爲內排序,若是排序過程當中須要使用外存,則稱爲外排序。算法
通常來講外排序分爲兩個步驟:預處理和合並排序。首先,根據可用內存的大小,將外存上含有n個紀錄的文件分紅若干長度爲t的子文件(或段);其次,利用內部排序的方法,對每一個子文件的t個紀錄進行內部排序。這些通過排序的子文件(段)一般稱爲順串(run),順串生成後即將其寫入外存。這樣在外存上就獲得了m個順串(m=[n/t])。最後,對這些順串進行歸併,使順串的長度逐漸增大,直到全部的待排序的概率成爲一個順串爲止。shell
內排序能夠分爲如下幾類:數組
(1)、插入排序:直接插入排序、折半插入排序、希爾排序。
性能
(2)、選擇排序:簡單選擇排序、堆排序。
ui
(3)、交換排序:冒泡排序、快速排序。spa
外排序能夠分爲一下幾類(既使用內部存儲也使用外部存儲,內存不夠時建議使用):code
(4)、歸併排序orm
(5)、基數排序htm
穩定性:就是能保證排序前兩個相等的數據其在序列中的前後位置順序與排序後它們兩個前後位置順序相同。再簡單具體一點,若是A i == A j,Ai 原來在 Aj 位置前,排序後 Ai 仍然是在 Aj 位置前。
不穩定:簡單選擇排序、快速排序、希爾排序、堆排序不是穩定的排序算法
穩定:冒泡排序、直接插入排序、折半插入排序,歸併排序和基數排序都是穩定的排序算法。
平均時間複雜度
O(n^2):直接插入排序,簡單選擇排序,冒泡排序。
在數據規模較小時(9W內),直接插入排序,簡單選擇排序差很少。當數據較大時,冒泡排序算法的時間代價最高。性能爲O(n^2)的算法基本上是相鄰元素進行比較,基 本上都是穩定的。
O(nlogn):快速排序,歸併排序,希爾排序,堆排序。
其中,快排是最好的, 其次是歸併和希爾,堆排序在數據量很大時效果明顯。
•思想:每步將一個待排序的記錄,按其順序碼大小插入到前面已經排序的字序列的合適位置,直到所有插入排序完爲止。
•關鍵問題:在前面已經排好序的序列中找到合適的插入位置。
•方法:
–直接插入排序
–折半插入排序
–希爾排序
private static void directInsertSort(int[] array) { for(int i=0;i<array.length;i++){ for(int j=0;j<i;j++){ if(array[i]<array[j]){ int temp=array[i]; System.arraycopy(array,j,array,j+1,i-j); array[j]=temp; } } } }
private static void binaryInsertSort(int[] array) { for(int i=1;i<array.length;i++){ int tempData=array[i]; int low=0; int high=i-1; while(low<=high){ int middle=(low+high)/2; if(array[middle]<tempData) low=middle+1; else high=middle-1; } System.arraycopy(array,low,array,low+1,i-low); array[low]=tempData; } }
圖例:
/** * shell排序算法 * 增量h=(h*3)+1; 這個增量公式是由Knuth給出的 * 若是不是很瞭解的話請百度一下吧 * @param array */ private static void shellSort(int[] array) { //首先根據數組的長度肯定增量的最大值 int h=1; // 按h * 3 + 1獲得增量序列的最大值 while(h <= array.length / 3) h = h * 3 + 1; //進行增量查找和排序 while(h>=1){ for(int i=h;i<array.length;i++){ for(int j=i;j >= h && array[j] < array[j-h];j -= h){ int temp = array[j]; array[j] = array[j-h]; array[j-h] = temp; } } h = h/3; } }
public static void directSelectionSort(int[] array){ for(int i=0;i<array.length-1;i++){ for(int j=i+1;j<array.length;j++){ if(array[i]>array[j]){ int temp = array[i]; array[i] = array[j]; array[j] = temp; } } } }
堆排序優於簡單選擇排序的緣由:直接選擇排序中,爲了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,而後在R[2..n]中選出關鍵字最小的記錄,又須要作n-2次比較。事實上,後面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經作過,但因爲前一趟排序時未保留這些比較結果,因此後一趟排序時又重複執行了這些比較操做。堆排序可經過樹形結構保存部分比較結果,可減小比較次數。堆排序的最壞時間複雜度爲O(nlogn)。堆序的平均性能較接近於最壞性能。因爲建初始堆所需的比較次數較多,因此堆排序不適宜於記錄數較少的文件。
public static void buildMInHeap(int[] array){ for(int i=array.length-1;i>0;i--){ for(int j=(i-1)/2;j>=0;j--){ //i是奇數,存在一個節點只有一個葉子節點 if((2*j+1==i)&&(i%2!=0)){ if(array[j]<array[2*j+1]) swap(array,j,2*j+1); }else{ if(array[j]<array[2*j+1]) swap(array,j,2*j+1); if(array[j]<array[2*j+2]) swap(array,j,2*j+2); } } swap(array,0,i); } } private static void swap(int[] array,int i,int j){ int temp=array[i]; array[i]=array[j]; array[j]=temp; }
public static int[] bubbleSort(int[] array){ if(array == null) return array; int len = array.length; for(int i = len - 1; i > 0; i--){ for(int j = 0; j < i; j++){ if(array[j] > array[j + 1]){ int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; } } } return array; }
冒泡排序是一種穩定的排序方法。
public static void quickSort(int[] array,int start,int end) { if(start==end) return; int i=start+1; int j=end; while(i<j){ for(;array[i]<array[start]&&i<j;i++); for(;array[j]>array[start]&&j>i;j--); swap(array,i,j); } if(array[i]<array[start]) swap(array,i,start); quickSort(array,start,i-1); quickSort(array,i,end); } public static void swap(int[] array, int i, int j){ int temp = array[i]; array[i] = array[j]; array[j] = temp; }分析:
快速排序是不穩定的排序。
快速排序的時間複雜度爲O(nlogn)。
當n較大時使用快排比較好,當序列基本有序時用快排反而很差。
private static void sort(int[] array,int low,int high) { if(low < high){ int middle=(low + high)/2; //遞歸處理相關的合併事項 sort(array,low,middle); sort(array,middle+1,high); merge(array,low,middle,high); } } private static void merge(int[] array, int low, int middle, int high) { //建立一個臨時數組用來存儲合併後的數據 int[] temp=new int[array.length]; int i = low; int j = middle+1; int k = low; while(i <= middle && j <= high){ if(array[i]<array[j]) temp[k++]=array[i++]; else temp[k++]=array[j++]; } //處理剩餘未合併的部分 while(i <= middle) temp[k++]=array[i++]; while(j <= high) temp[k++]=array[j++]; //將臨時數組中的內容存儲到原數組中 while(low <= high) array[low]=temp[low++]; }分析:
歸併排序是穩定的排序方法。
歸併排序的時間複雜度爲O(nlogn)。
速度僅次於快速排序,爲穩定排序算法,通常用於對整體無序,可是各子項相對有序的數列。
public static int[] radixSort(int[] 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<Integer>> alist = new ArrayList<ArrayList<Integer>>();
for (int i = 0; i < 10; i++){
ArrayList<Integer> queue = new ArrayList<Integer>();
alist.add(queue);
}
//進行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);
alist.get(x).add(array[j]);
}
//收集
int count = 0;
for (int j = 0; j < 10; j++){
ArrayList<Integer> queue = alist.get(j);
while(queue.size()>0){
array[count++] = queue.remove(0);
}
}
}
return array;
}
基數排序是穩定的排序算法。
基數排序的時間複雜度爲O(d(n+r)),d爲位數,r爲基數。