快速排序并不难

快速排序的核心框架是"二叉树的前序遍历+对撞型双指针"。我们在《一维数组》一章提到过"双指针思路":在处理奇偶等情况时会使用两个游标,一个从前向后,一个是从后向前来比较,根据结果来决定继续移动还是停止等待。快速排序的每一轮进行的时候都是类似的双指针策略,而递归的过程本质上就是二叉树的前序递归调用。

1 快速排序的基本过程

快速排序是将分治法运用到排序问题的典型例子

快速排序基本思想是 :通过一个标记pivot元素将n个元素的序列划分为左右两个子序列left和right,其中left中的元素都比pivot小,right的都比pivot的大,然后再次堆left和right各自再执行快速排序,在将左右子序列排好序之后,整个序列就有序了。这里排序进行左右划分的时候是一直划分到子序列只包含一个元素的情况,然后再递归返回。

我们以关键字序列{26,53,48,15,13,48,32,15}看一下一次划分的过程:

上面红框位置表示当前已经被赋值给了pivot或者其他位置,可以空出来放移动来的新元素了。我们可以看到26最终被放到了属于自己的位置上,不会再变化。而左侧的都比26小,左侧都比26大,因此26的左右两侧可以分别再进行排序。

这一轮过程是什么呢?就是数组增删的时候经常用的双指针策略,我们在数组部分讲过,不再赘述。而这里的每一轮都是一个相向的双指针而已,没有任何神秘的。

根据上述原理,我们可以写代码了,在实现过程中,为了方便实现,会对部分代码微调一下,详细代码如下:

java 复制代码
//原文件QuickSortBasic.java
public static void quickSort(int[] arr, int left, int right) {
        if (left < right) {
            int pivot = arr[right];
            int i = left - 1;
            for (int j = left; j < right; j++) {
                if (arr[j] < pivot) {
                    i++;
                    int temp = arr[i];
                    arr[i] = arr[j];
                    arr[j] = temp;
                }
            }
            //哨兵移动到位置pivotIndex上
            int pivotIndex = i + 1;
            int temp = arr[pivotIndex];
            arr[pivotIndex] = arr[right];
            arr[right] = temp;

            quickSort(arr, left, pivotIndex - 1);
            quickSort(arr, pivotIndex + 1, right);
        }
}

2.第二种实现方式

至于实现,有很多种方式,下面再给一种实现方式:

java 复制代码
void quickSort(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }
        //这里就是一个对撞的双指针操作
        int left = start, right = end;
        int pivot = array[(start + end) / 2];
        
        while (left <= right) {
            while (left <= right && array[left] < pivot) {
                left++;
            }
            while (left <= right && array[right] > pivot) {
                right--;
            }
            if (left <= right) {
                int temp = array[left];
                array[left] = array[right];
                array[right] = temp;
                left++;
                right--;
            }
        }
        //先处理元素再分别递归处理两侧分支,与二叉树的前序遍历非常像
        quickSort(array, start, right);
        quickSort(array, left, end);   
    }

复杂度分析

快速排序的时间复杂度计算比较麻烦一些。从原理来看,如果我们选择的pivot每次都正好在中间,效率是最高的,但是这是无法保证的,因此我们需要从最好、最坏和中间情况来分析

  • 最坏情况就是如果每次选择的恰好都是low结点作为pivot,如果元素恰好都是逆序的,此时时间复杂度为O(n^2)
  • 如果元素恰好都是有序的,则时间复杂度为O(n)
  • 折中的情况是每次选择的都是中间结点,此时序列每次都是长度相等的序列,此时的时间复杂度为(O(nlogn))
相关推荐
终末圆1 分钟前
MyBatis XML映射文件编写【后端 18】
xml·java·开发语言·后端·算法·spring·mybatis
Damon小智3 分钟前
C#进阶-基于雪花算法的订单号设计与实现
开发语言·算法·c#·雪花算法·订单号
没什么技术23 分钟前
java实现LRU 缓存
java·算法·lru
我爱豆子29 分钟前
Leetcode Hot 100刷题记录 -Day19(回文链表)
java·算法·leetcode·链表
繁依Fanyi1 小时前
828 华为云征文|华为 Flexus 云服务器打造 Laverna 在线笔记应用
运维·服务器·笔记·python·算法·华为·华为云
little redcap1 小时前
KMP整理+个人推导+快速上手理解
数据结构·笔记·算法
蠢蠢的打码1 小时前
8587 行编辑程序
数据结构·c++·算法·链表
m0_617775391 小时前
代码随想录训练营第34天| 62.不同路径 、63. 不同路径 II
数据结构·算法·图论
维生素C++1 小时前
【可变模板参数】
linux·服务器·c语言·前端·数据结构·c++·算法
我明天再来学Web渗透1 小时前
【hot100-java】【组合总和】
java·开发语言·数据结构·windows·算法·链表·散列表