(轉載) http://www.cnblogs.com/BeyondAnyTime/archive/2012/08/14/2638070.html
排序算法是常常使用的算法,在STL中也有一個比較牛X的快速排序(sort),可是咱們不能只會調用sort呀!?做爲一個好學的同窗,咱們要知道各類排序的內部是怎麼實現滴~~~提到排序算法咱們要知道兩個常常提到的概念:html
(1)排序算法的穩定性:所謂「穩定性」是指,在待排序數組出現的兩個相同的元素,排序以後相對維持保持不變。好比:待排序數組爲arr[] = {1,4,3,1},排序以後元素變爲arr_new[] = {1,1,4,3},而且arr_new中的第一個是arr中的第一個1,arr_new中的第二個1是arr中的第二個1,這是咱們就說這種排序時穩定的。ios
(2)原地排序:所謂原地排序是指,不申請多餘的空間來輔助完成排序算法,而是在原來的待排序的數據之上直接進行比較,交換,移動等操做。算法
1.插入排序數組
算法原理:將待排序的數組分爲:有序區 和 無序區。而後每次從無序區取出第一個數據插入到有序區的正確位置,最終完成排序。學習
算法代碼:優化
#include <iostream> using namespace std; void insert_sort(int *arr,int n) { int i,j; for(i = 1 ; i < n ; ++i) { int tmp = arr[i]; j = i - 1; while( j >= 0 && arr[j] > tmp) { arr[j+1] = arr[j]; j--; } arr[j+1] = tmp; } } int main() { int arr[] = {2,4,1,3,5,8,7,6,8}; insert_sort(arr,9); for(int i = 0 ; i < 9 ; ++i) { cout<<arr[i]<<" "; } cout<<endl; return 0; }
小結:看代碼能夠知道這種排序算法的時間複雜度是O(n^2),而且插入排序時穩定的,屬於原地排序。那麼何時使用插入排序比較好呢?那就是當數組中的大部分數據已經有序時,使用插入排序算法的效率比較高,這種狀況下,所須要進行的數據移動較少,而數據移動正式插入排序算法的主要步驟~~~~ui
2.冒泡排序spa
算法原理:冒泡排序是通過n-1趟子排序完成的,第 i 趟子排序從第1個數至第 n-i+1 個數,若第 i 個數比第 i+1 個數大,則交換這兩個數,實際上這樣通過 i 次子排序就使得 第1個數至第 n-i +1個數之間最大的數交換到了n-i+1 的位置上了。實際上冒泡排序時能夠優化的,那就是當第 i 次子排序並無發生元素的交換時,就說明數組已經排好序了,之後的子排序就不用作了。code
算法代碼:htm
#include <iostream> using namespace std; void swap(int &x,int &y) { x = x^y; y = x^y; x = x^y; } void bubble_sort(int *arr,int n) { int i,j; for(i = n - 1 ; i > 0 ; --i) { bool flag = true; for(j = 0 ; j < i ; ++j) { if(arr[j] > arr[j+1]) { flag = false; swap(arr[j],arr[j+1]); } } if(flag) //數組已經排好序不必在繼續進行其餘子排序了 break; } } int main() { int arr[] = {2,1,4,3,8,7,5,6}; bubble_sort(arr,8); for(int i = 0 ; i < 8 ; ++i) { cout<<arr[i]<<" "; } cout<<endl; return 0; }
小結:冒泡排序算法的時間複雜度是O(n^2),同時冒泡排序也是穩定的,而且屬於原地排序,排序的效率取決於逆序對的多少。採用一點小優化也加速了冒泡排序。
3.選擇排序
算法原理:所謂選擇排序通過 n-1 次選擇,當進行第 i 次選擇時,是從第1個元素到第 n-i+1 的元素中選擇最大的元素和第 n-i+1 個位置的元素交換,這樣作好比第1 次選擇使得最大的元素到了數組的最後一個位置。注意哦,在選擇排序中每次選擇時只進行一次數據的交換。
算法代碼:
#include <iostream> using namespace std; void swap(int &x,int &y) { int tmp = x; x = y; y = tmp; } void select_sort(int *arr,int n) { int i,j; for(i = n-1 ; i > 0 ; --i) { int tmp = 0; for(j = 1 ; j <= i ; ++j) { if(arr[j] >= arr[tmp])//這裏的「=」是保證選擇排序穩定的關鍵 { tmp = j; } } swap(arr[i],arr[tmp]); } } int main() { int arr[] = {2,1,4,3,8,7,5,6}; select_sort(arr,8); for(int i = 0 ; i < 8 ; ++i) { cout<<arr[i]<<" "; } cout<<endl; return 0; }
小結:選擇排序的思路很是的簡單,實現起來也不難。時間複雜度是O(n^2),選擇排序也是穩定的排序,而且也是原地排序。選擇排序的時間基本不受數據的影響,由於無論怎樣都要進行n-1次選擇排序。
4.歸併排序
算法原理:歸併排序的思想是分治,將一個帶排序的數組分紅兩個較小的數組,而後分別進行排序,組後將兩個排好序的較小的數組合並起來,就獲得了原來數組的排序後的結果。應該注意的是這種將兩個排好序的數組合並有一個較好的算法,時間複雜度是O(n1+n2)的。n一、n2分別是兩個小數組的長度。
算法代碼:
#include <iostream> using namespace std; void merge_sort(int *arr,int start,int end,int *temp) { if(end > start+1) { int mid = start + (end - start) / 2; merge_sort(arr,start,mid,temp); merge_sort(arr,mid,end,temp); int i = start , j = mid , k = start; while(i < mid || j < end) { if(j >= end || (i < mid && arr[i] <= arr[j])) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } for(i = start ; i < end ; ++i) { arr[i] = temp[i]; } } } int main() { int temp[8]; int arr[] = {2,1,4,3,8,7,5,6}; merge_sort(arr,0,8,temp); for(int i = 0 ; i < 8 ; ++i) { cout<<arr[i]<<" "; } cout<<endl; return 0; }
小結:歸併排序時穩定的排序,可是不屬於原地排序,由於用了額外的O(n)的空間,時間複雜度降到了O(n*log n),而且對任意的數組進行排序時間複雜度都能控制在O(n*logn)。
5.堆排序
算法原理:所謂的堆排序是利用徹底二叉樹的思想實現的。首先應該提到的是最大堆,在最大堆中(徹底二叉樹二叉樹)中每一個父節點都大於等於兩個兒子節點的值,這時候很明顯堆頂是元素的最大值,而後把堆頂元素和堆中最後一個元素(分層遍歷的節點編號最大的元素)交換,這樣最大值就落到了數組的arr[n-1]的位置,而後把前n-1元素繼續按照上面的方式處理,如此進行n-1次就完成堆排序。
算法代碼:
#include <iostream> using namespace std; void swap(int &x,int &y) { x = x + y; y = x - y; x = x - y; } void quick_sort(int *arr,int s,int e) { if(s+1 < e) { int tmp = arr[s]; int i = s+1; int j = e-1; while(i < j) { while(i <= j && arr[i] <= tmp) { i++; } while(i <= j && arr[j] >= tmp) { j--; } if(i < j) { swap(arr[i],arr[j]); } } swap(arr[s],arr[i-1]); quick_sort(arr,s,i-1); quick_sort(arr,i,e); } } int main() { int arr[] = {2,1,4,3,8,7,5,6}; quick_sort(arr,0,8); for(int i = 0 ; i < 8 ; ++i) { cout<<arr[i]<<" "; } cout<<endl; return 0; }
小結:堆排序是不穩定的排序,可是堆排序屬於原地排序。時間複雜度是O(n*log n),而且不須要額外的輔助空間,也就是說堆排序是一種不錯的排序算法哦~~~
6.快速排序
算法原理:快速排序時這樣的一種排序,選取數組中的第一個元素arr[0]做爲依據,遍歷一遍數組後,使得數組中的第一個元素進入正確的位置,即在該位置左面的元素均小於等於arr[0],在該位置右面的元素均大於等於arr[0]。而後,在對該位置左面和右面的元素分別進行快速排序,如此一來完成整個數組的排序。
算法代碼:
#include <iostream> using namespace std; void swap(int &x,int &y) { x = x + y; y = x - y; x = x - y; } void quick_sort(int *arr,int s,int e) { if(s+1 < e) { int tmp = arr[s]; int i = s+1; int j = e-1; while(i < j) { while(i <= j && arr[i] <= tmp) { i++; } while(i <= j && arr[j] >= tmp) { j--; } if(i < j) { swap(arr[i],arr[j]); } } swap(arr[s],arr[i-1]); quick_sort(arr,s,i-1); quick_sort(arr,i,e); } } int main() { int arr[] = {2,1,4,3,8,7,5,6}; quick_sort(arr,0,8); for(int i = 0 ; i < 8 ; ++i) { cout<<arr[i]<<" "; } cout<<endl; return 0; }
小結:首先仍是說明快速排序時不穩定的,可是是原地排序,不須要額外的空間,時間複雜度是O(nlog n),實際上,這種把第一個元素做爲依據元素只是快速排序的一種,STL中的sort內部實現是根據排序到了不一樣的階段選用不一樣的排序算法。當數據量大是採用quick_sort排序,當分段遞歸到了數據量小於某個數值時,爲避免quick_sort的遞歸調用帶來的額外開銷,就改用insert_sort 了;若是遞歸層次過深,還會考慮使用heap_sort 。
學習中的一點總結,歡迎拍磚哦^^