● 如果你需要在vector、string、deque或数组上进行完全排序,你可以使用sort或stable_sort。
● 如果你有一个vector、string、deque或数组,你只需要排序前n个元素,应该用partial_sort。
● 如果你有一个vector、string、deque或数组,你需要鉴别出第n个元素或你需要鉴别出最前的n个元素,而不用知道它们的顺序,nth_element是你应该注意和调用的。
● 如果你需要把标准序列容器的元素或数组分隔为满足和不满足某个标准,你大概就要找partition或stable_partition。
● 如果你的数据是在list中,你可以直接使用partition和stable_partition,你可以使用list的sort来代替sort和stable_sort。如果你需要partial_sort或nth_element提供的效果,你就必须间接完成这个任务
二、举例
1、partial_sort
如果你有一个Widget的vector,你想选择20个质量最高的Widget发送给你最忠实的客户,你需要做的只是排序以鉴别出20个最好的Widget,剩下的可以保持无序。你需要的是部分排序,有一个算法叫做partial_sort,它能准确地完成它的名字所透露的事情:
bool qualityCompare(const Widget& lhs, const Widget& rhs){
// 返回lhs的质量是不是比rhs的质量好
}
...
// 把最好的20个元素(按顺序)放在widgets的前端
partial_sort(widgets.begin(), widgets.begin() + 20, widgets.end(), qualityCompare);
// 使用widgets...
...
调用完partial_sort后,widgets的前20个元素是容器中最好的而且它们按顺序排列,也就是,质量最高的Widget是widgets[0],第二高的是widgets[1]等。这就是你很容易把最好的Widget发送给你最好的客户,第二好的Widget给你第二好的客户等。
2、nth_element
1)找到区间前n个元素
如果你关心的只是能把20个最好的Widget给你的20个最好的客户,但你不关心哪个Widget给哪个客户,partial_sort就给了你多于需要的东西。在那种情况下,你需要的只是任意顺序的20个最好的Widget。
// 把最好的20个元素放在widgets前端,但不用担心它们的顺序
nth_element(widgets.begin(), widgets.begin() + 19, widgets.end(), qualityCompare);
正如你所见,调用nth_element本质上等价于调用partial_sort。它们结果的唯一区别是partial_sort排序了在位置1-20的元素,而nth_element不。但是两个算法都把20个质量最高的Widget移动到vector前端。
2)找到区间的中值或者找到在指定百分点的元素
现在谈谈nth_element,这个名字奇怪的算法是个引人注目的多面手。除了能帮你找到区间顶部的n个元素,它也可以用于找到区间的中值或者找到在指定百分点的元素:
// 方便地表示widgets的起点和终点
vector
vector
// 迭代器的变量,这个迭代器指示了下面代码要找的中等质量等级的Widget的位置
vector
// 兴趣的Widget会是有序的vector的中间
goalPosition = begin + widgets.size() / 2;
// 找到widgets中中等质量等级的值
// goalPosition现在指向中等质量等级的Widget
nth_element(begin, goalPosition, end, qualityCompare);
...
// 下面的代码能找到质量等级为75%的Widget
// 指出兴趣的Widget离开始有多远
vector
// 找到质量值为75%的Widget
// begin + goalOffset现在指向质量等级为75%的Widget
nth_element(begin, begin + goalOffset, end, qualityCompare);
...
3、partition
比如现在假设,你不需要鉴别出20个质量最高的Widget。取而代之的是,你需要鉴别出所有质量等级为1或2的。当然你可以按照质量排序这个vector,然后搜索第一个质量等级比2差的。那就可以鉴别出质量差的Widget的区间起点
但是完全排序需要很多工作,而且对于这个任务做了很多不必要的工作。一个更好的策略是使用partition算法,它重排区间中的元素以使所有满足某个标准的元素都在区间的开头。
比如,移动所有质量等级为2或更好的Widget到widgets前端,我们定义了一个函数来鉴别哪个Widget是这个级别。
bool hasAcceptableQuality(const Widget& w){
// 返回w质量等级是否是2或更高;
}
传这个函数给partition:
// 把所有满足hasAcceptableQuality的widgets移动到widgets前端
//并且返回一个指向第一个不满足的widget的迭代器
vector
hasAcceptableQuality);
此调用完成后,从widgets.begin()到goodEnd的区间容纳了所有质量是1或2的Widget,从goodEnd到widgets.end()的区间包含了所有质量等级更低的Widget。如果在分割时保持同样质量等级的Widget的相对位置很重要,我们自然会用stable_partition来代替partition。
三、部分排序总结
算法sort、stable_sort、partial_sort和nth_element需要随机访问迭代器,所以它们可能只能用于vector、string、deque和数组。对标准关联容器排序元素没有意义,因为这样的容器使用它们的比较函数来在任何时候保持有序。
唯一我们可能会但不能使用sort、stable_sort、partial_sort或nth_element的容器是list,list通过提供sort成员函数做了一些补偿。(有趣的是,list::sort提供了稳定排序。)所以,如果你想要排序一个list,你可以,但如果你想要对list中的对象进行partial_sort或nth_element,你必须间接完成。
一个间接的方法是把元素拷贝到一个支持随机访问迭代器的容器中,然后对它应用需要的算法。
另一个方法是建立一个list::iterator的容器,对那个容器使用算法,然后通过迭代器访问list元素。
第三种方法是使用有序的迭代器容器的信息来迭代地把list的元素接合到你想让它们所处的位置。正如你所见,有很多选择。
partition和stable_partition与sort、stable_sort、partial_sort和nth_element不同,它们只需要双向迭代器。因此你可以在任何标准序列迭代器上使用partition和stable_partition。