1.快速排序
快速排序是冒泡排序的一种改进,它平均的时间复杂度为。
快速排序的思路如下:对于输入的数组a[p: r]
- 分解:以a[p]作为基准值,将数组分为三段 a[p:q-1] a[q]和a[q+1: r],并且满足a[p: q-1]的值小于等于a[q],而a[q+1:r]的值大于等于a[q]。下标q在划分过程中确定。
- 递归:递归调用快速排序分别对a[p:q-1]和a[q+1:r]进行排序。
- 合并:由于快速排序是原地排序,所以不需要合并。
由于快速排序是对冒泡排序的改进,所以快速排序在每次排序都会确认一个元素的最终值。
快速排序的代码并不算困难:
/**
* 对a[p:r]进行划分 扩展两个区域a[p:i]和a[j:r]
* @param nums: 数组
* @param start: 基准值索引 在这里也是第一个值的索引
* @param end: 数组最后一个值的索引
*/
template<class Type>
int partition(Type nums[], int start, int end) {
int left = start, right = end + 1;
Type x = nums[start];
//将 < x的元素交换到左边区域 > x的元素交换到右边区域
while (true) {
while (nums[++left] < x && left < end);
while (nums[--right] > x);
if (left >= right) break;
Type temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
}
nums[start] = nums[right];
nums[right] = x;
return right;
}
partition函数定死start为基准值,同时也是数组的起始位置。
按照快速排序的思路,快速排序的一趟遍历就是让小于等于基准值的元素在基准值左边;大于等于基准值的元素在基准值的右边。partition函数所做的工作如下:
- left从左向右遍历,找到一个大于基准值的元素;
- right从右向左,找到一个小于基准值的元素,然后两个元素相互交换。重复上述过程直至left >= right。
- 之后让基准值和right所在的元素进行交换。
/**
* 快速排序
* @param nums: 数组
* @param p: 基准值索引
* @param r: 数组最后一个值的索引
*/
template<class Type>
void quickSort(Type nums[], int p, int r) {
if (p < r) {
int q = partition(nums, p, r);
quickSort(nums, p, q - 1);
quickSort(nums, q + 1, r);
}
}
之后是主函数:
int main() {
int a[] = { 4, 2, 6, 7, 5, 3, 2};
int len = sizeof(a) / sizeof(a[0]);
quickSort(a, 0, len - 1);
for (int i = 0; i < len; i++)
cout << a[i] << " ";
cout << endl;
return 0;
}
2.快速排序的改进
快速排序的性能就在于基准值的选择,最坏情况下就是每次选择的基准值都是当前的最值,此时分成了n-1个元素和1个元素,其最坏时间复杂度为O()。所以基准值的选择也是比较重要的。
2.1 随机选择一个元素
随机选择一个元素作为基准值。
/**
* 随机选择一个基准值index属于[start, end],之后nums[start] 交换 nums[index],
* @params nums: 数组
* @params start: 起始索引
* @params end: 结束索引
*/
template<class Type>
int randomPartition(Type nums[], int start, int end) {
//随机数
random_device rd;
default_random_engine random(rd());
//随机数分布对象 [start, end]
uniform_int_distribution<unsigned> uniform(start, end);
int index = uniform(random);
//交换值
Type temp = nums[start];
nums[start] = nums[index];
nums[index] = temp;
return partition(nums, start, end);
}
这个函数在a[start: end]中随机选择一个元素作为基准值。
default_random_engine为c++11提供的 随机数生成函数,其包含在#include <random>库中。
template<class Type>
void quickSort(Type nums[], int p, int r) {
if (p < r) {
int q = randomPartition(nums, p, r);
quickSort(nums, p, q - 1);
quickSort(nums, q + 1, r);
}
}
接着要在快速排序中调用这个函数。
3.测试
测试基于LeetCode的排序数组
快速排序的性能测试:
2.1的快速排序的性能测试:
4. 参考
- 《计算机算法设计与分析 王晓东》