前言
首先這里列出的大家熟知的排序算法:冒泡排序、插入排序、歸并排序、堆排序、快速排序等。對于能在O(n lgn)時間內進行排序的算法,歸并排序和堆排序達到了最壞的情況下的上界;快速排序在平均情況下達到了該上界。
這些算法都有一個共同點:在排序的最終結果中,各元素的次序依賴于它們之間的比較。我們稱這類排序算法為比較排序。
比較排序在排序的排序過程可以抽象成一棵決策樹。在最壞的情況下,任何比較排序算法都需要做Ω(n lgn)次比較。
線性時間排序
下面介紹兩種線性時間排序的算法:計數排序、基數排序。
計數排序
計數排序假設n個輸入的元素中每一個都是在[0,k]區間內的一個整數,其中k為某個整數。當k=O(n)時,排序的運行時間為θ(n)。
計數排序執行過程:
- 開辟計數數組空間,并遍歷待排序數組對帶排序數組進行計數賦值;
- 對計數數組進行疊加操作,得到元素所在的最后的位置;
- 遍歷待排序數組,根據得出的計數數組完成輸出數組的賦值;
計數排序圖例
計數排序Java 代碼:
/**
*
* @param a 數組
* @param k 范圍;例如 k equals 5 ,range is [0,4]
*/
public void countingSort(int[] a,int[] b,int k) {
if(k<0||(a==null||a.length<1)) return;
int[] c=new int[k];
//counting
for(int j=0;j<a.length;j++) {
c[a[j]]+=1;
}
//convert
for(int i=1;i<k;i++) {
c[i]=c[i]+c[i-1];
}
for(int j=(a.length-1);j>=0;j--) {
b[c[a[j]]-1]=a[j];
c[a[j]]-=1;
}
}
測試代碼:
public static void main(String[] args) {
//假設a數組中的數都是[0,10)之間的數
int[] a= {4,6,3,3,2,2,9,0,1,4,4,8,7,7};
//排序數組
int[] b=new int[a.length];
new RadixSort().countingSort(a, b, 10);
for(int i=0;i<b.length;i++) {
System.out.println(i+" is "+b[i]);
}
}
測試結果:
0 is 0
1 is 1
2 is 2
3 is 2
4 is 3
5 is 3
6 is 4
7 is 4
8 is 4
9 is 6
10 is 7
11 is 7
12 is 8
13 is 9
基數排序
基數排序是先按最低有效位進行排序解決排序問題,算法用到了穩定排序算法-計數排序。
基數排序的偽代碼很簡單:
RADIX_SORT(A,d)
for i =1 to d
use a stable sort to sort array A on digit i;
基數排序排序過程
基數排序Java 代碼:
/**
* 10的次方數的結果值
* @param d 次方數
* @return 10的n次方的結果
*/
private int tenPow(int d) {
int result=1;
for(int i=0;i<d;i++) {
result*=10;
}
return result;
}
/**
* 為計數排序優化的計數排序
* @param a 待排序數組
* @param b 輸出的數組
* @param k 范圍;例如 k equals 5 ,range is [0,9]
*/
public void countingSort4Radix(int[] a,int[] b,int k) {
if(k<0||(a==null||a.length<1)) return;
int[] temps=new int[a.length];
for(int x=0;x<temps.length;x++) {//copying
temps[x]=b[x];
}
int[] c=new int[k];
//counting
for(int j=0;j<a.length;j++) {
c[a[j]]+=1;
}
//convert
for(int i=1;i<k;i++) {
c[i]=c[i]+c[i-1];
}
for(int j=(a.length-1);j>=0;j--) {
b[c[a[j]]-1]=temps[j];
c[a[j]]-=1;
}
}
/**
* 基數排序
* @param a 待排序數組
* @param d 位數 如:834102 d eauals 6
*/
public void radixSort(int[] a,int d) {
//存放相應位數據的臨時數組
int[] digitsTemp=new int[a.length];
for(int j=0;j<d;j++) {
//1.為相應位賦值
for(int i=0;i<digitsTemp.length;i++) {
digitsTemp[i]=(a[i]/tenPow(j))%10;
System.out.println((j+1)+"位:"+"digitsTemp["+i+"] is "+ digitsTemp[i]);
}
//2.使用穩定的計數排序算法進行從低位到高位的按位排序
countingSort4Radix(digitsTemp, a, 10);
for(int x=0;x<a.length;x++) {
System.out.println(" radixing->"+x+" is "+a[x]);
}
}
}
測試代碼:
public static void main(String[] args) {
int[] x= {834102,634101,834112,512311};
new RadixSort().radixSort(x, 6);
System.out.println("-------基數排序的結果-------");
for(int i=0;i<x.length;i++) {
System.out.println(i+" is "+x[i]);
}
}
測試結果:
1位:digitsTemp[0] is 2
1位:digitsTemp[1] is 1
1位:digitsTemp[2] is 2
1位:digitsTemp[3] is 1
radixing->0 is 634101
radixing->1 is 512311
radixing->2 is 834102
radixing->3 is 834112
2位:digitsTemp[0] is 0
2位:digitsTemp[1] is 1
2位:digitsTemp[2] is 0
2位:digitsTemp[3] is 1
radixing->0 is 634101
radixing->1 is 834102
radixing->2 is 512311
radixing->3 is 834112
3位:digitsTemp[0] is 1
3位:digitsTemp[1] is 1
3位:digitsTemp[2] is 3
3位:digitsTemp[3] is 1
radixing->0 is 634101
radixing->1 is 834102
radixing->2 is 834112
radixing->3 is 512311
4位:digitsTemp[0] is 4
4位:digitsTemp[1] is 4
4位:digitsTemp[2] is 4
4位:digitsTemp[3] is 2
radixing->0 is 512311
radixing->1 is 634101
radixing->2 is 834102
radixing->3 is 834112
5位:digitsTemp[0] is 1
5位:digitsTemp[1] is 3
5位:digitsTemp[2] is 3
5位:digitsTemp[3] is 3
radixing->0 is 512311
radixing->1 is 634101
radixing->2 is 834102
radixing->3 is 834112
6位:digitsTemp[0] is 5
6位:digitsTemp[1] is 6
6位:digitsTemp[2] is 8
6位:digitsTemp[3] is 8
radixing->0 is 512311
radixing->1 is 634101
radixing->2 is 834102
radixing->3 is 834112
-------基數排序的結果-------
0 is 512311
1 is 634101
2 is 834102
3 is 834112
總結
- 計數排序 適用于k比較小的場景下,比如“某大型企業有兩萬名員工,希望根據年齡為員工們排序,并計算平均年齡?”,這個問題在不使用數據庫和磁盤相關算法(B樹)的前提下,計數排序是較優的解決方案。
- 基數排序是一種原地排序算法,不需要額外的空間進行輔助;但是我的代碼中計數排序是還是開辟了空間,這是一個值得優化的點。
以上,謝謝閱讀,希望你有所收獲!
算法導論公開課筆記(一)算法分析與設計
算法導論公開課筆記(二)快速排序和隨機化算法
算法導論公開課筆記(三)線性時間排序
算法導論公開課筆記(四)順序統計、中值
動態規劃算法
竟然有人,為這個文章贊賞了¥2.0,雖然寫博客的目的不是為了掙錢,但是也好開心!!!
謝謝匿名的大神的鼓勵,事事順利!