瞭解各個排序的算法原理比較適合找工做面試的時候用,在刷題競賽的時候直接使用sort函數便可
sort做爲C++自帶的函數,使用頻率比較高,通常遇到須要排序的數組用就好了,能解決大部分須要排序的問題。下面演示一下各類用法。c++
最基礎的用法,對數組直接排序(默認從小到大排序)。面試
int A[10] = { 5,4,8,7,6,4,1,6,5,1 }; sort(A, A + 10); // A = { 1,1,4,4,5,5,6,6,7,8 }
sort函數的前兩個參數爲首地址與尾地址,表示須要排序一段數組。在上例中,A有10個元素,而使用A時會返回A的首地址,A + 10表示在首地址上向後偏移10個位置後的地址,用(A,A + 10)能夠表示A[0 ~ 9]。算法
若是隻想對前五位排序,也能夠這樣寫sort(A, A + 5)
。segmentfault
對於STL中的vector也是如此,使用對象中的begin()
與end()
獲取首地址與尾地址。數組
vector<int> A = { 5,4,6,8,7,4,1,6,9 }; sort(A.begin(), A.end()); // A = { 1,4,4,5,6,6,7,8,9 }
那麼對於自定義的結構體呢?看下面。數據結構
sort函數還有第三個可選參數,就是自定義排序的方法,傳入方法名或者匿名函數便可。傳入的方法的參數必須是兩個同類型的且返回值爲bool類型。less
若是不使用第三個參數,sort函數會默認調用對象的<
方法來比較兩個對象的大小關係,使用結構體時須要重寫一下operator<
。函數
使用匿名函數ui
int A[10] = { 5,1,7,9,5,4,8,3,1,0 }; sort(A, A + 10, [](int a, int b) { return a > b; }); //從大到小排序 //A = { 9,8,7,5,5,4,3,1,1,0 }
使用方法spa
//從大到小排序 bool cmp(int a, int b) { return a > b; } int main() { int A[10] = { 5,1,7,9,5,4,8,3,1,0 }; sort(A, A + 10, cmp); //A = { 9,8,7,5,5,4,3,1,1,0 } return 0; }
結構體重載operator<
struct Node { int a, b; //按a對Node從小到大排序(與傳入的Node比較) bool operator<(Node u) const { return a < u.a; } }; int main() { Node A[3] = { {1, 6}, {8, 0}, {4, 5} }; sort(A, A + 3); // A = { {1, 6}, {4, 5}, {8, 0} }; return 0; }
固然,也能夠選擇不重載operator<
,直接傳匿名函數或者方法。
less<T>()
與greater<T>()
若是隻是想讓元素 從小到大 或者 從大到小 排序,那麼大可沒必要本身手寫一個方法實現,直接使用系統內置的less<T>()
與greater<T>()
便可,T爲元素類型。
sort(A, A + 10, less<int>()); //從小到大排序 sort(A, A + 10, greater<int>()); //從大到小排序
less<T>()
須要類重載operator<()
greater<T>()
須要類重載operator>()
快速排序與歸併排序都用了遞歸實現,若是你對遞歸併非很熟練,能夠大概瞭解後暫時跳過,之後再回來看看。
快速排序先會隨機找一個基準點,把小於基準點的數放到左邊,把大於基準點的數放到右邊,再將區間劃分爲兩半遞歸執行,一直如此劃分下去,就達到了排序的目的。
具體的排序方法:肯定基準點後,雙指針從數組兩端向中間移動,左邊指針先找到大於基準點的,而後右指針移動到小於基準點的,交換兩個指針的數值便可。
引用一下《啊哈!算法》這本書中的快速排序流程圖,將雙指針當作兩個哨兵。(以左端點6爲基準點,右指針先出發)
左右指針找到符合條件的值,交換!
交換完以後繼續查找,找到後繼續交換,直到兩個哨兵(雙指針)相遇爲止。
兩個哨兵相遇後,咱們要把基準值放到相遇的位置,也就是中間,畢竟左邊都是比基準值小的,右邊都是比基準值大的數嘛。
到此第一輪排序完畢,但咱們發現整個數組其實並無徹底排好序,接下來須要將其不斷分紅左右兩個區間重複上述過程最後就能將整個數組排序完畢了。
下面咱們來寫寫代碼吧,上模板題!(代碼實現與上述過程有所差別,不過原理大體相同)
P1177 【模板】快速排序 (注意:該題若是隨機選取的數爲最左邊的數,即x = A[L],則會超時)
#include<bits/stdc++.h> using namespace std; const int maxn = 100000 + 10; int n, A[maxn]; void quick_sort(int l, int r) { if (l >= r) return; //不能再劃分區間了,終止 //x爲隨機選取的值,i爲起點,j爲終點。 int x = A[(l + r) / 2], i = l - 1, j = r + 1; while (i < j) { while (A[++i] < x); //找出左邊大於x的值 (當A[j] > x 時循環中止 即目前A[j] > x while (A[--j] > x); //同理找出右邊小於x的值 if (i < j) swap(A[i], A[j]); //若是符合要求則交換 } quick_sort(l, j); //繼續劃分左邊 quick_sort(j + 1, r); //劃分右邊 } int main() { cin >> n; for (int i = 0; i < n; i++) cin >> A[i]; quick_sort(0, n - 1); for (int i = 0; i < n; i++) cout << A[i] << ' '; return 0; }
歸併排序與快速排序最大的不一樣就是 快速排序是先排序再劃分,而歸併排序是先劃分再排序,其中間過程能夠理解爲合併兩個有序數組(這一步須要開額外空間實現)。
合併兩個有序數組的方法:比較兩個數組開頭的數,誰小就選誰,最後若是有數組不爲空,則一次性所有選取便可。具體題目:88. 合併兩個有序數組
咱們沒有對數組排過序,又怎麼會獲得有序數組讓咱們合併呢,其實劃分最終會劃分到每一個元素上,對於單個元素而言不須要在乎順序了。
依舊可使用上面的題目來練手歸併排序 P1177 【模板】快速排序
#include<bits/stdc++.h> using namespace std; const int maxn = 100000 + 10; int n, A[maxn], B[maxn]; void merge_sort(int l, int r) { if (l >= r) return; int mid = (l + r) / 2; //找出中點 merge_sort(l, mid); merge_sort(mid + 1, r); //劃分兩邊 //合併兩個有序數組(當劃分到只有一個元素的時候也是有序的) int k = l, i = l, j = mid + 1; while (i <= mid && j <= r) if (A[i] <= A[j]) B[k++] = A[i++]; else B[k++] = A[j++]; //兩個數組合並完畢,只要數組中有元素就放進去(只要一組有,能夠保證另外一組爲空,因此不用作判斷) while (i <= mid) B[k++] = A[i++]; while (j <= r) B[k++] = A[j++]; for (i = l; i <= r; i++) A[i] = B[i]; //將排序好的數組覆蓋放入原數組中 } int main() { cin >> n; for (int i = 0; i < n; i++) cin >> A[i]; merge_sort(0, n - 1); for (int i = 0; i < n; i++) cout << A[i] << ' '; return 0; }
大部分算法的時間複雜度與空間複雜度都沒有快速排序與歸併排序好,因此這裏就不一一細說了,列舉一下其餘常見的排序算法,有興趣能夠本身瞭解瞭解。
題目列表待增長,洛谷的官方題單是個不錯的選擇。