常见排序算法及实现
2019獨角獸企業重金招聘Python工程師標準>>>
一、冒泡排序
???? 冒泡排序(Bubble Sort)是先從數組第一個元素開始,依次比較相鄰兩個數,若前者比后者
大,就將兩者交換位置,然后處理下一對,依此類推,不斷掃描數組,直到完成排序。
???? 這個算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端,故名。
1、算法原理
???? 冒泡排序算法的運作如下:(從后往前)
??? 1)比較相鄰的元素。如果第一個比第二個大,就交換它們兩個。
??? 2)對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元
????????? 素應該會是最大的數。
??? 3)針對所有的元素重復以上的步驟,除了最后一個。
??? 4)持續每次對越來越少的元素重復上面的步驟,知道沒有任何一對數字需要比較。
2、算法分析
??? 時間復雜度:
? ? 若文件的初始狀態是正序的,一趟掃描即可完成排序。所需的的關鍵字比較次數為最小n-1和
記錄移動次數均為最小0。所以最好的時間復雜度為O(n)。 ?
??? 若初始文件是反序的,需要進行趟排序。每趟排序要進行次關鍵字的比較(1≤i≤n-
1),且每次比較都必須移動記錄三次來達到交換記錄位置。在這種情況下,比較和移動次數均
達到最大值。所以最壞時間復雜度為O(n^2)。
???? 因此,冒泡排序總的平均時間復雜度為O(n^2)。
??? 算法穩定性:
? (穩定性的解釋:假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排
序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列
中,ri仍在rj之前,則稱這種排序算法是穩定的;否則稱為不穩定的。)
??? 冒泡排序就是把小的元素往前調或者把大的元素往后調。比較是相鄰的兩個元素比較,交換
也發生在這兩個元素之間。所以,如果兩個元素相等,則不必交換;如果兩個相等的元素沒有相
鄰,那么即使通過前面的兩兩交換把兩個相鄰起來,這時候也不會交換,所以相同元素的前后
元素的前后順序并沒有改變,所以冒泡排序是一種穩定排序算法。
3、算法實現
/*冒泡排序 */#include <iostream> #include <stdio.h>void bubble_sort( int *array, int length ) {if( array == NULL || length <= 0 ){printf( "invalid input.\n" );return;}int temp;for( int i = 0; i < length - 1; i++ )for( int j = 0; j < length - 1 - i; j++ ){if( array[ j ] > array[ j + 1 ] ){temp = array[ j ];array[ j ] = array[ j + 1 ];array[ j+ 1 ] = temp;}} }void Test( const char* testName, int *array, int length ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );bubble_sort( array, length );printf( "after bubble sort, the result is: \n" );for( int i = 0; i < length; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int length = 6;int array[ ] = { 4, 2, 6, 7, 1, 5 };Test( "Test1", array, length ); }// 有重復數字 void Test2() {int length = 6;int array[ ] = { 4, 2, 6, 2, 1, 5 };Test( "Test2", array, length ); }// 輸入數組為空 void Test3() {Test( "Test3", NULL, -1 ); }int main() {Test1();Test2();Test3();return 0; }?
二、選擇排序
??? 選擇排序(Selection Sort)簡單而低效。它線性逐一掃描數組元素,從中挑出最小的元素,
將它移動到最前面(也就是與最前面的元素交換)。然后,再次線性掃描數組,找到第二小的
元素,并移到前面,如此反復,直到全部元素各歸其位。
?? 1、算法原理
??? n個記錄的文件的直接選擇排序可經過n-1趟直接選擇排序得到有序結果:
?? 1)初始狀態:無序區為R[1...n],有序區為空。
?? 2)第一趟排序
???????? 在無序區R[1...n]中選出關鍵字最小的記錄R[k],將它與無序區的第一個記錄R[1]交換,使
R[1...1]和R[2...n]分別變為記錄個數增加1個的新有序區和記錄個數較少1個的新無序區。
?? ......
? 3)第i趟排序
??????? 第i趟排序開始時,當前有序區和無序區分別為R[1...i-1]和R[i...n]。該趟排序從當前無序區中
選出關鍵字最小的記錄R[k],將它與無序區第一個記錄R交換,使R[1...i]和R分別標為記錄個數
增加1個的新有序區和記錄個數較少1個的新無序區。
?? 2、算法分析
??? 時間復雜度:
??? 選擇排序的交換操作介于 0 和 (n - 1) 次之間。選擇排序的比較操作為 n (n - 1) / 2 次之間。
選擇排序的賦值操作介于 0 和 3 (n - 1) 次之間。比較次數O(n^2),比較次數與關鍵字的初始
狀態無關,總的比較次數N=(n-1)+(n-2)+...+1=n*(n-1)/2。所以復雜度為O(n^2)。
?? 穩定性:
?? 選擇排序是給每個位置選擇當前元素最小的,比如給第一個位置選擇最小的,在剩余元素里面
給第二個元素選擇第二小的,依次類推,直到第n-1個元素,第n個 元素不用選擇了,因為只剩
下它一個最大的元素了。那么,在一趟選擇,如果一個元素比當前元素小,而該小的元素又出
現在一個和當前元素相等的元素后面,那么 交換后穩定性就被破壞了。舉個例子,序列5 8 5 2
9,我們知道第一遍選擇第1個元素5會和2交換,那么原序列中兩個5的相對前后順序就被破壞
了,所以選擇排序是一個不穩定的排序算法。
3、算法實現
/*選擇排序。 */#include <iostream> #include <stdio.h>void SelectSort( int *array, int length ) {if( array == NULL || length <= 0 ){printf( "invaild input.\n" );return;}int index = 0;// 每次循環只進行一次交換,最多進行len - 1次循環,比冒泡進行交換的次數少。for( int i = 0; i < length - 1; i++ ){// 第一次排序時,已經進行一次大循環,因此已經排好了1個元素// 已排好序的元素0,...,i-2,i-1// 待排元素為i,i+1,...,length-1index = i;for( int j = i + 1; j < length; j++ ){if( array[ j ] < array[ index ] ){index = j;}}// 交換if( index != i ){int temp = array[ i ];array[ i ] = array[ index ];array[ index ] = temp;}} }void Test( const char *testName, int *array, int length ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );SelectSort( array, length );printf( "after select sort, the result is: \n" );for( int i = 0; i < length; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int length = 6;int array[ ] = { 4, 2, 6, 7, 1, 5 };Test( "Test1", array, length ); }// 有重復數字 void Test2() {int length = 6;int array[ ] = { 4, 2, 6, 2, 1, 5 };Test( "Test2", array, length ); }// 輸入數組為空 void Test3() {int array[ ] = {};Test( "Test3", array, 0 ); }// 輸入數組為null,且長度異常 void Test4() {Test( "Test4", NULL, -1 ); }int main() {Test1();Test2();Test3();Test4();return 0; }三、歸并排序? ? ?
? ? ? 歸并排序(Merge-Sort)是建立在歸并操作上的一種有效的排序算法,該算法是采用分治法
(Divide and Conquer)的一個非常典型的應用。將數組分成兩半,這兩半分別排序后,再歸
并在一起。排序某一半時,繼續沿用同樣的排序算法,最終,將歸并兩個只含一個元素的數
組。
??? 1、算法原理
??? 歸并操作的工作原理如下:
?? 1)申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合并后的序列。
?? 2)設定兩個指針,最初位置分別為兩個已經排序序列的起始位置。
?? 3)比較兩個指針所指向的元素,選擇相對小的元素放入到合并空間,并移動指針到下一位?
???????? 置。
?? 4)重復步驟3直到某一指針超出序列尾,將另一序列剩下的所有元素直接復制到合并序列
???????? 尾。
?? 2、算法分析
??? 時間復雜度:
??? 歸并排序的效率是比較高的,設數列長為N,將數列分開成小數列一共要logN步,每一步都
是一個合并有序數列的過程,時間復雜度可以記為O(N),故一共為O(N*logN)。
?? 穩定性:
?? 歸并排序是穩定的排序.即相等的元素的順序不會改變.如輸入記錄 1(1) 3(2) 2(3) 2(4) 5(5) (括
號中是記錄的關鍵字)時輸出的 1(1) 2(3) 2(4) 3(2) 5(5) 中的2 和 2 是按輸入的順序.這對要排序數
據包含多個信息而要按其中的某一個信息排序,要求其它信息盡量按輸入的順序排列時很重要.這
也是它比快速排序優勢的地方。
? 3、算法實現
/*歸并排序。 */#include <iostream> #include <stdio.h>void merge( int *array, int low, int mid, int high ) {int nLeft = low; // nLeft是左邊序列的下標int nRight = mid + 1; // nRight是右邊序列的下標int nMerge = 0; // nMerge是臨時存放合并的下標int *tempArray = ( int* )new int[ high - low + 1 ]; // tempArray是臨時合并序列// 掃描左邊序列和右邊序列,直到一邊掃描結束while( nLeft <= mid && nRight <= high ){if( array[ nLeft ] <= array[ nRight ] ){tempArray[ nMerge ] = array[ nLeft ];nLeft++;nMerge++;}else{tempArray[ nMerge ] = array[ nRight ];nRight++;nMerge++;}}// 若左邊序列還沒掃描完,則將其全部賦值到臨時合并序列while( nLeft <= mid ){tempArray[ nMerge ] = array[ nLeft ];nLeft++;nMerge++;}// 若右邊序列還沒掃描完,則將其全部賦值到臨時合并序列while( nRight <= high ){tempArray[ nMerge ] = array[ nRight ];nRight++;nMerge++;}// 將臨時合并序列復制到原始序列中for( nMerge = 0, nLeft = low; nLeft <= high; nLeft++, nMerge++ ){array[ nLeft ] = tempArray[ nMerge ];}delete [] tempArray; }void mergeSort( int *array, int low, int high ) {if( array == NULL || low < 0 || high < 0 || low > high ){printf( "invaild input.\n" );return;}if( low < high ){int mid = ( low + high ) / 2 ;mergeSort( array, low, mid );mergeSort( array, mid + 1, high );merge( array, low, mid, high );} }void Test( const char* testName, int *array, int low, int high ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );mergeSort( array, low, high );printf( "after select sort, the result is: \n" );for( int i = 0; i < high - low + 1; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int length = 6;int array[ ] = { 4, 2, 6, 7, 1, 5 };Test( "Test1", array, 0, 5 ); }// 有重復數字 void Test2() {int length = 6;int array[ ] = { 4, 2, 6, 2, 1, 5 };Test( "Test2", array, 0, 5 ); }// 輸入數字只有一個元素 void Test3() {int array[ ] = { 100 };Test( "Test3", array, 0, 0 ); }// 輸入數組為空 void Test4() {int emptyArray[ ] = { };Test( "Test4", emptyArray, 0, 0 ); }// 輸入數組為null,且長度異常 void Test5() {Test( "Test5", NULL, -1, -1 ); }int main() {Test1();Test2();Test3();Test4();Test5();return 0; }?? (補充:1、當數組為空,low=0,high=0時,數組本應沒有元素,但gcc編譯后最終會打印出
一個元素,這個元素經多次測試是上次Test結果的最后一個元素。如果上次沒有執行Test則會出
現如:-1220152147 之類的未初始化結果;? 2、當然對于數組指針為NULL的情況,打印就會出
現段錯誤)。
?? (繼續補充:好吧,沒按捺住,又去找了為啥是上次Test結果的最后一個元素,可以從上圖看
出,原因是在初始化emptyArray[]數組時(也就是:int emptyArray[ ] = { };這句代碼上),gcc每
次都分配0xbffff0cc地址給emptyArray[],而是這個地址正是上一次Test結果中的最后一個元素地
址,而且每次array[]不管有多少元素的最后一個元素地址都是0xbffff0cc,至于原因應該跟具體
gcc數組內存分配策略有關,當然這里就不再跟下去了,要不然就沒飯吃了╮(╯▽╰)╭ )。
四、快速排序
??? 快速排序(Quicksort)是對冒泡跑序的一種改進。它的基本思想是:隨機選擇一個元素,對
數組進行分割,將所有比它小的元素排在前面,比它大的元素則排后面。這里的分割經由一系
列元素交換的動作完成。然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可
以遞歸進行,以此達到整個數據變成有序序列。
??? 1、算法原理
??? 基本思想:
??? 1)先從數列中取出一個數作為基準數;
? ? 2)分區過程,將比這個數大的數全放到它的右邊,小于或等于它的數全放到它的左邊;
? ? 3)再對左右區間重復第二步,直到各區間只有一個數。
??? 一趟快速排序的算法是:
?? 1)設置兩個變量i、j,排序開始的時侯:i=0,j=N-1;?
?? 2)以第一個數組元素作為關鍵數據,賦值給key,即key=A[0];
?? 3)從j開始向前搜索,即由后開始向前搜索(j--),找到第一個小于key的值A[j],將A[j]和A[i]互
???????? 換;
?? 4)從i開始向后搜索,即由前開始向后搜索(i++),找到第一個大于key的A[i],將A[i]和A[j]互換;
?? 5)重復第3、4步,直到i=j。 (3,4步中,沒找到符合條件的值,即3中A[j]不小于key,4中A[i]
??????? 不大于key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進
??????? 行交換的時候i, j指針位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令
??????? 循環結束)。
?? 快速排序還有很多改進版本,如隨機選擇基準數,區間內數據較少時直接用另外的方法排序以
減小遞歸深度。
?? 2、算法分析
??? 時間復雜度:
??? 快速排序之所比較快,因為相比冒泡排序,每次交換是跳躍式的。每次排序的時候設置一個
基準點,將小于等于基準點的數全部放到基準點的左邊,將大于等于基準 點的數全部放到基準
點的右邊。這樣在每次交換的時候就不會像冒泡排序一樣每次只能在相鄰的數之間進行交換,
交換的距離就大的多了。因此總的比較和交換次數 就少了,速度自然就提高了。當然在最壞的
情況下,仍可能是相鄰的兩個數進行了交換。因此快速排序的最差時間復雜度和冒泡排序是一
樣的都是O(N2),它的平均時間復雜度為O(NlogN)。
?? 穩定性:
?? 快速排序有兩個方向,左邊的i下標一直往右走,當a[i] <= a[center_index],其中center_index
是中樞元素的數組下標,一般取為數組第0個元素。而右邊的j下標一直往左走,當a[j] >
a[center_index]。如果i和j都走不動了,i <= j, 交換a[i]和a[j],重復上面的過程,直到i>j。 交換a[j]
和a[center_index],完成一趟快速排序。在中樞元素和a[j]交換的時候,很有可能把前面的元素
的穩定性打亂,比如序列為 5 3 3 4 3 8 9 10 11, 現在中樞元素5和3(第5個元素,下標從1開始
計)交換就會把元素3的穩定性打亂,所以快速排序是一個不穩定的排序算法,不穩定發生在中
樞元素和a[j] 交換的時刻。
? 3、算法實現
? 第一種實現為選取第一個元素為樞紐:
/*快速排序。(以第一個元素作為基準) */#include <iostream> #include <stdio.h>void QuickSort( int *array, int low, int high ) {if( array == NULL || low < 0 ){printf( "invalid input.\n" );return;}if( high < 0 || low > high ) // 必須加這句,因為遞歸的時候high可能為-1{return;}int first = low;int last = high;int key = array[ first ]; // 用第一個元素作為作為樞紐while( first < last ){while( first < last && array[ last ] >= key ){--last;}array[ first ] = array[ last ]; // 將比樞紐元素小的移到低端while( first < last && array[ first ] <= key ){++first;}array[ last ] = array[ first ]; // 將比樞紐元素大的移到高端}array[ first ] = key; // 樞紐到位記錄QuickSort( array, low, first - 1 );QuickSort( array, first + 1, high );}void Test( const char* testName, int* array, int low, int high ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );QuickSort( array, low, high );printf( "after quick sort, the result is: \n" );for( int i = 0; i < high - low + 1; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int array[ ] = { 4, 2, 6, 7, 1, 5 };Test( "Test1", array, 0, 5 ); }// 有重復數字 void Test2() {int array[ ] = { 4, 2, 6, 2, 1, 5 };Test( "Test2", array, 0, 5 ); }// 輸入數字只有一個元素 void Test3() {int array[ ] = { 100 };Test( "Test3", array, 0, 0 ); }// 輸入數組為空 void Test4() {int emptyArray[ ] = { };Test( "Test4", emptyArray, 0, 0 ); }// 輸入數組為null,且長度異常 void Test5() {Test( "Test5", NULL, -1, -1 ); }int main() {Test1();Test2();Test3();Test4();Test5();return 0; }?
第二種實現為隨機選取元素為樞紐:???
/*快速排序。(以隨機元素作為基準) */#include <stdlib.h> #include <iostream> #include <time.h> #include <stdio.h>void Swap( int *first, int *second ) {int temp = *first;*first = *second;*second = temp; } int Partition( int *array, int low, int high ) {if( array == NULL || low < 0 ){printf( "invalid input.\n" );return -1;}srand( ( unsigned )time( NULL ) ); // 利用時間設置隨機數種子int index = low + rand()%( high - low + 1 ); // 產生low到high的隨機數Swap( &array[ index ], &array[ low ] );int first = low;int last = high;int key = array[ first ]; // 用第一個元素作為作為樞紐while( first < last ){while( first < last && array[ last ] >= key ){--last;}array[ first ] = array[ last ]; // 將比樞紐元素小的移到低端while( first < last && array[ first ] <= key ){++first;}array[ last ] = array[ first ]; // 將比樞紐元素大的移到高端}array[ first ] = key; // 樞紐到位記錄return first; }/* int Partition( int *array, int low, int high ) {if( array == NULL || low < 0 ){printf( "invalid input.\n" );return -1;}srand( ( unsigned )time( NULL ) ); // 利用時間設置隨機數種子int index = low + rand()%( high - low + 1 ); // 產生low到high的隨機數Swap( &array[ index ], &array[ low ] );int small = low - 1;for( index = low; index < high; ++index ){if( array[ index ] < array[ high ] ){++small;if( small != index )Swap( &array[ index ], &array[ small ] );}}++small;Swap( &array[ small ], &array[ high ] ); return small; } */void QuickSort( int *array, int low, int high ) {if( low == high ){return;}int index = Partition( array, low, high );if( index < 0 )return;if( index > low )QuickSort( array, low, index - 1 );if( index < high )QuickSort( array, index + 1, high ); }void Test( const char* testName, int* array, int low, int high ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );QuickSort( array, low, high );printf( "after quick sort, the result is: \n" );for( int i = 0; i < high - low + 1; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int array[ ] = { 4, 2, 6, 7, 1, 5 };Test( "Test1", array, 0, 5 ); }// 有重復數字 void Test2() {int array[ ] = { 4, 2, 6, 2, 1, 5 };Test( "Test2", array, 0, 5 ); }// 輸入數字只有一個元素 void Test3() {int array[ ] = { 100 };Test( "Test3", array, 0, 0 ); }// 輸入數組為空 void Test4() {int emptyArray[ ] = { };Test( "Test4", emptyArray, 0, 0 ); }// 輸入數組為null,且長度異常 void Test5() {Test( "Test5", NULL, -1, -1 ); }int main() {Test1();Test2();Test3();Test4();Test5();return 0; }?? 隨機樞紐快速排序的Partition函數有兩種實現。
五、堆排序
??? 堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序
的一種。可以利用數組的特點快速定位指定索引的元素。堆分為大根堆和小根堆,是完全二叉
樹。大根堆的要求是每個節點的值都不大于其父節點的值,即A[PARENT[i]] >= A[i]。在數組的
非降序排序中,需要使用的就是大根堆,因為根據大根堆的要求可知,最大的值一定在堆頂。
??? 堆實際上是一棵完全二叉樹,其任何一非葉節點滿足性質: Key[i]<=key[2i+1]&&Key[i]
<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]即任何一非葉節點的關鍵字不大于或者不
小于其左右孩子節點的關鍵字。堆分為大頂堆和小頂堆,滿足Key[i]>=Key[2i+1]&&
key>=key[2i+2]稱為大頂堆,滿足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱為小頂堆。由上述性
質可知大頂堆的堆頂的關鍵 字肯定是所有關鍵字中最大的,小頂堆的堆頂的關鍵字是所有關鍵
字中最小的。
??? 1、算法原理
? ?? 其基本思想(大頂堆):
??? 1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆為初始的無序區;
??? 2)將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(R1,R2,......Rn-1)和新的有
序區(Rn),且滿足R[1,2...n-1]<=R[n];?
?? 3)由于交換后新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,......Rn-1)調整
為新堆,然后再次將R[1]與無序區最后一 個元素交換,得到新的無序區(R1,R2....Rn-2)和新的有
序區(Rn-1,Rn)。不斷重復此過程直到有序區的元素個數為n-1,則整個排序過 程完成。
???? 操作過程如下:
??? 1)初始化堆:將R[1..n]構造為堆;
??? 2)將當前無序區的堆頂元素R[1]同該區間的最后一個記錄交換,然后將新的無序區調整為新
的堆。
?? 2、算法分析
??? 時間復雜度:
??? 堆排序其實也是一種選擇排序,是一種樹形選擇排序。只不過直接選擇排序中,為了從
R[1...n]中選擇最大記錄,需比較n-1次,然后 從R[1...n-2]中選擇最大記錄需比較n-2次。事實上
這n-2次比較中有很多已經在前面的n-1次比較中已經做過,而樹形選擇排序恰好利用樹形的 特
點保存了部分前面的比較結果,因此可以減少比較次數。對于n個關鍵字序列,最壞情況下每個
節點需比較log2(n)次,因此其最壞情況下時間復雜度為 O(nlogn)。
?? 穩定性:
?? 我們知道堆的結構是節點i的孩子為2*i和2*i+1節點,大頂堆要求父節點大于等于其2個子節
點,小頂堆要求父節點小于等于其2個子節點。在一個長為n 的序列,堆排序的過程是從第n/2開
始和其子節點共3個值選擇最大(大頂堆)或者最小(小頂堆),這3個元素之間的選擇當然不會破壞穩
定性。但當為n /2-1, n/2-2, ...1這些個父節點選擇元素時,就會破壞穩定性。有可能第n/2個父節
點交換把后面一個元素交換過去了,而第n/2-1個父節點把后面一個相同的元素沒 有交換,那么
這2個相同的元素之間的穩定性就被破壞了。所以,堆排序不是穩定的排序算法。不適合記錄較
少的排序。???
? 3、算法實現
/*堆排序。 */#include <iostream> #include <stdio.h>// array是待調整的堆數組,pos是待調整的數組元素的位置,length是數組的長度 // 本函數功能是:根據數組array構建大根堆 void HeapAdjust( int *array, int pos, int length ) {if( array == NULL || pos < 0 || length <= 0 ){printf( "invalid input.\n" );return;}int child;int temp;for( ; 2 * pos + 1 < length; pos = child ){child = 2 * pos + 1; // 子結點的位置if( child < length - 1 && array[ child + 1 ] > array[ child ] ) // 得到子結點中較大的結點++child;if( array[ pos ] < array[ child ] ) // 如果較大的子結點大于父結點那么把較大的子結點往上移動,替換它的父結點{temp = array[ pos ];array[ pos ] = array[ child ];array[ child ] = temp;}else break;} }void HeapSort( int *array, int length ) {int i;// 調整序列的前半部分元素,調整完之后第一個元素是序列的最大的元素// length/2-1是最后一個非葉節點for( i = length / 2 - 1; i >= 0; --i )HeapAdjust( array, i, length );// 從最后一個元素開始對序列進行調整,不斷的縮小調整的范圍直到第一個元素for( i = length - 1; i > 0; --i ){// 把第一個元素和當前的最后一個元素交換,// 保證當前的最后一個位置的元素都是在現在的這個序列之中最大的array[ i ] = array[ 0 ] ^ array[ i ];array[ 0 ] = array[ 0 ] ^ array[ i ];array[ i ] = array[ 0 ] ^ array[ i ];// 不斷縮小調整heap的范圍,每一次調整完畢保證第一個元素是當前序列的最大值HeapAdjust( array, 0, i );} }void Test( const char* testName, int* array, int length ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );HeapSort( array, length );printf( "after heap sort, the result is: \n" );for( int i = 0; i < length; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int array[ ] = { 4, 2, 6, 7, 1, 5 };Test( "Test1", array, 6 ); }// 有重復數字 void Test2() {int array[ ] = { 4, 2, 6, 2, 1, 5 };Test( "Test2", array, 6 ); }// 輸入數字只有一個元素 void Test3() {int array[ ] = { 100 };Test( "Test3", array, 1 ); }// 輸入數組為空 void Test4() {int emptyArray[ ] = { };Test( "Test4", emptyArray, 0 ); }// 輸入數組為null,且長度異常 void Test5() {Test( "Test5", NULL, -1 ); }int main() {Test1();Test2();Test3();Test4();Test5();return 0; }六、基數排序
??? 基數排序(Radix sort)屬于“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)
或bin sort,顧名思義,它是透過鍵值的部分資訊,將要排序的元素分配至某些“桶”中,藉以達
到排序的作用。
?? 1、算法原理
?? 1)首先根據個位數的數值,在走訪數值時將它們分配至編號0到9的桶子中,接下來將這些桶
子中的數值重新串接起來;
?? 2)接著再進行一次分配,這次是根據十位數來分配,接下來將這些桶子中的數值重新串接起
來,成為以下的數列;
?????? 這時候整個數列已經排序完畢;如果排序的對象有三位數以上,則持續進行以上的動作直至
最高位數為止。
????? 最高位優先(Most Significant Digit first)法,簡稱MSD法:先按k1排序分組,同一組中記錄,
關鍵碼k1相等,再對各組按k2排序分成子組,之后,對后面的關鍵碼繼續這樣的排序分 組,直
到按最次位關鍵碼kd對各子組排序后。再將各組連接起來,便得到一個有序序列。
???? 最低位優先(Least Significant Digit first)法,簡稱LSD法:先從kd開始排序,再對kd-1進行排
序,依次重復,直到對k1排序后便得到一個有序序列。
?? 2、算法分析
??? 時間復雜度:
??? 設待排序列為n個記錄,d個關鍵碼,關鍵碼的取值范圍為radix,則進行鏈式基數排序的時間
復雜度為O(d(n+radix)),其中,一趟分配時間復雜度為O(n),一趟收集時間復雜度為O(radix),
共進行d趟分配和收集。其時間復雜度為O (nlog(r)m),其中r為所采取的基數,而m為堆數。
?? 穩定性:
?? 基數排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高
位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優 先級排序,最后的次序
就是高優先級高的在前,高優先級相同的低優先級高的在前。基數排序基于分別排序,分別收
集,所以其是穩定的排序算法。??
??? 3、算法實現
/*基數排序。 */#include <iostream> #include <stdio.h>// 輔助函數,求數據的最大位數 int MaxBit( int *array, int n ) {int maxbit = 1; // 保存最大的位數int div = 10;for( int i = 0; i < n; ++i ){while( array[ i ] >= div ){div *= 10;++maxbit;}}return maxbit; }void RadixSort( int *array, int n ) {if( array == NULL || n < 0 ){printf( "invalid input.\n" );return;}int maxbit = MaxBit( array, n );int *temp = new int[ n ];int *count = new int[ 10 ]; // 計數器int radix = 1;int i, j, k;for( i = 1; i <= maxbit; i++ ) //進行maxbit次排序{for( j = 0; j < 10; j++ )count[ j ] = 0; // 每次分配前清空計數器for( j = 0; j < n; j++ ){k = ( array[ j ] / radix ) % 10; // 統計每個桶中的記錄數count[ k ]++;}for( j = 1; j < 10; j++ )count[ j ] = count[ j - 1 ] + count[ j ]; // 將tmp中的位置依次分配給每個桶for( j = n - 1; j >= 0; j-- ) // 將所有桶中記錄依次收集到tmp中{k = ( array[ j ] / radix ) % 10;temp[ count[ k ] - 1 ] = array[ j ];count[ k ]--;}for( j = 0; j < n; j++ ) // 將臨時數組的內容復制到data中array[ j ] = temp[ j ];radix = radix * 10;}delete [] temp;delete [] count; }void Test( const char* testName, int* array, int n ) {if( testName == NULL ){printf( "test invaild input.\n" );return;}printf( "%s begins: \n", testName );RadixSort( array, n );printf( "after radix sort, the result is: \n" );for( int i = 0; i < n; i++ )printf( "%d ", array[ i ] );printf( "\n" ); }// 無重復數字 void Test1() {int array[ ] = { 42, 24, 61, 71, 11, 57 };Test( "Test1", array, 6 ); }// 有重復數字 void Test2() {int array[ ] = { 421, 24, 6,421, 121, 54 };Test( "Test2", array, 6 ); }// 輸入數字只有一個元素 void Test3() {int array[ ] = { 100 };Test( "Test3", array, 1 ); }// 輸入數組為空 void Test4() {int emptyArray[ ] = { };Test( "Test4", emptyArray, 0 ); }// 輸入數組為null,且長度異常 void Test5() {Test( "Test5", NULL, -1 ); }int main() {Test1();Test2();Test3();Test4();Test5();return 0; }轉載于:https://my.oschina.net/u/2537915/blog/724277
總結
- 上一篇: 在linux查看内存的大小
- 下一篇: MyBatis学习总结(16)——Myb