STL sort解析
從上學(xué)接觸到編程開始,到工作了幾年。有關(guān)排序算法的內(nèi)容反反復(fù)復(fù)有接觸,但是要說每一種排序算法的細(xì)節(jié)都能說清,那就有難度了。
一來算法難度有深有淺。有比較簡單的冒泡,插入,也有復(fù)雜的堆排序,快排這些。
二來排序算法實(shí)際上成型已久,是個套路的東西。
三來平時工作用不到每一種算法。實(shí)際數(shù)據(jù)不多的時候用冒泡足夠了。數(shù)據(jù)多時,用快排足夠了。
關(guān)聯(lián)容器內(nèi)部有自動排序,序列容器中只有vector,deque的迭代器是隨機(jī)位置迭代器(RandomAccessIterators),因此只有vector和deque能使用std::sort()。
后面講快排會講為什么sort()必須是隨機(jī)位置迭代器。
?
今天看到一個實(shí)際工作中經(jīng)常用到的例子,但是平時沒有注意它的原理。
這個例子有助于加深對排序的理解。便是STL里面的sort()算法。
?
sort()是有策略進(jìn)行排序。具體規(guī)則如下:
// 1.數(shù)據(jù)量大時Quick Sort(快排)
// 2.分段時數(shù)據(jù)量小于門檻,改用Inserition Sort(插入排序)
// 3.遞歸層次太深,改用Heap Sort(堆排序)
里面使用了3種排序。既有復(fù)雜度O(n*n)的插入排序,也有復(fù)雜度O(nlogn)的快排和堆排序。
先分別來看這3種排序。從簡單的插入排序開始吧。
?
插入排序
假設(shè)待排序的序列是a[0..n - 1],總共n個數(shù)據(jù)。
1)a[0]是已排序的序列,a[i, n - 1]是未排序的序列。令i = 1。
2)取a[i]插入到有序序列合適位置,使得新序列有序。
3)重復(fù)2步驟,直到i == n - 1。
外層循環(huán)是從1到n-1,內(nèi)層循環(huán)是找插入位置,無需遍歷所有位置,當(dāng)a[j - 1]?< a[j]即可停止,否則繼續(xù)找位置,同時交換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]);}} }有兩層循環(huán),算法復(fù)雜度是O(n*n)? 。穩(wěn)定不穩(wěn)定,取決于中間的判斷條件。a[j - 1] > a[j]則是穩(wěn)定的,條件變成a[j - 1] >= a[j]則是不穩(wěn)定的。
?
快速排序
快排的核心是分治法。
1.如果序列S中數(shù)據(jù)的數(shù)量是0或者1。結(jié)束。
2.取S中的任意一個數(shù)字,當(dāng)作樞紐v。
3.將S分割成兩段L,R,使得L中的元素全部小于等于v,R中的元素全部大于等于v。
4.將L,R遞歸執(zhí)行快排。
其中第2步,選取一個數(shù)字做樞紐。可以是任意的數(shù)字,通常用的是median-of-three。從首尾和中間數(shù)中,選擇中值。
比如序列:1,4,5,7,9,8,3。那么1,7,3中選3作為樞紐來對序列進(jìn)行分割。
為了能快速取出中間元素,迭代器必須支持隨機(jī)位置(RandomAccess)。
三個數(shù)取中值的函數(shù),只有畫線段標(biāo)位置,容易看出a,b,c三者的大小關(guān)系。
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向頭端移動。
當(dāng)first大于等于樞紐時,停止。當(dāng)last小于等于樞紐時,停止。
檢查first,last是否交錯(first >= last),如果交錯返回first。如果沒有交錯,first,last各自向中央移動一個位置。
重復(fù)上面的步驟。
最終返回的first是右半部分的起點(diǎn)。左半部分全部小于等于樞紐,右半部分全部大于等于樞紐。
來看代碼,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白話經(jīng)典算法之七大排序第2版》
轉(zhuǎn)載于:https://www.cnblogs.com/yao2yaoblog/p/7443804.html
總結(jié)
以上是生活随笔為你收集整理的STL sort解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Class类 获取Class对象
- 下一篇: 使用maven profile实现多环境