從上學接觸到編程開始,到工做了幾年。有關排序算法的內容反反覆覆有接觸,可是要說每一種排序算法的細節都能說清,那就有難度了。算法
一來算法難度有深有淺。有比較簡單的冒泡,插入,也有複雜的堆排序,快排這些。編程
二來排序算法實際上成型已久,是個套路的東西。dom
三來平時工做用不到每一種算法。實際數據很少的時候用冒泡足夠了。數據多時,用快排足夠了。函數
關聯容器內部有自動排序,序列容器中只有vector,deque的迭代器是隨機位置迭代器(RandomAccessIterators),所以只有vector和deque能使用std::sort()。ui
後面講快排會講爲何sort()必須是隨機位置迭代器。spa
今天看到一個實際工做中常常用到的例子,可是平時沒有注意它的原理。指針
這個例子有助於加深對排序的理解。即是STL裏面的sort()算法。code
sort()是有策略進行排序。具體規則以下:blog
// 1.數據量大時Quick Sort(快排)
// 2.分段時數據量小於門檻,改用Inserition Sort(插入排序)
// 3.遞歸層次太深,改用Heap Sort(堆排序)排序
裏面使用了3種排序。既有複雜度O(n*n)的插入排序,也有複雜度O(nlogn)的快排和堆排序。
先分別來看這3種排序。從簡單的插入排序開始吧。
插入排序
假設待排序的序列是a[0..n - 1],總共n個數據。
1)a[0]是已排序的序列,a[i, n - 1]是未排序的序列。令i = 1。
2)取a[i]插入到有序序列合適位置,使得新序列有序。
3)重複2步驟,直到i == n - 1。
外層循環是從1到n-1,內層循環是找插入位置,無需遍歷全部位置,當a[j - 1] < a[j]便可中止,不然繼續找位置,同時交換a[j - 1],a[j]的位置。
void InsertSort(int *a, int count) { if (count <= 1) { return; } for (int i = 1; i < count; ++i) { for (int j = i; j >= 0 && a[j - 1] > a[j]; j--) { swap(a[j - 1], a[j]); } } }
有兩層循環,算法複雜度是O(n*n) 。穩定不穩定,取決於中間的判斷條件。a[j - 1] > a[j]則是穩定的,條件變成a[j - 1] >= a[j]則是不穩定的。
快速排序
快排的核心是分治法。
1.若是序列S中數據的數量是0或者1。結束。
2.取S中的任意一個數字,看成樞紐v。
3.將S分割成兩段L,R,使得L中的元素所有小於等於v,R中的元素所有大於等於v。
4.將L,R遞歸執行快排。
其中第2步,選取一個數字作樞紐。能夠是任意的數字,一般用的是median-of-three。從首尾和中間數中,選擇中值。
好比序列:1,4,5,7,9,8,3。那麼1,7,3中選3做爲樞紐來對序列進行分割。
爲了能快速取出中間元素,迭代器必須支持隨機位置(RandomAccess)。
三個數取中值的函數,只有畫線段標位置,容易看出a,b,c三者的大小關係。
int median(int a, int b, int c) { if (a < b) { if (b < c) { return b; // a < b < c } else { if (a < c) { return c; // a < c <= b } else { return a; // c <= a < b } } } else { if (b < c) { if (a < c) { return a; // b <= a < c } else { return c; // b < c < a } } else { return b; // c <= b <= a } } }
再來看分割算法:
令first指向頭元素,last指向尾元素。first向尾端移動,last向頭端移動。
當first大於等於樞紐時,中止。當last小於等於樞紐時,中止。
檢查first,last是否交錯(first >= last),若是交錯返回first。若是沒有交錯,first,last各自向中央移動一個位置。
重複上面的步驟。
最終返回的first是右半部分的起點。左半部分所有小於等於樞紐,右半部分所有大於等於樞紐。
來看代碼,RandomAccessIter能夠理解成一個普通指針。
int* partition(int *first, int *last, int pivot) { while (true) { while (*first < pivot) first++; while (*last > pivot) last--; if (first >= last) return first; swap(*first, *last); first++; last--; } }
引用:
1.《STL源碼剖析》
2.《MoreWindows白話經典算法之七大排序第2版》