講完容器之後,我們迅速進入到算法部分。
首先看一下,我們這講在整個算法大圖的中位置:

在進入排序相關之前,我們把雖然與排序無關,但是也有關聯的計數和最大值最小值部分先看一下。算是對算法部分作個預熱,将來會廣泛出場的lambda表達式也先借機會亮亮相。
計數的目的,是數一數,在容器裡,符合某一條件的元素有多少個。
算法1: std::count,數一數跟這個值相等的對象有多少個。
我們看一個例子,數數vector中有幾個1:
算法2: std::count_if,count更新版,可以提供一個條件判斷的函數來進行判斷。
我們看一個例子,判斷是奇偶還是偶數:
count_if的第三個參數,是一個可調用對象,它可以是函數,仿函數(函數對象)和lambda表達式。
lambda表達式是一種匿名函數,特别适合用于stl的算法中。這是c++11帶給算法的禮物。
lambda表達式的形式為:
如果傳回值類型可以像auto一樣被推斷出來,就不用寫了。變量捕獲用不到可以為空,參數沒有也可以為空。隻有函數體是必要的。
我們看看上例中的這個lambda表達式:
不需要捕獲變量
參數是int elem
傳回值類型因為隻可能是bool,就讓編譯器自己推斷去
函數體裡就跟普通函數一樣,就不多說了
關于function, functor和lambda表達式,我們後面會詳細再講,目前快餐教程,大家先學會能看懂,能用。
算法3: std::max_element算法求最大值
算法4: std::min_element算法求最小值
例,求1〜100中的最大值和最小值:
在《資料結構》課中,講完各種資料結構了之後,重頭戲就變成了排序和查找。
我們上一講主要是線性表、連結清單和二叉排序樹。這一講就重點說說排序和查找。
我們先來一張排序相關的總圖,然後再去看每一部分的細圖:
從上圖中可以看到,排序相關的主要由三部分構成:
排序算法:真正做排序的算法
檢查算法:确定是否是排序的,如果不是,是從哪裡開始無序的
在已排序區間上的操作:排好序了,有什麼用處,這些算法是消費排序帶來的好處的。最重要的就是二分法查找了。
我們再聚一下焦,看看排序算法都包括哪些内容:
在stl的排序算法中,主要有三種算法會被用到:快速排序,歸并排序和堆排序。
算法
sort
partial_sort
stable_sort
預設算法
快速排序
堆排序
歸并排序
優點
很好的平均效率
在任何情況下都保持n*log(n)的複雜度
保持相等元素的相對順序
不足
最差情況下效率較差
實際情況中比快速排序慢2~5倍
記憶體不足時複雜度提高log(n)倍
額外好處
可以隻做前n個排序就停止
快速排序在不是最差情況下的平均速度最快,是以它是預設的算法:
從小到大是預設的行為:
從大到小可以通過第三個參數,傳入一個比較函數來實作:
有一點需要注意,因為是要改變内容的,是以疊代器不能用常量的cbegin和cend.
stable_sort的參數與sort完全一緻。可以無縫替換使用,視記憶體和需求決定。
例:
部分排序除了指定起點和終點之外,還需要一個參數指定排序的終點。
比如下面的例子是:隻排出前三大的,後面就不管了。
排序後是這樣的:
堆有兩種用法,一種是随時更新堆,就像set和map一樣。後面我們會介紹priority_queue容器,就是這方面的專用容器;另一種就是将線性結構一次性建個堆,不一定随時維護。這樣就隻有一次性的成本。
比如把上例中的vector來建個堆:
堆中的組織結構是這樣的:
100是樹根,左子樹是92,右子樹是99.
然後92的左子樹是76,右子樹是91. 99的左子樹是98, 右子樹是60. 以此類推,我們畫一下前4層的圖:
如果這麼看起來實在太費勁了,我們可以将堆重新變成排序好的結果:
列印出來就又變成有序的了。不過,堆的結構也被破壞了,還需要重建立堆。
排好序了,最常見的應用之一就是二分法查找。
我們看一個例子:
輸出當然是“found 97!”了。
因為二分查找不修改疊代器的值,是以又可以使用隻讀的疊代器cbegin和cend了。
比如,在上面排序的基礎上,我們想把比4小的排在4前面,比4大的排在後面:
排序後的結果如下:
從上面的結果可以看到,排序的結果中,并不是完全有序的。在滿足要求的情況下,并沒有對4以後的數做更進一步的有序化。這樣會帶來性能的提升。
前面的nth_element是根據某個值分成兩部分,而partition算法可以有更多的玩法。
比如下面,我們按奇偶性,把偶數排到前面:
重新排列之後變成這樣:
正如排序有穩定排序一樣,std::partition把順序排亂了,我們不喜歡。這時有内部排序穩定的std::stable_partition算法出馬:
排序結果如下:
這節我們學習了排序算法的主要架構:快速排序sort,穩定的歸并排序stable_sort,可以部分排序的堆排序算法partial_sort.
我們可以顯式建堆make_heap,也可以将堆轉化成排序sort_heap。
二分查找binary_search用在有序結構上,速度比線性查找要快得多。
nth_element可以按某個值劃分成兩部分,partition可以應用更複雜的條件,但是不穩定。保持内部排序穩定需要用stable_partition.
count和count_if用來計數。
max_element和min_element用于不想做排序隻想擷取最大最小值。