計數排序
學習基數排序之前首先學習計數排序。
計數排序假設每個元素都是在0到k之間的一個整數。
基數排序的基本思想,對於每個元素x,如果我們知道了小於x的元素的個數,就可以確定輸出數組中元素x的位置,那麼直接將元素x放到輸出數組中。比如有3小於x的元素,那在輸出數組中,x肯定位於第4個位置。
計數排序的演算法用虛擬碼描述為:
COUNTING-SORT(A,k)// 初始化數組Cfor i=0 to kC[i]=0// 統計A[j]元素出現的次數,儲存到C數組中for j=0 to A.lengthC[A[j]]=C[A[j]]+1// 統計小於等於A[j]元素的個數for k=0 to kC[K]=C[K-1]+C[K]// 將A中的元素放在B中正確的位置for i=A.length to 0B[C[A[i]]-1]=A[i]C[A[i]]=C[A[i]]-1
註:由於有可能有相同元素存在,所以,每次將A[i]元素放入B數組中,都要將C[A[j]]的值減一,這樣當遇到下一個值等於A[j]的元素時,該元素就能放在輸出數組中A[j]的前面。
計數排序的詳細過程如下
計數排序的關鍵代碼如下
public int[] countSort(int a[], int k) {int[] b = new int[a.length];int[] c = new int[k];for (int i = 0; i < c.length; i++) {c[i] = 0;}for (int i = 0; i < a.length; i++) {c[a[i]] += 1;}for (int i = 0; i < c.length; i++) {if (i != 0) {c[i] += c[i - 1];}}for (int i = a.length - 1; i >= 0; i--) {b[c[a[i]] - 1] = a[i];c[a[i]] = c[a[i]] - 1;}return b;}
計數排序的效能
很容易得到計數排序的時間複雜度為:T(n)=O(k+n),因此當k小於等於n,也就是當k=O(n),k和n同階時,採用計數排序的時間複雜度為T(n)=O(n)
同時,計數排序也是一種穩定的排序演算法。
基數排序
基數排序最初是用在打孔卡片製表機上的一種排序演算法,由Herman Hollerith發明,也就是IBM的創始人。
基數排序從最低為開始來排序的,從低位到高位,按位排序,按位排序必須是穩定的。
基數排序的詳細過程
基數排序演算法描述為
RADIX-SORT(A,d)for i=1 to duse a stable sort to sort arrat A on digit i
在這裡我們選擇計數排序。考慮常規情況,對[0...9]之間的數排序,k=10,且一般有k<<n,此時能達到最好的時間複雜度O(n)
基數排序的關鍵代碼,這裡以數組排序時按照十進位每位進行比較。
/** * 基數排序 * @param result 最終已排序的數組,共用一個節省空間的 * @param maxLen 待排序的數組中最大的位元 */public static void radixSort(int[] a,int[] result, int maxLen) {int flag = 1;// 儲存每輪要排序的位對應數組a的值int [] digitArr = new int[a.length];for(int i=0; i < maxLen; i++) {// 共比較的輪數flag *= 10;// b數組中對應的裝著a數組中每位的數,第一輪裝著各位,第二輪裝著十位元...for (int j = 0; j < digitArr.length; j++) {digitArr[j]=a[j]%flag;digitArr[j]=digitArr[j]/(flag/10);}countSort(a, digitArr,result,10);// 每一輪計數排序完後重新整理下一輪要排序的數組System.arraycopy(result, 0, a, 0,result.length);}}
調用計數排序的函數
/** * 計數排序 :對數組a中的元素按某些位排序 * @param tmp 要參與排序的當前位的值儲存在tmp中 * @param result 每次計數排序後的新的數組順序 */public static void countSort(int a[], int tmp[], int result[], int k) {int[] c = new int[k];for (int i = 0; i < c.length; i++) {c[i] = 0;}for (int i = 0; i < tmp.length; i++) {c[tmp[i]] += 1;}for (int i = 0; i < c.length; i++) {if (i != 0) {c[i] += c[i - 1];}}for (int i = tmp.length - 1; i >= 0; i--) {// 和計數排序唯一的差別在於賦值的時候用真實的資料result[c[tmp[i]] - 1] = a[i];c[tmp[i]] = c[tmp[i]] - 1;}}基數排序的效能
如果基數排序使用的穩定排序演算法的時間複雜度為O(n+k),那麼基數排序的時間複雜度為T(n)=O(d(n+k))
很容易理解要迴圈d輪,每輪耗時為O(n+k),於是總的耗時為O(d(n+k))
在此基礎上,從2^r進位來看,此時k為2^r,並且一個b位元要比較b/r輪。於是我們得到T(n)=O((b/r)(n+2^r))
對上式求導可得其最小值。此時r=lgn,此時T(n)=O((b/lgn)n),如果再取b=lgn,這時就能達到最少的已耗用時間,時間複雜度為T(n)=O(n)
基數排序也是穩定的排序演算法