前言
相信大家对快速排序并不陌生,在面试中、学习上都会遇到,本篇将对快速排序的概念和代码以及复杂度展开叙述。
一、快速排序基本概念
快速排序的核心就是选择一个基准值(就是一个作参照的数),然后将数组分成左右两部分,左边是比基准值小的元素。右边是比基准值大的元素。 然后递归地对左右两部分继续进行这个过程,直到每部分都排好序。
举例:
假设我们现在对数组[6, 3, 7, 1, 9, 4]这个6个数进行排序:
第一步:选一个基准值(pivot)
我们选第一个数字作为基准值,选择 6 作为基准。
第二步:比较交换
定义两个变量i,j,i位于数组的起始位置,j位于数组的末尾位置,j从右往左找一个小于 6 的数(j–),i从左往右找一个大于 6 的数(即i++),因为我们选取的是第一个元素作为基准值,所以j先移动。
当j直到找到一个小于 6 的数才停下来。接着挪动i直到找到一个大于 6 的数停下来。最后j停在了数字 4 面前,i停在了数字7面前。
然后进行交换,交换之后的数组为:[6, 3, 4, 1, 9, 7]
到此,第一次交换结束。接下来开始j继续向左挪动,他发现了1(比基准数6要小,满足要求)之后又停了下来。i继续向右移动,此时i和j相遇了,i和j都走到1面前。说明此时“探测”结束。
我们将基准数6和1进行交换。交换之后的序列如下:[1, 3, 4, 6, 9, 7]
到此第一轮“探测”真正结束。此时以基准数6为分界点,6左边的数都小于等于6,6右边的数都大于等于6。回顾一下刚才的过程,其实j的任务就是要找小于基准数的数,而i的任务就是要找大于基准数的数,直到i和j碰头为止。
此时我们已经将原来的序列,以6为分界点拆分成了两个序列,左边的序列是[1,3,4],右边的序列是[9,7]。接下来还需要分别处理这两个序列。接下来只要模拟刚才的方法分别处理6左边和右边的序列即可。整个过程如下:
二、Java代码实现
public class QuickSort {
public static int[] quickSort(int[] arr, int left, int right) {
int i, j, temp, t;
if (left > right) {
return null;
}
i = left;
j = right;
temp = arr[left];//基准位
while (i < j) {
//先从右边将j依次往左移动
while (arr[j] >= temp && i < j) {
j--;
}
//再从左边将i依次往右移动
while (arr[i] <= temp && i < j) {
i++;
}
//满足条件则交换
if (i < j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//最后将基准元素为与i和j相等位置的元素交换
arr[left] = arr[i];
arr[i] = temp;
quickSort(arr, left, j - 1);//递归调用左半数组
quickSort(arr, j + 1, right);//递归调用左半数组
return arr;
}
public static void main(String[] args) {
int[] arr = {6, 3, 7, 1, 9, 4};
System.out.println(Arrays.toString(quickSort(arr, 0, arr.length - 1)));
}
}
三、时间和空间复杂度
时间复杂度:
- 最差的情况下时间复杂度为:O( n^2 )
- 平均时间复杂度是:O(nlogn)
- 最优的情况下时间复杂度:O( nlogn )
空间复杂度:
- 最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况。
- 最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况。