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