開發環境: Microsoft C++ 2010 sp1
Merge Sort( 合併排序 ) :
主要演算法假設是,在n段以排序過的資料裡面,要把n段的資料合併並排序成一段,
所以只要每兩段做一次資料的比較並且合併成 n / 2 段,
然後再把n / 2段的資料進行比較與合併成 n / 4 段,
重複以上的過程直到最後剩下一段的資料,最後一段資料即為所求。
下圖為排序順序:
陣列:
遞迴:
Heap Sort( 堆積排序 ):
Heap為一個完整二元樹( Complete Binary Tree ),
而Min Heap的定義是,父節點會比子節點的值還要小,Max Heap反之,
建構Min Heap的步驟是,逐一把資料加在完整樹的最後一個節點,
再把最後一個節點的值往根接點的方向來比較,把值往上送到保持heap的地方,
以下為建構Min Heap的例子:
如果要用來做排序,直接把heap的根節點輸出,接下來把最後一個節點與根結點交換,
最後再把根結點往下沉到該擺放的地方,如下圖所示,
以下為實作的code:
Radix Sort( 基底排序法 )
其演算法主要的概念為,將整數位元切割成不同的數字,接著按每個位數進行比較,
假設基底為10,要排序的資料分別為 5 3 2 1 30 20 52 11 (x[i], 0 <= i < 8 ),
run1 ( 個位數字 ) :
radix | 0 1 2 3 4 5 6 7 8 9
---------------------------------
5
3
2
1
30
20
52
11
----------------------------------
個數 2 2 2 1 0 1 0 0 0 0
累加個數 y[i] = { 2 , 4 , 6 , 7 , 7 , 8 , 8 , 8 , 8 , 8 } ,這個即為填回去的陣列的最高陣列位置,
用上面的y[i]來進行陣列的重新分配,最原本x最後面開始,
x[7] = 11 , 11 / 1 % 10 = 1 推論 radix = 1,所以對回去y[i]之後,
11這個值的位置要從7變成 --y[1] = 3,也就是新排列的陣列x' = { X , X , X , 11 , X , X , X , X },
x[6] = 52 , 52 / 1 % 10 = 2 推論 radix = 2,所以對回去y[i]之後,
52這個值的位置要從6變成 --y[2] = 5,也就是新排列的陣列x' = { X , X , X , 11 , X , 52 , X , X },
x[5] = 20 , 20 / 1 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
20這個值的位置要從5變成 --y[0] = 1,也就是新排列的陣列x' = { X , 20 , X , 11 , X , 52 , X , X },
x[4] = 30 , 30 / 1 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
30這個值的位置要從4變成 --y[0] = 0,也就是新排列的陣列x' = { 30 , 20 , X , 11 , X , 52 , X , X },
x[3] = 1 , 1 / 1 % 10 = 1 推論 radix = 1,所以對回去y[i]之後,
1這個值的位置要從3變成 --y[1] = 2,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , X , 52 , X , X },
x[2] = 2 , 2 / 1 % 10 = 2 推論 radix = 2,所以對回去y[i]之後,
2這個值的位置要從2變成 --y[2] = 4,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , 2 , 52 , X , X },
x[1] = 3 , 3 / 1 % 10 = 3 推論 radix = 3,所以對回去y[i]之後,
3這個值的位置要從1變成 --y[3] = 6,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , 2 , 52 , 3 , X },
x[0] = 5 , 5 / 1 % 10 = 5 推論 radix = 5,所以對回去y[i]之後,
5這個值的位置要從0變成 --y[5] = 7,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , 2 , 52 , 3 , 5 },
Merge Sort( 合併排序 ) :
主要演算法假設是,在n段以排序過的資料裡面,要把n段的資料合併並排序成一段,
所以只要每兩段做一次資料的比較並且合併成 n / 2 段,
然後再把n / 2段的資料進行比較與合併成 n / 4 段,
重複以上的過程直到最後剩下一段的資料,最後一段資料即為所求。
下圖為排序順序:
以下為實作的code:
有兩種版本,一為陣列(陣列位置數到快瘋了),二為遞迴,
陣列:
#include#include const int BASE = 1000 ; //快速排序法所需要的比較子 int comp( const void *p , const void *q ) { int *_p = (int *)p ; int *_q = (int *)q ; if ( *_p == *_q ) return 0 ; else { if ( *_p < *_q ) return -1 ; else return 1 ; } } void printRange( int *x , int s , int n ) { int i ; for ( i = s ; i < n ; i++ ) { std::cout << x[i] << " "; } std::cout << std::endl ; } int main() { int m , n = 100000 - 3, i ; //輸入資料 int *x = new int[1000000] ; //暫存資料 int *y = new int[1000000] ; //存放第n個區段裡面有幾筆資料 int table[ 1000000 / 2 / BASE + 1] ; while ( std::cin >> n ) { for ( i = 0 ; i < n ; i++ ) { std::cin >> x[i] ; if ( (i+1) % BASE == 0 ) { //排序每個區段的資料 qsort( (void *)( x + ( (i / BASE) * BASE ) ) , BASE , sizeof(int) , comp ) ; } } if ( n % BASE != 0 && n > BASE ) { qsort( &x[ (n / BASE) * BASE ] , n % BASE , sizeof(int) , comp ) ; } //切割splits個BASE大小的區段 int splits = (int)( (double)n / (double)BASE + 0.5 ) ; int runs = 1 , j ; int a , b , c ; //如果splits > 0才需要Merge if ( splits > 0 ) { //每個區段為BASE個 for ( a = 0 ; a < splits ; a++ ) table[a] = BASE ; //如果輸入的資料數不能整除BASE個 if ( n % BASE != 0 ) table[splits-1] = n % BASE ; //當切割數 > 1時才開始Merge while ( splits > 1 ) { c = ( splits % 2 == 0 ) ? splits : splits - 1 ; //合併 n 個區段至 n / 2個 for ( m = 0 ; m < c ; m += 2 ) { a = 0 ; b = 0 ; int cmp1 = x[m * BASE * runs + a] ; int cmp2 = x[ (m+1) * BASE * runs + b] ; //合併兩個區段 for ( j = 0 ; a < table[m * runs] || b < table[(m + 1) * runs] ; j++ ) { if ( a == table[m * runs] ) { y[j] = cmp2 ; cmp2 = x[ (m+1) * BASE * runs + (++b)] ; } else if ( b == table[(m + 1) * runs] ) { y[j] = cmp1 ; cmp1 = x[ m * BASE * runs + (++a)] ; } else { if ( cmp1 <= cmp2 ) { y[j] = cmp1 ; cmp1 = x[ m * BASE * runs + (++a)] ; } else { y[j] = cmp2 ; cmp2 = x[ (m+1) * BASE * runs + (++b)] ; } } } //放大這個區段的大小 table[ m * runs ] = table[ m * runs ] + table[ (m + 1) * runs ] ; //寫回去原本的陣列裡面 for ( a = m * BASE * runs , b = 0 ; a < m * BASE * runs + table[ m * runs ] ; a++ , b++) x[a] = y[b] ; } //切割數目 / 2 splits = (int)((double)splits / 2. + 0.5) ; runs *= 2 ; } } else { qsort( x , n , sizeof(int) , comp ) ; table[0] = n ; } printRange( x , 0 , n ) ; } return 0; }
遞迴:
#include#include const int BASE = 1000 ; //每個切割資料要用快速排序法的比較子 int comp( const void *p , const void *q ) { int *_p = (int *)p ; int *_q = (int *)q ; if ( *_p == *_q ) return 0 ; else { if ( *_p < *_q ) return -1 ; else return 1 ; } } //Merge Sort的主函式 void MergeSort( int *x , int numberElement ) { if ( numberElement <= BASE ) { //如果切割大小 >= 目前傳入的陣列大小 qsort( x , numberElement , sizeof(int) , comp ) ; //使用快速排序法排序整個element( conqure ) } else { int pad = numberElement % 2 == 0 ? 0 : 1 , i , j = 0 ; int *z = x + numberElement / 2 ; //左排序(divide) MergeSort( x , numberElement / 2 ) ; //右排序(divide) MergeSort( z , numberElement / 2 + pad ) ; //組合在一起 int a = 0 , b = 0 ; const int divide = (numberElement / 2) ; // const int a_range = divide ; const int b_range = divide + pad ; int *y = new int[numberElement] ; //把左右兩邊各自排序完成的資料使用Merge的規則進行排序 for ( i = 0 ; i < divide * 2 ; i++ , j++ ) { if ( a == a_range ) { y[j] = z[b++] ; } else if ( b == b_range ) { y[j] = x[a++] ; } else { if ( x[a] < z[b] ) { y[j] = x[a++] ; } else { y[j] = z[b++] ; } } } //判斷最後一個 if ( a != a_range ) y[j] = x[a] ; else if ( b != b_range ) y[j] = z[b] ; //排序後的資料設定回整個陣列 for ( i = 0 ; i < numberElement ; i++ ) x[i] = y[i] ; delete []y ; } } //印出陣列內容 void printRange( int *x , int s , int n ) { int i ; for ( i = s ; i < n ; i++ ) { std::cout << x[i] << " "; } std::cout << std::endl ; } int main() { int n , i ; int *x = new int[1000000] ; while ( std::cin >> n ) { for ( i = 0 ; i < n ; i++ ) std::cin >> x[i] ; MergeSort( x , n ) ; printRange( x , 0 , n ) ; } return 0; }
Heap Sort( 堆積排序 ):
Heap為一個完整二元樹( Complete Binary Tree ),
而Min Heap的定義是,父節點會比子節點的值還要小,Max Heap反之,
建構Min Heap的步驟是,逐一把資料加在完整樹的最後一個節點,
再把最後一個節點的值往根接點的方向來比較,把值往上送到保持heap的地方,
以下為建構Min Heap的例子:
如果要用來做排序,直接把heap的根節點輸出,接下來把最後一個節點與根結點交換,
最後再把根結點往下沉到該擺放的地方,如下圖所示,
以下為實作的code:
#includevoid swap( int *a , int *b ) { int x = *a ; *a = *b ; *b = x ; } int main() { int *x = new int [1000001] ; int n , i , m , j ; while ( std::cin >> n ) { int tmp , parent ; //一開始先按照heap的資料結構建立一個min heap std::cin >> x[1] ; //第0個位置不使用 for ( i = 1 ; i < n ; i++ ) { std::cin >> m ; //complete binary tree x[i + 1] = m ; //以輸入的值往完整二元樹的祖先,一直交換找,直到小孩比父節點還要小為止 tmp = (i + 1) ; parent = (i + 1) / 2 ; while ( parent > 0 ) { //小孩節點與父節點比較小 if ( x[parent] > x[tmp] ) { swap( &x[tmp] , &x[parent] ) ; //交換兩個數值 } else //如果小孩比父節點還要小了 break ; //不需要往下做下去 parent /= 2 ; //繼續往下尋找 tmp /= 2 ; } } for ( j = n - 1 ; j >= 2 ; --j ) { //輸出根節點 std::cout << x[1] << " " ; //把根節點替換到這個heap的最後一個位置 swap( &x[1] , &x[j+1] ) ; //把根節點的值往下沉,以保證這個heap結構沒有錯誤 parent = 1 ; tmp = parent * 2 ; while ( tmp < j ) { //找兩個子節點最小的那個 if ( x[tmp] > x[tmp+1] ) tmp = tmp + 1 ; //如果父節點大於子節點的值 if ( x[parent] > x[tmp] ) swap( &x[parent] , &x[tmp] ) ; //把數值往下替換 else //如果沒有就節束動作, break ; //繼續往下沉 parent = tmp ; tmp = parent * 2 ; } } if ( x[1] < x[2] ) std::cout << x[1] << " " << x[2] ; else std::cout << x[2] << " " << x[1] ; std::cout << std::endl ; } delete []x ; return 0; }
Radix Sort( 基底排序法 )
其演算法主要的概念為,將整數位元切割成不同的數字,接著按每個位數進行比較,
假設基底為10,要排序的資料分別為 5 3 2 1 30 20 52 11 (x[i], 0 <= i < 8 ),
run1 ( 個位數字 ) :
radix | 0 1 2 3 4 5 6 7 8 9
---------------------------------
5
3
2
1
30
20
52
11
----------------------------------
個數 2 2 2 1 0 1 0 0 0 0
累加個數 y[i] = { 2 , 4 , 6 , 7 , 7 , 8 , 8 , 8 , 8 , 8 } ,這個即為填回去的陣列的最高陣列位置,
用上面的y[i]來進行陣列的重新分配,最原本x最後面開始,
x[7] = 11 , 11 / 1 % 10 = 1 推論 radix = 1,所以對回去y[i]之後,
11這個值的位置要從7變成 --y[1] = 3,也就是新排列的陣列x' = { X , X , X , 11 , X , X , X , X },
x[6] = 52 , 52 / 1 % 10 = 2 推論 radix = 2,所以對回去y[i]之後,
52這個值的位置要從6變成 --y[2] = 5,也就是新排列的陣列x' = { X , X , X , 11 , X , 52 , X , X },
x[5] = 20 , 20 / 1 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
20這個值的位置要從5變成 --y[0] = 1,也就是新排列的陣列x' = { X , 20 , X , 11 , X , 52 , X , X },
x[4] = 30 , 30 / 1 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
30這個值的位置要從4變成 --y[0] = 0,也就是新排列的陣列x' = { 30 , 20 , X , 11 , X , 52 , X , X },
x[3] = 1 , 1 / 1 % 10 = 1 推論 radix = 1,所以對回去y[i]之後,
1這個值的位置要從3變成 --y[1] = 2,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , X , 52 , X , X },
x[2] = 2 , 2 / 1 % 10 = 2 推論 radix = 2,所以對回去y[i]之後,
2這個值的位置要從2變成 --y[2] = 4,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , 2 , 52 , X , X },
x[1] = 3 , 3 / 1 % 10 = 3 推論 radix = 3,所以對回去y[i]之後,
3這個值的位置要從1變成 --y[3] = 6,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , 2 , 52 , 3 , X },
x[0] = 5 , 5 / 1 % 10 = 5 推論 radix = 5,所以對回去y[i]之後,
5這個值的位置要從0變成 --y[5] = 7,也就是新排列的陣列x' = { 30 , 20 , 1 , 11 , 2 , 52 , 3 , 5 },
run2 ( 10位數字x' = x ) :
radix | 0 1 2 3 4 5 6 7 8 9
---------------------------------
30
20
1
11
2
52
3
5
----------------------------------
個數 4 1 1 1 0 1 0 0 0 0
累加個數 y[i] = { 4 , 5 , 6 , 7 , 7 , 8 , 8 , 8 , 8 , 8 } ,這個即為填回去的陣列的最高陣列位置,
用上面的y[i]來進行陣列的重新分配,最原本x最後面開始,
x[7] = 5 , 5 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
5這個值的位置要從7變成 --y[0] = 3,也就是新排列的陣列x' = { X , X , X , 5 , X , X , X , X },
x[6] = 3 , 3 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
3這個值的位置要從6變成 --y[0] = 2,也就是新排列的陣列x' = { X , X , 3 , 5 , X , X , X , X },
x[5] = 52 , 52 / 10 % 10 = 5 推論 radix = 5,所以對回去y[i]之後,
52這個值的位置要從5變成 --y[5] = 7,也就是新排列的陣列x' = { X , X , 3 , 5 , X , X , X , 52 },
x[4] = 2 , 2 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
2這個值的位置要從4變成 --y[0] = 1,也就是新排列的陣列x' = { X , 2 , 3 , 5 , X , X , X , 52 },
x[3] = 11, 11 / 10 % 10 = 1 推論 radix = 1,所以對回去y[i]之後,
11這個值的位置要從3變成 --y[1] = 4,也就是新排列的陣列x' = { X , 2 , 3 , 5 , 11 , X , X , 52 },
x[2] = 1 , 1 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
1這個值的位置要從2變成 --y[0] = 0,也就是新排列的陣列x' = { 1 , 2 , 3 , 5 , 11 , X , X , 52 },
x[1] = 20 , 20 / 10 % 10 = 2 推論 radix = 2,所以對回去y[i]之後,
20這個值的位置要從1變成 --y[2] = 5,也就是新排列的陣列x' = { 1 , 2 , 3 , 5 , 11 , 20 , X , 52},
x[0] = 30 , 30 / 10 % 10 = 3 推論 radix = 3,所以對回去y[i]之後,
30這個值的位置要從0變成 --y[3] = 6,也就是新排列的陣列x' = { 1 , 2 , 3 , 5 , 11 , 20 , 30 , 52 },
最後沒有百位數字,所以停止排序 x' 即為所求。
以下為實作的code:
參考網站 : http://zerojudge.tw/ShowProblem?problemid=a233
radix | 0 1 2 3 4 5 6 7 8 9
---------------------------------
30
20
1
11
2
52
3
5
----------------------------------
個數 4 1 1 1 0 1 0 0 0 0
累加個數 y[i] = { 4 , 5 , 6 , 7 , 7 , 8 , 8 , 8 , 8 , 8 } ,這個即為填回去的陣列的最高陣列位置,
用上面的y[i]來進行陣列的重新分配,最原本x最後面開始,
x[7] = 5 , 5 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
5這個值的位置要從7變成 --y[0] = 3,也就是新排列的陣列x' = { X , X , X , 5 , X , X , X , X },
x[6] = 3 , 3 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
3這個值的位置要從6變成 --y[0] = 2,也就是新排列的陣列x' = { X , X , 3 , 5 , X , X , X , X },
x[5] = 52 , 52 / 10 % 10 = 5 推論 radix = 5,所以對回去y[i]之後,
52這個值的位置要從5變成 --y[5] = 7,也就是新排列的陣列x' = { X , X , 3 , 5 , X , X , X , 52 },
x[4] = 2 , 2 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
2這個值的位置要從4變成 --y[0] = 1,也就是新排列的陣列x' = { X , 2 , 3 , 5 , X , X , X , 52 },
x[3] = 11, 11 / 10 % 10 = 1 推論 radix = 1,所以對回去y[i]之後,
11這個值的位置要從3變成 --y[1] = 4,也就是新排列的陣列x' = { X , 2 , 3 , 5 , 11 , X , X , 52 },
x[2] = 1 , 1 / 10 % 10 = 0 推論 radix = 0,所以對回去y[i]之後,
1這個值的位置要從2變成 --y[0] = 0,也就是新排列的陣列x' = { 1 , 2 , 3 , 5 , 11 , X , X , 52 },
x[1] = 20 , 20 / 10 % 10 = 2 推論 radix = 2,所以對回去y[i]之後,
20這個值的位置要從1變成 --y[2] = 5,也就是新排列的陣列x' = { 1 , 2 , 3 , 5 , 11 , 20 , X , 52},
x[0] = 30 , 30 / 10 % 10 = 3 推論 radix = 3,所以對回去y[i]之後,
30這個值的位置要從0變成 --y[3] = 6,也就是新排列的陣列x' = { 1 , 2 , 3 , 5 , 11 , 20 , 30 , 52 },
最後沒有百位數字,所以停止排序 x' 即為所求。
以下為實作的code:
#includeint main() { int *x = NULL , *y = NULL ; int n , i ; while ( std::cin >> n ) { x = new int[n] ; y = new int[n] ; int max = 0 ; for ( i = 0 ; i < n ; i++ ) { std::cin >> x[i] ; //尋找最大值 max = x[i] > max ? x[i] : max ; } //從個位數字開始 int exp = 1 ; //籃子,來放每個數值的索引位置 int bucket[10] ; //排列到最大數字的位元數 while ( max / exp > 0 ) { //初始化每個籃子 for ( i = 0 ; i < 10 ; i++ ) bucket[i] = 0 ; //找到每個數字要擺放的陣列位置,並記錄每個籃子的大小 for ( i = 0 ; i < n ; i++ ) bucket[x[i] / exp % 10]++ ; //根據每個籃子的大小,計算出籃子中的元素的最大陣列位置 for ( i = 0 ; i < 9 ; i++ ) bucket[i+1] += bucket[i] ; //根據上面計算出來的籃子陣列位置,把數值填到對應的位置 for ( i = n - 1 ; i >= 0 ; --i ) y[ --bucket[ x[i] / exp % 10 ] ] = x[i] ; //最後拷貝一份到原始陣列中 for ( i = 0 ; i < n ; i++ ) x[i] = y[i] ; exp *= 10 ; } for ( i = 0 ; i < n ; i++ ) std::cout << x[i] << " " ; std::cout << std::endl ; delete []x ; delete []y ; } return 0; }
參考網站 : http://zerojudge.tw/ShowProblem?problemid=a233
沒有留言:
張貼留言