標籤:style blog http color os io 資料 for
在前面三節排序演算法中,我們分別分析了不同策略,思想用於排序,而這些演算法都是基於資料間的比較來確定順序的。假設我不用比較,換一種思路,那麼就可以達到時間複雜度為O(n)的排序演算法,當然是以付出額外的空間為代價的。
一、基本思想
線性時間排序的演算法思想:
(1):在計數排序中,利用比x小或等的元素個數和的來確定x位置。比如2 5 4 9 1 6.9比其餘5個數都大,那就說明9 在排序後的第6個位置,這樣我們只要得到比某個數大的元素個數就能得到元素在排序後數組中的位置了。
(2):在桶排序中,是通過映射的方式,將大範圍內的數映射到[0,1]之間的10個桶在中,這樣再對每個桶中元素進行排序,將最後結果組合就行。本質也是分治的思想,不過這個分治有一定的規律,且分的子問題規模在一般情況下都很小,除了把元素都映射到少數幾個桶的情況。
1.計數排序
計數排序的思想在前面已經闡述過了,前面舉的例子也比較簡單,我們沒有考慮出現多個元素是相同的情況,如果出現多個元素是相同的情況,那應該怎麼處理呢?多個元素不可能在同一個位置,必然有先後順序,怎麼處理先後順序能決定計數排序的穩定性。下面結合圖和程式來說明
輸入的是數組a,計數的數組為c,利用c的數組標號來計數,這裡標號有可能超過10,標號0存的a中0的個數,1存的是1的個數。存好後,計算大於等於a中元素的個數,如右圖,小於等於0的元素有1。
首先初始化計數數組,大小要等於元素a中的最大元素,可以利用一個迴圈求的,在下面程式未考慮,僅設定為a長度的兩倍。然後計算每個元素的個數並存在c對應標號的位置。計算大於等於a中元素的個數,從右向左掃描數組a(為什麼不左向右)取元素成為c的標號,得出這一元素在輸出數組中的位置,把元素存於在一位置。因為可能有多個元素都存在這一位置,所以需要把c的數值減一。假設我後面掃描到相同大小的元素,我還是來取c的這個位置來確定排序後的元素所在位置,因為已經-1了,所以自然就排到前面的位置去了。這裡就保持了排序的穩定,也回答為什麼要從尾開始掃描。多個相同元素的前後位置保持一致。
#include <iostream>const int length=10;using namespace std;void counting_sort(int *a,int *b,int length){ int *c=new int[2*length]; for (int i=0;i<2*length;i++) c[i]=0; for (int j=0;j<length;j++) c[a[j]]=c[a[j]]+1; for (int k=1;k<2*length;k++) c[k]=c[k]+c[k-1]; for (int l=0;l<length;l++) { b[c[a[l]]-1]=a[l];//輸出的元素編號是從0開始 c[a[l]]--; }}int main(){ int a[length]={0,3,2,1,3,2,1,2,2,3}; int *b=new int[length]; counting_sort(a,b,length); for(int i=0;i<length;i++) cout<<b[i]<<" ";} //main end二:基數排序
基數排序是以計數排序為基礎的,實現在關鍵在於分離各個位元,以及在之前排序在基礎在進行排序。這裡的問題主要就是為什麼不同高位牌開始呢?假設從高位排開始,那麼對於每個位元就必須先儲存其他的,等最小的那個排好後,然後排大二小的。基數排序當然也可以用於位元不相等的資料,第一步就是求最大的位元,然後從低位開始應用計數排序,直到最高位。
#include <iostream>using namespace std;const int radix=10;void radixsort(int a[],int n){ //求數組的最大位元 int temp=0,bi=1,mbi=0,count[radix],rad=1; int *b=new int[n]; for (int i=0;i<n;i++) { temp=a[i]; while (temp/=10) { bi++; } if (bi>mbi) { mbi=bi; } bi=0; } //從低位開始對數組進行計數排序 for (int i=0;i<mbi;i++) { for (int j=0;j<radix;j++) count[j]=0; for (int k=0;k<n;k++) { count[a[k]/rad%10]++;//a[k]/rad%10:用於分離各個位,rad不斷變化。 } for (int j=1;j<radix;j++) count[j]+=count[j-1]; for (int k=n-1;k>=0;k--) { b[count[a[k]/rad%10]-1]=a[k]; count[a[k]/rad%10]--; } for (int k=0;k<n;k++) { a[k]=b[k]; } rad=rad*radix; }}int main(){ int temp,k=10; //int *data=new int [radix]; /*while (cin>>temp) { data[k]=temp; k++; } for(int i=0;i<10;i++) cout<<data[i]<<" "; cout<<endl;*/ int data[10]={713, 221, 93, 243, 55, 4, 8, 65, 39, 81}; radixsort(data,k); for(int i=0;i<10;i++) cout<<data[i]<<" "; cout<<endl; return 0;}
三、桶排序
參考http://www.roading.org/algorithm/introductiontoalgorithm/%E7%AC%AC%E5%85%AB%E7%AB%A0%EF%BC%884%EF%BC%89-%E6%A1%B6%E6%8E%92%E5%BA%8F%EF%BC%88bucket-sort%EF%BC%89.html