算法:有限的步骤,解决逻辑或数学上的问题。唯有用来解决特定问题(如排序、查找、最短路径、三点共线、…),并且获取数学上的效能分析与证明,这样的算法才具有可复用性。STL中算法主要包括:排序、查找、排列组合算法等,以及用于数据移动、复制、删除、比较、组合、运算等等的算法。
特定算法对应特定数据结构:二叉查找树和红黑树主要是为了解决查找问题而发展出来的特殊数据结构;散列表拥有快速查找的能力;最大堆和最小堆可以协助完成堆排序。
算法分析:时间复杂度和空间复杂度。
算法分类:质变算法(会改变操作对象之值)和非质变算法(不改变操作对象之值)。质变算法包括:拷贝、互换、替换、填写、删除、排列组合、分割、随机重排、排序等。非质变算法:查找、匹配、计数、巡访、比较、寻找极值等。
STL算法一般形式:所有泛型算法的前两个参数都是一对迭代器,通常称为first和last,用以标示算法的操作区间。迭代器类型5中:InputIterator、ForwardIterator、BidirectionalIterator、RandomIterator、OutputIterator。注意有的迭代器类型不能作为输入参数,有的迭代器类型不能作为输出参数。将无效的迭代器传给某个算法,虽然是一种错误,却不保证能够在编译时期就能捕捉出来,因为所谓的迭代器类型并不是真实的型别,它们只是function template的一种型别参数。质变算法通常提供两个版本:1)in-place(就地进行)版,就地改变其操作对象;2)copy(另地进行)版,将操作对象的内容复制一份副本,然后在副本上进行修改并返回该副本。无copy版本之质变算法实施于某一段区间元素的副本上,必须自行制作并传递那一份副本。
算法泛化过程:关键在于,只要把操纵对象的型别加以抽象化,把操作对象的标示法和区间目标的移动行为抽象化,整个算法也就在一个抽象层上工作了。
C++最大的优点是几乎所有东西都可以改写为程序员自行定义的形式或行为。迭代器是一种行为类似指针的对象,换句话说,是一种smart pointers。
1)accumulate算法:用来计算初始值init和[first,last)内所有元素的总和。
2)adjacent_difference算法:用来计算[first,last)中相邻元素的差额。
3)inner_product算法:计算[frist1,last1)和[first2,first2+(last1-first1))的一般内积。
4)partial_sum算法:用来计算局部求和。
5)power算法:用来计算某数的n幂次方。这里n幂次是指对自己进行某种运算,达n次。
6)iota算法:用来设定某个区间的内容,使其内的每一个元素从指定的value值开始,呈现递增状态。它改变了区间的内容,所以是一种质变算法。
1)equal算法:用来比较两个序列在[first,last)区间内是否相等。如果第二个序列的元素多于第一个序列元素,则多出的元素不予考虑。
2)fill算法:将[first,last)内的所有元素改填新值。
3)fill_n算法:将[first,last)内的前n个元素改填新值,返回的迭代器指向被填入的最后一个元素的下一个位置。
4)iter_swap算法:将两个ForwardIterator所指对象对调。它是迭代器value type派上用场的一个好例子。该函数必须知道迭代器的value type,才能够据此声明一个对象,用来暂时存放迭代器所指对象。
5)lexicographical_compare算法:以“字典排列方式”对两个序列[first1,last1)和[firs2,last2)进行比较。比较操作针对两序列中的对应位置上的元素进行,并持续直到1)某一组对应元素彼此不相等;2)同时到达last1和last2(当两序列的大小相同);3)到达last1或last2(当两序列的大小不同)。
这两个函数在对应位置上发现第一组不相等的元素时,有以下几种可能:1)如果第一序列的元素较小,返回true,否则返回false;2)如果到达last1而尚未到达last2,返回true;3)如果达到last2而尚未到达last1,返回false;4)如果同时到达last1和last2(即所有元素都匹配)返回false。
6)max算法:取两个对象中的较大值。
7)min算法:取两个对象中的较小值。
8)mismatch算法:用来平行比较两个序列,指出两者之间的第一个不匹配点。返回一对迭代器,分别指向两序列中的不匹配点。如果两序列的所有对应元素都匹配,返回的便是两序列各自的last迭代器。如果第二序列的元素个数比第一序列多,多出来的元素忽略不计。如果第二序列的元素个数比第一序列少,会发生未可预测的行为。
9)swap算法:用来交换(对调)两个对象的内容。
10)copy算法:将输入区间[first,last)内的元素赋值到输出区间[result,result+(last-first))内。它返回一个迭代器:result+(last-first)。copy对其template参数所要求的条件非常宽松。其输入区间只需有Input构成,输出区间只需由OutputIterator构成即可。copy算法,将任何容器的任何一段区间的内容,复制到任何容器的任何一段区间上。copy算法为了提高效率,使用了各种方法,包括:函数重载、型别特性、偏特化等编程技巧。
11)copy_backward算法:将[first,last)区间内的每一个元素,以逆性的方向复制到以result-1为起点,方向亦为逆行的区间上。返回一个迭代器:result-(last-first)。copy_backward所接受的迭代器必须是BidirectionalIterators,才能够“倒行逆施”。使用copy_backward算法,可将任何容器的任何一段区间的内容,复制到任何容器的任何一段区间上。如果输入区间和输出区间完全没有重叠,则毫无问题,否则需特别注意。
set相关算法所接受的set,必须是有序区间,元素值可以重复出现。
1)set_union算法:可构造S1,S2之并集。即构造出S1并S2,此集合内含S1或S2内的每一个元素。S1、S2及其并集都是以排序区间表示。返回值是一个迭代器,指向输出区间的尾端。它是一种稳定操作,其输入区间内的每个元素的相对顺序不会改变。因S1和S2元素不唯一,如果某个值在S1中出现n次,在S2中出现m次,则该值在此算法输出中出现max(m,n)次。其中n个来自S1,剩下的来自S2。
2)set_intersection算法:可构造S1,S2之交集。即构造出S1交S2,此集合内含同时出现于S1和S2内的每一个元素。S1,S2及其交集都是以排序区间表示。返回值为一个迭代器,指向输出区间的尾端。它也是一种稳定操做。因S1和S2元素不唯一,如果某个值在S1中出现n次,在S2中出现m次,则该值在此算法输出中出现min(m,n)次。其全部来自S1。
3)set_difference算法:可构造S1,S2之差集。即它能够构造出S1差S2,此集合内含“出现于S1但不出现与S2”的每一个元素。S1,S2及其交集都是以排序区间表示。返回值为一个迭代器,指向输出区间的尾端。它也是一种稳定的操作。因S1和S2元素不唯一,如果某个值在S1中出现n次,在S2中出现m次,则该值在此算法输出中出现max(n-m,0)次。其全部来自S1。
4)set\symmetric_difference算法:可构造S1、S2之对称差,即(S1差S2)并(S2差S1),此集合内含出现于S1但不出现与S2以及出现于S2但不出现于S1的每一个元素。S1,S2及其对称差集都以排序区间表示。返回值为一个迭代器,指向输出区间的尾端。它也是稳定排序。因S1和S2元素不唯一,如果某个值在S1中出现n次,在S2中出现m次,则该值在此算法输出中出现abs|m-n|次。如果n>m,输出区间内的最后n-m个元素直接有S1复制而来,如果n
此算法只要存在于stl_heap.h中,主要有make_heap(),pop_heap(),push_heap(),sort_heap()。再此不做详细介绍。
(1)单纯的数据处理算法
单纯的数据处理算法主要包括:数据移动、线性查找、计数、循环遍历、逐一对元素施行指定运算等操作。其算法实现中,有时使用STL内建的仿函数和配接器,有时使用自己定义的仿函数。
1)adjacent_find算法:找出第一组满足条件的相邻元素。满足条件可能指相等或满足一定运算关系。
2)count算法:运用equality操作符,将[first,last)区间内的每一个元素拿来和指定值value比较,并返回与value相等的元素个数。
3)count_if算法:指定操作(一个仿函数)pred实施于[first,last)区间内的每一个元素身上,并将“造成pred之计算结果为true”的所有元素的个数返回。
4)find算法:根据equality操作符,循序查找[frist,last)内的所有元素,找出第一个匹配“等同条件”者。如果找到,就返回一个InputIterator指向该元素,否则返回迭代器last。
5)find_if算法:根据指定的pred运算条件(以仿函数表示)循序查找[first,last)内的所有元素,找出第一个令pred运算结果为true者。如果找到就返回一个InputIterator指向该元素,否则返回迭代器last。
6)find_end算法:在序列一[first2,last1)所涵盖的区间中,查找序列二[first2,last2)的最后一次出现点。如果序列一之内不存在“完全匹配序列二”的子序列,便返回迭代器last1。此算法有两个版本。版本一:使用元素型别所提供的equality操作符,版本二:允许用户指定某个二元运算(以仿函数呈现),作为判断元素相等与否的依据。因此算法查找最后一次出现地点,若能逆向查找,则变为首次出现地点。逆向查找的关键在于迭代器的双向移动,故SGI将算法设计为双层架构,称上层函数为dispatch function(分派函数、派送函数)。
7)find_first_of算法:以[first2,last2)区间内的某些元素作为查找目标,寻找他们在[first1,last1)区间内的第一次出现点。举例:假设查找字符序列synesthesia的第一个元音,定义第二个序列为aeiou。此算法返回一个ForwardIterator,指向元音序列中任一元素首次出现于第一序列的地点,此例将指向字符序列的第一个e。如果第一序列并未内含第二序列的任何元素,返回的将是last1。本算法第一个版本使用元素型别所提供的equality操作符,第二版本允许用户指定第一个二元符运算pred。
8)for_each算法:将仿函数f实行于[first,last)区间内的每一个元素身上。f不可改变元素内容,因为first和last都是InputIterators,不保证接受赋予行为。如果要一一修改元素内容,应该使用算法transform()。f可返回一个值,但该值会被忽略。
9)generator算法:将仿函数gen的运算结果填写在[first,last)区间内的所有元素身上。所谓填写,用的是迭代器所指元素值赋值操作符。
10)generator_n算法:将仿函数gen的运算结果填写在从迭代器first开始的n个元素身上,所谓填写,用的是迭代器所指元素的assignment操作符。
11)includes算法:应用于有序区间。判断序列二S2是否“涵盖于”序列一S1。S1和S2都必须是有序集合,其中的元素都可重复(不必唯一)。所谓涵盖,指S2的每一个元素都出现于S1。由于要判断两元素是否相等,故使用greater或less运算符。
12)max_element算法:返回一个迭代器,指向序列之中数值最大的元素。
13)merge算法:应用于有序区间。将两个经过排序的集合S1和S2,合并起来置于另一段空间。所得结果也是一个有序系列。返回一个迭代器,指向最后结果序列的最后一个元素的下一位置。
14)min_element算法:返回一个迭代器,指向序列之中数值最小的元素。
15)partition算法:将区间[first,last)中的元素重新排列。所有被一元条件运算pred判定为true的元素,都会被放在区间的前段,被判定为false的元素,都会被放在区间的后段。这个算法并不保证保留元素的原始相对位置。如果需要保证原始相对位置,应使用stable_partition。
16)remove算法:移除但不删除。移除[first,last)之中所有与value相等的元素。这一算法并不真正从容器中删除那些元素(换句话说容器大小并未改变),而是将每一个不与value相等(即不打算删除)的元素轮番赋值给first之后的空间。返回值ForwardIterator标示出重新整理后的最后一个元素的下一个位置。
17)remove_copy算法:移除[first,last)区间内所有与value相等的元素。它并不真正从容器中删除那些元素(原容器不变),而是将结果复制到一个以result标示起始位置的容器上。注意新容器与就容器空间大小。返回值OutputIterator指出被复制的最后元素的下一个位置。
18)remove_if算法:移除[first,last)区间内所有被仿函数pred核定为true的元素。它并不真正的从容器中删除那些元素(容器大小不变),每个不符合pred条件的元素都会被轮番赋值给first之后的空间。返回值ForwardIterator标示出重新整理后的最后元素的下一个位置。
19)remove_copy_if算法:移除[first,last)区间内所有被仿函数pred评估为true的元素。它并不真正从容器中删除那些元素(原容器没有任何改变),而是将结果复制到一个以result标示起始位置的容器身上。新容器可以和原容器重叠,但如果针对新容器实际给值,超越了就容器大小,就会产生无法预期的结果。返回值OutputIterator指出被复制的最后元素的下一位置。
20)replace算法:将[first,lase)区间内的所有old_value都以new_value取代。
21)replace_copy算法:行为与replace()类似,唯一不同的是新序列会被复制到result所指的容器中。返回值OutputIterator指向被复制的最后一个元素的下一个位置。原序列没有任何改变。
22)replace_if算法:将[first,lase)区间内所有被“pred评估为true”的元素,都以new_value取而代之。
23)replace_copy_if算法:行为与replace_if()类似,但是新序列会被复制到result所指的区间内。返回值OutputIterator指向被复制的最后一个元素的下一个位置。原序列无任何改变。
24)reverse算法:将序列[frist,last)的元素在原容器中颠倒重排。迭代器的双向或随机定位能力,影响了这个算法的效率,所以设计为双层架构。
25)reverse_copy算法:行为类似reverse(),但产生出来的新序列会被置于以result指出的容器中,返回值OutputIterator指向新产生的最后元素的下一位置。原序列没有任何改变。
26)rotate算法:将[first,middle)内的元素和[middle,last)内的元素互换。middle所指的元素会成为容器的第一个元素。看起来和swap_range()功能相似,但swap_range()只能交换两个长度相同的区间,rotate()可以交换两个长度不同的区间。
27)rotate_copy算法:行为类似rotate(),但产生出来的新序列会被置于result所指出的容器中,返回值OutputIterator指向新产生的最后元素的下一个位置。原序列没有任何改变。由于他不需要就地在原容器中调整内容,实现上也就简单得多。旋转操作其实只是两段元素彼此交换,所以只要先把后段复制到新容器的前端,再把前段接续复制到新容器即可。
28)search算法:在序列一[first1,last1)所涵盖的区间中,查找序列二[first2,last2)的首次出现点。如果序列一不存在与序列二完全匹配的子序列,便返回迭代器last1。版本一使用equality操作符,版本二使用用户指定的某个二元运算符(仿函数呈现),作为相等判断的依据。
29)search_n算法:在序列[first,last)所涵盖的区间中,查找连续count个符合条件之元素所形成的子序列,并返回一个迭代器指向该子序列起始处。如果找不到这样的子序列,就返回迭代器last。版本一使用equality操作符,版本二使用用户指定的二元运算符(仿函数)。
30)swap_ranages算法:将[first1,last1)区间内的元素与从first2开始、个数相同的元素互相交换。这两个序列可位于同一容器中,也可位于不同的容器中。如果第二序列的长度小于第一序列,或是两序列在同一容器中且彼此重叠,执行结果未可预期。此算法返回一个迭代器,指向第二个序列中的最后一个被交换元素的下一个位置。
31)transform算法:第一版本以仿函数op作用于[first,last)中的每一个元素身上,并以其结果产生出一个新序列。第二版以仿函数binary_op作用于一双元素身上(其中一个元素来自[first1,last),另一个元素来自从first2开始的序列),并将以其结果产生出一个新序列。如果第二序列的元素少于第一序列,执行结果未可预知。transform()的两个版本都把执行结果放进迭代器result所标示的容器中。result也可以指向原端容器,那么transform()的运算结果就会取代该容器内的元素。返回值OutputIterator将指向结果序列的最后元素的下一位置。
32)unique算法:能够移除重复的元素。每当在[first,last)内遇到有重复元素群,它便移除该元素群中第一个以后的所有元素。注意:unique只移除相邻的重复元素,如果想要移除所有(包括不相邻的)重复元素,必须先将序列排序,使所有重复元素相邻。unique会返回一个迭代器指向新区间的尾端,新区间之内不含相邻的重复元素。此算法稳定,即所保留下来的元素,其原始相对次序不变。unique不改变[first,last)的元素个数,有一些残留数据会留下来。
33)unique_copy算法:从[first,last)中将元素复制到以result开头的区间上;如果面对相邻的重复元素群,只会复制其中第一个元素。返回的迭代器指向以result开头的区间的尾端。
(2)lower_bound算法
此算法应用于有序区间。它是二分查找的一种版本,试图在已排序[first,last)中寻找元素value。如果[first,last)具有与value相等的元素s,便返回一个迭代器,指向其中第一个元素。如果没有这样的元素存在,便返回假设这样的元素存在时应该出现的位置。即返回一个迭代器,指向第一个“不小于value”的元素。如果value大于[first,last)内的任何一个元素,则返回last。其返回值是在不破坏排序状态的原则下,可插入value的第一个位置。算法包括两个版本,版本一采用operator < 进行比较,版本二使用仿函数comp。版本一返回[first,last)中最远的迭代器i,使得[first,i)中的每个迭代器j都满足*j < value。版本二返回[first,last)中最远的迭代器i,使[first,i)中的每个迭代器j都满足comp(*j,value)为真。
(3)upper_bound算法
此算法应用于有序区间。它是二分查找的一种版本。它试图在已排序的[first,last)中寻找value。更明确的说,它会返回在不破坏顺序情况下,可插入value的最后一个合适位置。因STL规范区间圈定时的起头和结尾并不对称([frist,last))故upper_bound和lower_bound的返回值意义大有不同。如果查找某值,而它的确出现在区间之内,则lower_bound返回的是一个指向该元素的迭代器,然后upper_bound不这么做。因为upper_bound所返回的是在不破坏排序状态的情况下,value可被插入的“最后一个”合适位置。如果value存在,那么他返回的迭代器将指向value的下一位置。而非指向value本身。算法包括两个版本,版本一采用operator<进行比较,版本二使用仿函数comp。版本一返回[first,last)中最远的迭代器i,使得[first,i)中的每个迭代器j都满足value<*j不为真。版本二返回[first,last)中最远的迭代器i,使[first,i)中的每个迭代器j都满足comp(value,*j)不为真。
(4)binary_search算法
此算法应用于有序区间。它是二分查找的一种版本。它试图在已排序的[first,last)中寻找元素value。如果[first,last)内有等同于value的元素,便返回true,否则返回false。binary_search是利用lower_bound先找出“假设value存在的话,应该出现的位置”,然后再对比该位置上的值是否为所要查找的目标,并返回对比结果。binary_search第一版本使用operator < 进行比较,第二版本采用仿函数comp进行比较。即当且仅当[first,last)中存在一个迭代器i使得*i < value和value < *i皆不为真,返回true。当且仅当[first,last)中存在一个迭代器i使得comp(*i,value)和comp(value,*i)皆不为真,返回true。
(5)next_permutation算法
它是排列组合算法。它会取[first,last)所标示之序列的下一个排列组合。如果没有下一个排列组合,便返回fasle,否则返回true。此算法有两个版本:版本一使用元素型别所提供的less-than操作符来决定下一个排列组合,版本二使用仿函数comp来决定。算法的详细过程为:首先从最尾端开始往前寻找两个相邻元素,令第一个元素为*i,第二个元素为*ii,且满足*i<*ii,找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个大于*i的元素,令为*j,将i,j元素对调,再将ii之后的所有元素颠倒排序,即为下一个排列组合。
(6)pre_permutation算法
它也是排列组合算法。它会取[first,last)所标示之序列的上一个排列组合。其算法详细过程为:首先从最尾端开始往前寻找两个相邻元素,令第一个元素为*i,第二个元素为*ii,且满足*i>*ii。找到这样一组相邻元素后,再从最尾端开始往前检验,找出第一个小于*i的元素,令为*j,将i,j元素对调,再将ii之后的所有元素颠倒排列,即为上一个排列组合。
(7)random_shuffle算法
将[first,last)的元素次序随机重排。即从n!种可能的元素排列顺序中随机选一种,此处N为last-first。N个元素的序列,其排列方式有N!种,random_shuffle会产生一个均匀分布,因此任何一个排列被选中的机率为1/N!。这很重要,因为有不少算法在其第一阶段过程中必须获得序列的随机重排,但如果其结果未能形成“在N!个可能排列上均匀分布”,变很容易造成算法错误。STL中此算法实现包括两个版本。版本一:使用内部随机数产生器;版本二:使用一个会产生随机数的仿函数,该仿函数的传递方式是by reference,而非by value。随机数产生器一个重要特质:他拥有局部状态,每次被调用时都会有所改变,并因此保证产生出来的随机数能够随机。
(8)partial_sort/partial_sort_copy算法
此算法接受一个middle迭代器(位于序列[first,last)之内),然后重新安排[first,last),使序列中的middle-first个最小元素以递增顺序排序,置于[first,middle)内。其余last-middle个元素安置于[middle,last)中,不保证有任何特定顺序。使用sort算法,同样能够保证较小的N个元素以递增顺序置于[first,first+N)之内。选择partial_sort而非sort的唯一理由是效率。如果只是挑出前N个最小元素排序,当然比对整个序列排序快。partial_sort有两个版本,版本一:使用less-than操作符;版本二:使用仿函数comp。算法内部使用heap sort完成。
partial_sort任务是找出middle-first个最小元素,故首先界定出区间[first,middle),利用make_heap()将它组织成一个max-heap,然后可将[middle,last)中的每一个元素拿来与max-heap的最大值比较(max-heap的最大值就在第一个元素身上);如果小于该最大值,就互换位置并重新保持max-heap的状态。如此,走遍整个[middle,last)时,较大的元素都已被抽离出[first,middle),此时再以sort_heap()将[first,middle)做一次排序,即可。
partial_sort和partial_sort_copy两者行为逻辑完全相同,只不过后者将(last-first)个最小的元素(或最大元素,视comp而定)排序后的所的结果置于[result_first,result_last)。
(9)sort算法
sort算法接受两个RandomAccessIterators(随机存取迭代器),然后将区间内的所有元素以渐增方式由小到大重新排序。STL中的关系型容器都拥有自动排序功能(底层使用RB-tree实现),不需要用到sort算法。至于序列式容器中的stack、queue和priority-queue都有特别的出入口,不允许用户对元素排序。剩下vector、deque和list,前者的迭代器属于RandomAccessIterators,适合使用sort算法,list迭代器属于BidirectionalIterators,不在STL标准之列的slist,其迭代器更属于ForwardIterators,都不适用于sort算法。如果要对list或slist排序,应该使用它们自己提供的member functions sort()。
排序在日常生活中非常重要。字典需要排序,书籍索引需要排序,磁盘目录需要排序,名片需要排序,图书馆藏需要排序,户籍数据需要排序。任何数据只要想快速查找,就需要排序。排序可以改善效率。
STL中的sort算法,数据量大时采用Quick Sort,分段递归排序,一旦分段后的数据量小于某个门槛,为避免Quick Sort的递归调用带来过大的额外负担,就改用Insertion Sort。如果递归层次过深,改用Heap Sort。STL中排序是使用Quick Sort和Insertion Sort完成的。
1)Insertion Sort算法:Insertion Sort以双层循环形式进行。外循环遍历整个序列,每次迭代决定出一个子区间;内循环遍历子区间内的每一个“逆转对”倒转过来。逆转对指任何两个迭代器i,j,i < j而*i > *j。一旦不存在逆转对,则排序完毕。时间复杂度为O(N^2)。数据量较少时效果比较好。STL中提供了两个版本实现。版本一:以渐增方式排序,即以operator < 为两个元素的比较函数;版本二:允许用户指定一个仿函数,作为两元素的比较函数。
2)Quick Sort算法:它是目前最快的排序算法,平均复杂度为O(NlogN),最坏情况下将达到O(N^2)。不过IntroSort(类似median-of-three QuickSort的一种排序算法)可将最坏情况推进到O(NlogN)。
Quick Sort算法叙述如下。假设s代表将被处理的序列:1)如果S的元素个数为0或1,结束;2)取S中的任何一个元素,当做枢轴(pivot)v;3)将S分割为L,R两段,使L内的每个元素都小于或等于v,R内的每一个元素都大于或等于v;4)对L,R递归执行Quick Sort。
Quick Sort精神在于将大区间分割为小区间,分段排序。每一个小区间排序完成后,串接起来的大区间也就完成了排序。最坏情况发生在分割时产生出一个空的子区间——那完全没有达到分割的预期效果。
2)Median-of-Three(三点中值):注意,任何一个元素都可以被选来当做枢轴,但是其合适与否却会影响Quick Sort的效率。为避免“元素当初输入时不够随机”所带来的恶化效应,最理想最稳当的方式就是取整个序列的头、尾、中央三个位置,以其中值作为枢轴。成为median-of-three partitioning或median-of-three Quick Sort。为了能够快速取出中央位置的元素,显然迭代器必须能够随机定位,即必为RandomAccessIterators。
3)Partitioning(分割)算法:令头迭代器first向尾部移动,尾端迭代器last向头部移动。当*first大于或等于枢轴时就停下来,当*last小于或等于枢轴时也停下来,然后检验两个迭代器是否交错。如果first仍然在左而last仍然在右,就将两者元素互换,然后各自调整一个位置(向中央逼近),再继续进行相同的行为。如果发现两个迭代器交错了(即!(first < last)),表示整个序列已经调整完毕,此时的first为轴,将序列分为左右两半,左半部分所有元素都小于等于枢轴,右半部分所有元素值都大于等于枢轴。
IntroSort算法:Insertion Sort在面对几近排序的序列时,表现出很好的排序功能,效果更好。sort算法中,使用混合排序算法:Introspective Sorting(内省式排序)IntroSort,其行为大部分情况下与median-of-3 Quick Sort完全相同,但是当分割行为有恶化为二次行为的倾向时,能够自我侦测,转为Heap Sort,使效率维持在Heap sort的O(NlogN),又比一开始使用Heap Sort效果好。
(10)equal_ranage算法
此算法应用于有序区间。它是二分查找的一个版本,试图在已排序的[first,last)中寻找value。它返回一对迭代器i和j,其中i是在不破坏次序的前提下,value可插入的第一个位置,亦即lower_bound,j是在不破坏次序的前提下,value可插入的最后一个位置,亦即upper_bound。因此,[i,j)内的每个元素都同于value,而且[i,j)是[first,last)之中符合此性质的最大子区间。此算法有两个实现版本,第一版本采用operator<进行比较;第二版本采用仿函数comp进行比较。
(11)inplace_merge算法
此算法应用于有序区间。如果两个连接在一起的序列[first,middle)和[middle,last)都已经排序,那么inplace_merge可将他们结合成单一一个序列,并扔保有序性。如果原先两个序列是递增排序,执行结果也是递增排序,如果原先两序列是递减排序,执行结果也会是递减排序。它也是一种稳定的排序。每个作为操作数据来源的子序列中的元素相对次序都不会变动,如果两个子序列有等同的元素,第一个序列的元素被排在第二个序列元素之前。它的实现有两个版本,版本一使用operator<进行比较;版本二使用仿函数comp进行比较。
(12)nth_element算法
此算法重新排序[first,last),使迭代器nth所指的元素,与整个[first,last)完整排序后,同一位置的元素同值。此外并保证[nth,last)内没有任何一个元素小于(更精确地说是不大于)[first,nth)内的元素,但对于[first,nth)和[nth,last)两个子区间内的元素次序则无任何保证,此为与partial_sort很大的不同处。nth_element比较近似partition而非sort或partition_sort,它有两个实现版本:版本一,使用operator<进行比较,版本二,使用仿函数comp进行比较。此算法只接受RandomIterators。
实现方法:不断地以median-of-3 partitioning(以首、尾、中央三点中值为枢轴之分割法),将整个序列分割为更小的左(L)、右(R)子序列。如果nth迭代器落于左子序列,就再对左子序列进行分割,否则就再对右子序列进行分割。以此类推,直到分割后的子序列长度不大于3(够小了),便对最后这个待分割的子序列做Insertion Sort即可。
(13)merge_sort算法
将两个有序区间归并成一个有序区间,效果不错,利用分而治之思想,以各个击破的方式来对一个区间进行排序。首先,将区间对半分割,左右两段各自排序,再利用inplace_merge重新组合为一个完整的有序序列。对半分割的操作可以递归进行,直到每一小段的长度为0或1(此时该小段自动完成排序)。
参考:STL源码剖析——侯捷