第二期:快速排序(与严老教程略有差异)
此博客根据哔哩哔哩教学视频整理,仅用于自己学习,如有侵犯版权,立马删除。
题目分析:
快速排序(Quicksort)是对冒泡排序的一种改进。以从小到大排序为例进行分析。快速排序采用双向查找的策略,每一趟选择当前所有子序列中的一个关键字作为枢纽轴,将子序列中比枢纽轴小的前移,比枢纽轴大的后移,当本趟所有子序列都被枢轴按上述规则划分完毕后将会得到新的一组更短的子序列,他们将成为下趟划分的初始序列集。(小的往前仍,大的往后扔)
示例:{6, 1, 2, 7, 9, 3, 4, 5, 10, 8}
第一步: 选取基准数,一般选取 array[0] 为基准数,定义变量 j 从数组的最右边往左遍历,如果比基准数大,继续往左移动,当遇到比基准数小的值,暂停。定义变量 i 从数组的最左边往右遍历,如果比基准数小,继续往右移动,当遇到比基准数大的值,暂停。此时,索引 i 指向的值为 7 ,索引 j 指向的值为 5。再将 7 与 5 交换 。得到的数组是 {6, 1, 2, 5, 9, 3, 4, 7, 10, 8}
第二步:此时索引 i 指向的值为 5 ,索引 j 指向的值为 7。索引 i,j 继续移动一步后均停下, 索引 i 指向的值为 9 ,索引 j 指向的值为 4。再将 9 与 4 交换 。得到的数组是 {6, 1, 2, 5, 4, 3, 9, 7, 10, 8}
第三步:索引 j 继续移动, j 指向 3 暂停,此时索引 i,j 相等,即i=j。那么不能再继续移动。需要将基准数array[0] 与 3 交换。得到的数组是 {3, 1, 2, 5, 4, 6, 9, 7, 10, 8}
第四步:此时 6 这一个元素的位置就确定下来了。只需要将基准数的前面以及后面分别利用这样的思想进行 递归 排序即可。
第五步: 将原始数组传入给自己,变化索引 i,j 即可。当然也可以换一种思路,变换数组(更复杂),索引 i,j 无需变化。
基准数左边:quickSort(array, left, i-i)
基准数右边:quickSort(array, i+i, right)
Java代码:
public class QuickSort {
public static void main(String[] args) {
int[] array = {6,0,1,45,9,7,5,12,4,10};
quickSort(array,0, array.length-1);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
/**
* 快速排序法
* @param arr 待排序的数组
* @param left 左端索引
* @param right 右端索引
*/
private static void quickSort(int[] arr, int left, int right) {
// 进行判断,如果左边索引比右边索引大,是不合法的,直接使用return结束这个方法
if (left>right){
return;
}
// 定义变量保存基准数
int base = arr[left];
// 定义变量i,指向最左边
int i = left;
// 定义变量j,指向最右边
int j = right;
// 具体循环次数不知道,利用while循环
while (i!=j){
// 由j从右往左检索比基准数小的,就停下
while (arr[j]>=base && i<j){
j--;
}
// 由i从左往右检索比基准数大的,就停下
while (arr[i]<=base && i<j){
i++;
}
// 代码走到这里,说明i停下了,j也停下了,需要交换了此时对应的值。
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 如果上面的while循环条件不成立,会跳出循环,往下执行
// 说明i和j相遇了。此时需要交换基准数和arr[i]的值
arr[left] = arr[i];// 或者 arr[left] = arr[j];
arr[i] = base;
// 排基准数的左边
quickSort(arr, left,i-1);
// 排基准数的右边
quickSort(arr,j+1,right);
}
简要分析:
时间复杂度分析:
- 最好情况(待排序列接近无序)时间复杂度为 O ( n log 2 n ) {\rm O}(n{\log _2}n) O(nlog2n);
- 最坏情况(待排序列接近有序并且是与待排逆序)时间复杂度为 O ( n 2 ) {\rm O}({n^2}) O(n2);
- 平均时间复杂度为 O ( n log 2 n ) {\rm O}(n{\log _2}n) O(nlog2n)。
空间复杂度分析: 主要是递归造成的栈空间的使用
- 最好情况,递归树的深度为 log 2 n {\log _2}n log2n; 其空间复杂度也就为 O ( log 2 n ) {\rm O}({\log _2}n) O(log2n);
- 最坏情况,需要进行n‐1递归调用,其空间复杂度为 O ( n ) {\rm O}(n) O(n);
- 平均情况,空间复杂度也为 O ( log 2 n ) {\rm O}({\log _2}n) O(log2n)。
稳定性分析: 由于关键字的比较和交换是跳跃进行的,因此,快速排序是一种不稳定的排序方法。