快速排序
快速排序算法基于分治策略,其基本思想为:从一个数组中随机选出一个数N(作为基准值),通过一趟排序将数组分割成三个部分(小于N的区域 、等于N的区域 、大于N的区域),然后再按照此方法对小于区的和大于区分别递归进行,从而达到整个数据变成有序数组。
举例说明
以 47、29、71、99、78、19、24、47 数列为例进行排序。
一般选择中间的一个数或者头尾的数,这里直接选择第 1 个数 47 作为基准数,接着把比 47 小的数字移动到左边,把比 47 大的数字移动到右边,对于相等的数字不做移动。
1.向左移动i,直到r[i].key<47,将24和47进行交换。
2.向右移动j,直到r[j].key>47,将71和47进行交换。
3.向左移动i,直到r[i].key<47,将19和47进行交换。
4.接下来我们继续对 i、j 进行操作,直到i==j,第一次划分结束。
代码实现
class Solution {
/**
* 快速排序
*
* @param arr 待排序的数组
* @param low 数组的起始地址
* @param high 数组的结束地址
*/
public void quickSort(int[] arr, int low, int high) {
if (low < high) {
int k = partition(arr, low, high);
quickSort(arr, low, k - 1);
quickSort(arr, k + 1, high);
}
}
/**
* 快速排序,分割的过程
*
* @param arr 待排序的数组
* @param low 数组的起始地址
* @param high 数组的结束地址
* @return k 值
*/
public int partition(int[] arr, int low, int high) {
int pivot = arr[low];//基准值
while (low < high) {
// high 向左移动,直至遇到比pivot值小的记录,停止移动
while (low < high && arr[high] >= pivot) {
high--;
}
// 交换两个元素的位置
swap(arr, low, high);
//low 向右移动,直至遇到比pivot值大的记录,停止移动
while (low < high && arr[low] <= pivot) {
low++;
}
// 交换两个元素的位置
swap(arr, low, high);
}
return low;
}
/**
* 交换数组中两个元素的位置
*/
public static void swap(int arr[], int low, int high) {
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
}
时间复杂度(均分)
如果每次都能差不多均匀分,那么
- 每次循环的耗时主要就在这个 while 循环里,也就是 O(right - left);
- 均分的话那就是 logn 层;
- 所以总的时间是O(nlogn).
空间复杂度(均分)
递归树的高度是 logn,
每层的空间复杂度是 O(1),
所以总共的空间复杂度是 O(logn).
时间复杂度(不均分)
如果每次都能取到最大/最小值,那么递归树就变成了这个样子:
时间复杂度为:O(n^2)
空间复杂度(不均分)
这棵递归树的高度就变成了 O(n).
每层的空间复杂度是 O(1),
所以总共的空间复杂度是 O(n).
稳定性
在 swap 的时候,已经破坏了元素之间的相对顺序,所以快排并不具有稳定性。(本人的一个理解:如上面的例子,47、29、71、99、78、19、24、47* 在交换时,可能会将47* 调换到47前面)
参考
1.https://mp.weixin.qq.com/s/dwdWivM7LL4dJEN9zUDzCA
2.https://mp.weixin.qq.com/s/-IrUlsSBPIex9I3AY3_OXA