C/C++ Quick Sort Algorithm
本系列文章由 @YhL_Leo 出品,转载请注明出处。
文章链接: http://blog.csdn.net/yhl_leo/article/details/50255069
快速排序算法,由C.A.R.Hoare于1962年提出,算法相当简单精炼,基本策略是随机分治。首先选取一个枢纽元(pivot),然后将数据划分成左右两部分,左边的大于(或等于)枢纽元,右边的小于(或等于枢纽元),最后递归处理左右两部分。分治算法一般分成三个部分:分解、解决以及合并。快排是就地排序,所以就不需要合并了。只需要划分(partition)和解决(递归)两个步骤。因为划分的结果决定递归的位置,所以Partition是整个算法的核心。快速排序最佳运行时间O(nlogn),最坏运行时间O(n2),随机化以后期望运行时间O(nlogn)。

首先来看一段升序快速排序算法的实现代码:
#include <iostream>
using namespace std;
void quickSort(int arr[], int first, int last);
void printArray(int arr[], const int& N);
void main()
{
int test[] = { 1, 12, 5, 26, 7, 14, 3, 7, 2 };
int N = sizeof(test)/sizeof(int);
cout << "Size of test array :" << N << endl;
cout << "Before sorting : " << endl;
printArray(test, N);
quickSort(test, 0, N-1);
cout << endl << endl << "After sorting : " << endl;
printArray(test, N);
}
/**
* Quicksort.
* @param a - The array to be sorted.
* @param first - The start of the sequence to be sorted.
* @param last - The end of the sequence to be sorted.
*/
void quickSort(int arr[], int left, int right)
{
int i = left, j = right;
int tmp;
int pivot = arr[(left + right) / 2];
/* partition */
while (i <= j)
{
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j)
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
/* recursion */
if (left < j)
quickSort(arr, left, j);
if (i < right)
quickSort(arr, i, right);
}
/**
* Print an array.
* @param a - The array.
* @param N - The size of the array.
*/
void printArray(int arr[], const int& N)
{
for(int i = 0 ; i < N ; i++)
cout << "array[" << i << "] = " << arr[i] << endl;
}
1 划分(Partition)
划分分为两个步骤:
- 选取枢纽元
- 根据枢纽元所在位置将数组分为左右两部分
1.1 选取枢纽元
所谓的枢纽元,也就是将数组分为两部分的参考元素,选取的方式并不唯一。对于完全随机的数据,枢纽元的选取不是很重要,往往可以直接选取数组的初始位置的元素作为枢纽元。但是实际中,数据往往是部分有序的,如果仍然使用数组两端的数据作为枢纽元,划分的效果往往不好,导致运行时间退化为O(n2)。因此,这里给出的代码就是选取数组中间位置元素:
int pivot = arr[(left + right) / 2];
也有三数取中的方法、随机选取法等。
1.2 根据枢纽元分为左右两部分
上文算法代码使用的是Hoara的双向扫描方法:
/* partition */
while (i <= j)
{
while (arr[i] < pivot)
i++;
while (arr[j] > pivot)
j--;
if (i <= j)
{
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
i++;
j--;
}
}
除此以外还有单向扫描,双向扫描(区别于Hoara的方法)以及改进的双向扫描等。
1.3 关于双向扫描的思考
- 内层循环中的
while循环条件是用<=/>=还是</>。- 一般的想法是用
<=/>=,忽略与枢纽元相同的元素,这样可以减少不必要的交换,因为这些元素无论放在哪一边都是一样的。但是如果遇到所有元素都一样的情况,这种方法每次都会产生最坏的划分,也就是一边1个元素,令一边n−1个元素,使得时间复杂度变成O(n2)。而如果用严格</>,虽然两边指针每此只挪动1位,但是它们会在正中间相遇,产生一个最好的划分。 - 也有人分析,认为内循环使用严格
</>,可以减少内循环。 - 因此,建议内循环使用
</>。
- 一般的想法是用
- 小数组的特殊处理
- 按照上面的方法,递归会持续到分区只有一个元素。而事实上,当分割到一定大小后,继续分割的效率比插入排序要差。由统计方法得到的数值是50左右,也有采用20的,这样
quickSort函数就可以优化成:
- 按照上面的方法,递归会持续到分区只有一个元素。而事实上,当分割到一定大小后,继续分割的效率比插入排序要差。由统计方法得到的数值是50左右,也有采用20的,这样
void newQuickSort(int arr[], int left, int right, int thresh)
{
if(right - left > thresh)
{
// quick sort for large array
quickSort(arr, left, right);
}
else
{
// insertion sort for small array
insertionSort(arr, left, right);
}
}
2 递归(Recursive)
即重复上述的划分(Partition)操作,最底层的情形是数列的大小是0或者1。快速排序算法和大多数分治排序方法一样,都有两次递归调用,但是快速排序的递归在函数尾部,因此可以实施尾递归优化,从而缩减堆栈的深度,减少算法的时间复杂度。
最后,贴上前文代码运行的过程:
参考文献
C/C++ Quick Sort Algorithm的更多相关文章
- Quick Sort Algorithm
快速排序算法实现代码: //============================================================================ // Name : ...
- 1101. Quick Sort (25)
There is a classical process named partition in the famous quick sort algorithm. In this process we ...
- PAT1101:Quick Sort
1101. Quick Sort (25) 时间限制 200 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CAO, Peng There is a ...
- A1101. Quick Sort
There is a classical process named partition in the famous quick sort algorithm. In this process we ...
- 1101 Quick Sort
There is a classical process named partition in the famous quick sort algorithm. In this process we ...
- PAT甲1101 Quick Sort
1101 Quick Sort (25 分) There is a classical process named partition in the famous quick sort algorit ...
- PAT 1101 Quick Sort[一般上]
1101 Quick Sort(25 分) There is a classical process named partition in the famous quick sort algorith ...
- What does Quick Sort look like in Python?
Let's talk about something funny at first. Have you ever implemented the Quick Sort algorithm all by ...
- PAT 甲级 1101 Quick Sort
https://pintia.cn/problem-sets/994805342720868352/problems/994805366343188480 There is a classical p ...
随机推荐
- ubuntu16.04安装配置mysql数据库,分割视频为帧图像
参考http://wiki.ubuntu.org.cn/MySQL%E5%AE%89%E8%A3%85%E6%8C%87%E5%8D%97 版本为5.7 一.安装 安装命令sudo apt-get i ...
- 0x28 IDA*
一个早上做完了我真牛B 就是A*用于DFS啊,现在我才发现迭代加深真是个好东西. poj3460 %了%了我们的目标是把它的顺序变对,那么第i个位置的值+1是要等于第i+1个位置的值的.对于一个操作, ...
- python fuzzy c-means demo
摘自:http://pythonhosted.org/scikit-fuzzy/auto_examples/plot_cmeans.html#example-plot-cmeans-py,加入了自己的 ...
- SQL的优化技巧
一.一些常见的SQL实践 (1)负向条件查询不能使用索引 select * from order where status!=0 and stauts!=1 not in/not exists都不是好 ...
- Oracle DBA优化数据库性能心得
如今的优化己经向优化等待(waits)转型了,实际中性能优化最根本的出现点也都集中在IO,这是影响性能最主要的方面,由系统中的等待去发现Oracle库中的不足.操作系统某些资源利用的不合理是一个比较好 ...
- c++面向对象程序设计 谭浩强 第五章答案
1: #include <iostream> using namespace std; class Student {public: void get_value() {cin>&g ...
- 7. Reverse Integer[E]整数反转
题目 Given a 32-bit signed integer, reverse digits of an integer. Example1: x = 123, return 321 Exampl ...
- 洛谷P4014 分配问题(费用流)
题目描述 有 nn 件工作要分配给 nn 个人做.第 ii 个人做第 jj 件工作产生的效益为 c_{ij}cij .试设计一个将 nn 件工作分配给 nn 个人做的分配方案,使产生的总效益最大. ...
- Java对象、Json、Xml转换工具Jackson使用
在Java项目中將一个对象转换成一段Json格式的字符串是非常常见的,能够实现这种需求的工具包也比较多,例如Gson.JSON-lib.Jackson等等.本文主要介绍Jackson的使用,Jacks ...
- Android实现图片相似度
Android实现图片相似度 最近公司有一个需求,就是希望能判断用户提交的照片是否是身份证的正面或者反面.可以通过预设一张拍摄清晰的身份证正面或者反面,来对比是否相似,那么问题就转化为如何计算两张图片 ...