这几天在读程杰的《大话数据结构》,上面有介绍快速排序算法。关于排序算法一直没有下功夫研究过,全都是用现成的,这次花了点时间自己动手写了写,顺便也检验了自己对 C++11 的掌握程度。
快速排序原理上很简单,一个数组分成前后两部分,保证前边的数都比后面的小。之后递归的再这么分下去就行了。
下面是代码,首先需要一个能交换两个元素的 swap 函数,这个 swap 用到了 C++ 11 的 auto 关键字。说实话要是没有 auto 这个代码我还真不会写。。。
template<typename RandomIterator>
inline void swapValueByIterator(RandomIterator p, RandomIterator q)
{
auto t = *p;
*p = *q;
*q = t;
}
之后要有个函数能将数组分成前后两部分,后面的比前面的大。
这个函数需要注意的是 end 开始时指向的是一个最后一个元素的下一个元素,所以需要向前移动一下。之所以这样设计是为了与 C++ 中的 iterator 一致。
template<typename RandomIterator, typename LessThan>
RandomIterator partition(RandomIterator start, RandomIterator end, LessThan func)
{
--end;
while(start != end)
{
while (start != end && func(*start, *end))
{
--end;
}
swapValueByIterator(start, end);
while(start != end && !func( *end, *start ))
{
++start;
}
swapValueByIterator(start, end);
}
return start;
}
之后就是主角了:
template<typename RandomIterator, typename LessThan>
void quickSort(RandomIterator start, RandomIterator end, LessThan func)
{
if(start != end)
{
RandomIterator pivot = partition(start, end, func);
quickSort(start, pivot, func);
quickSort(pivot + 1, end, func);
}
}
下面是个例子:
template<typename T>
bool my_less(T& x, T& y)
{
return x < y;
}
template<typename RandomIterator>
void print( RandomIterator start, RandomIterator end)
{
if(start != end) cout << *start;
while(++start != end)
{
cout << ", " << *start;
}
cout << endl;
}
int main(int argc, char *argv[])
{
(void) argc;
(void) argv;
double test[] = {5,1, 6,7, 1,4, 8,4, 3,9, 6,4 ,2,1, 5, 34, 4, 3, 5, 8, 5,9,4,2};
print(std::begin(test), std::end(test));
quickSort(std::begin(test), std::end(test), my_less<double>);
print(std::begin(test), std::end(test));
}
当然这个代码还可以优化,比如数组元素比较少时应该用 insertSort:
template<typename RandomIterator, typename LessThan>
void insertSort(RandomIterator start, RandomIterator end, LessThan func)
{
for( ;start < end; ++start)
{
auto min = *start;
RandomIterator min_pos = start;
for (RandomIterator p = start + 1; p != end; ++p) // 寻找最小的元素
{
if( func(*p, min) )
{
min = *p;
min_pos = p;
}
}
swapValueByIterator(start, min_pos);
}
}
const int INSERT_SORT_N = 15;
template<typename RandomIterator, typename LessThan>
void quickSort2(RandomIterator start, RandomIterator end, LessThan func)
{
if(end - start > INSERT_SORT_N)
{
RandomIterator pivot = partition(start, end, func);
quickSort(start, pivot, func);
quickSort(pivot + 1, end, func);
}
else
{
insertSort(start, end, func);
}
}
quickSort 还可以写成 尾递归的形式,我们知道编译器会对尾递归进行优化。
template<typename RandomIterator, typename LessThan>
void quickSort3(RandomIterator start, RandomIterator end, LessThan func)
{
if(end - start > INSERT_SORT_N)
{
while(start != end )
{
RandomIterator pivot = partition(start, end, func);
quickSort(start, pivot, func);
start = pivot + 1;
}
}
else
{
insertSort(start, end, func);
}
}
至此,一个 quicksort 函数就基本完成了。当然还可以进一步的优化,不过那都是些细枝末节的优化了。对我们学习快速排序的原理意义不大。