排序算法(一)
排序算法
C++ STL的排序算法(Sorting algorithms)是一组将无序序列排列成有序序列的模板函数或与排列相关的模板函数,如折半查找,堆排序等。排序算法一般适用于序列容器,不适用于内部数据结构较为复杂的关联容器。
1、元素入堆push_heap
一个随机迭代器区间[first,last)的元素{a0,a1,a2,...an-1,an},都可以逻辑上从左到右排列成一个完全二叉树。如果父结点都小于或大于子节点,则构成一个堆。
关于堆排序及相关概念,有:
http://blog.163.com/zhoumhan_0351/blog/static/399542272009918112712205
这里,节点元素的下标从0开始编号,len个节点的最大索引号为len-1。
push_heap算法就是将一个元素置入已构成堆的迭代器区间中,使扩展了一个元素的区间元素仍构成堆。
template<class RanIt>
void push_heap(RanIt first, RanIt last);
在迭代器区间[first,last-2)中,添加元素*(last-1),使[first,last)区间元素仍构成一个堆。(大顶堆)
template<class RanIt, class Pred>
void push_heap(RanIt first, RanIt last, Pred pr);
pr可自行设计大于或小于关系。
2、创建堆make_heap
重新排列元素顺序,使它们逻辑上构成一个堆。
template<class RanIt>
void make_heap(RanIt first, RanIt last);
默认构成大根堆
template<class RanIt, class Pred>
void make_heap(RanIt first, RanIt last, Pred pr);
由pr来确定是构成大顶堆还是小顶堆,还是其它。
它的实现源于想法:将二叉树中的每个仅二层的子树都构成堆,那么整个数据集的布局几乎就满足堆的定义。
异常情况发生时,再进行调整。
template<typename _RandomAccessIterator>
void
make_heap(_RandomAccessIterator first, _RandomAccessIterator last)
{
if (last - first < 2) return;
_DistanceType len = last - first;
_DistanceType parent = (len - 2)/2;
while (true) {
adjust_heap(first, parent, len, _ValueType(*(first + parent)));
if (parent == 0) return;
parent--;
}
}
adjust_heap为调整函数。
template<typename _RandomAccessIterator,typename _Distance, typename _Tp>
void
adjust_heap(_RandomAccessIterator first, _Distance holeIndex,
_Distance len, _Tp value)
{
_Distance topIndex = holeIndex;
_Distance secondChild = 2 * holeIndex + 2;
while (secondChild < len) {
if (*(first + secondChild) < *(first + (secondChild - 1)))
secondChild--;
*(first + holeIndex) = *(first + secondChild);
holeIndex = secondChild;
secondChild = 2 * (secondChild + 1);
}
if (secondChild == len) {
*(first + holeIndex) = *(first + (secondChild - 1));
holeIndex = secondChild - 1;
}
push_heap(first, holeIndex, topIndex, value);
}
3、元素出堆pop_heap
template<class RanIt>
void pop_heap(RanIt first, RanIt last);
将迭代器区间[first,last)中的最大值元素(根结点)移到区间的最后元素位置。原来的最后元素调整为根结点元素,再对[first,last-1)重新构建堆。
template<class RanIt, class Pred>
void pop_heap(RanIt first, RanIt last, Pred pr);
用到辅助函数pop_heap_aux等。
#include <algorithm>
#include <iostream>
#include "list"
#include "vector"
using namespace std;
void print(int x){
cout << x << ' ';
}
int main(void){
int iArray[]={1, 2, 3, 4, 5, 6, 7, 8, 9};
const int len=sizeof(iArray)/sizeof(int);
//
cout << "创建堆" << endl;
make_heap(iArray, iArray+len);
for_each(iArray, iArray+len, print);
cout << endl;
cout << "执行一次元素出堆" << endl;
pop_heap(iArray, iArray+len);
for_each(iArray, iArray+len, print);
cout << endl;
return 0;
}
4、堆排序sort_heap
利用堆进行排序,时间复杂度为O(nlog2n).
template<class RanIt>
void sort_heap(RanIt first, RanIt last);
由区间元素小到大排序。
template<class RanIt, class Pred>
void sort_heap(RanIt first, RanIt last, Pred pr);
依照二元谓词设定的有序关系排序。
template<typename _RandomAccessIterator>
void
sort_heap(_RandomAccessIterator first, _RandomAccessIterator last)
{
while (last - first > 1)
pop_heap(first, last--);
}
5、是否为堆is_heap
由stl.algo.h实现,来判断是否是一个堆。
引申:奇偶数的判断:
String str=Val & 1?"偶数":“奇数”
5、局部排序partial_sort
对部分元素排序,通常用来挑选最小(或最大)的若干元素。使用堆排序实现。时间复杂度为O(nlog2n)
template<class RanIt>
void partial_sort(RanIt first, RanIt middle, RanIt last);
将迭代器区间[first,last)中元素局部排序后,区间[first,middle)元素被排序出来,而[middle,last)区间元素顺序还没有排序。
template<class RanIt, class Pred>
void partial_sort(RanIt first, RanIt middle, RanIt last, Pred pr);
由stl.algo.h实现。
首先调用make_heap将[first,middle)元素创建为堆,然后通过一个for循环,将处部[middle,last)中的每一个元素与堆的具有最大值的根结点相交换,并重建堆。,这样外部更小的结点就被容纳进来排序。
如v={3,9,6,8,-10,7,-11,19,30,12,23},如果middle=5,则结果为v={-11,-10,3,6,7,9,8,19,30,12,23,}
6、局部排序复制partial_sort_copy
也是局部的堆排序,只是排序结果放置到另一个区间中。
template<class InIt, class RanIt>
RanIt partial_sort_copy(InIt first1, InIt last1,
RanIt first2, RanIt last2);
由stl.algo.h实现。
将迭代器区间[first1,last1)中若干最小的元素(或依二元谓词判断pr指定),复制到目标区间[first2,last2),目标区间的元素被排序,被复制的元素个数为min(last1-first1,last2-first2)。
template<class InIt, class RanIt, class Pred>
RanIt partial_sort_copy(InIt first1, InIt last1,
RanIt first2, RanIt last2, Pred pr);
partial_sort_copy先将源迭代器区间的元素原封不动的搬到目标区间,并将目标区间创建为堆,此时根结点的元素值为最大。然后,再对源区间的每个元素进行检查,是否小于目标区间的最大元素,是则作为目标的根结点,并重建目标区间的堆,如此循环。
#include <algorithm>
#include <iostream>
#include "list"
#include "vector"
using namespace std;
void print(int x){
cout << x << ' ';
}
int main(void){
int iArray[]={3, 9, 6, 2, 11, 23, 80, 27, 1, 62, 55};
const int len=sizeof(iArray)/sizeof(int);
vector<int> v1(6);
partial_sort_copy(iArray, iArray+len, v1.begin(), v1.end());
for_each(v1.begin(), v1.end(), print);
cout << endl;
vector<int> v2(len);
partial_sort_copy(iArray, iArray+len, v2.begin(), v2.end());
for_each(v2.begin(), v2.end(), print);
cout << endl;
return 0;
}