快排面试可能会这么问

快速排序算法

本文会从 快排的概述、适用场景、最坏、最好、平均时间复杂度,是否是原地算法,为什么比归并排序好这几个方面进行阐述。

快速排序(Quick Sort)算法是由英国计算机科学家 Tony Hoare 在 1959年发明的。“Quick Sort” 的名字来源于这样一个事实:快速排序速度比任何常见排序算法都要快得多(快两到三倍)。 它是最有效的排序算法之一。它将需要排序的数组分割(splitting)成更小的数组,并且基于与选定的“枢轴”(pivot)元素的比较(comparison)交换元素。 因此,快速排序也被称为“划分交换”(Partition Exchange)排序。 与归并排序一样,快速排序也属于分治方法。

适用实际场景

在深入研究任何算法之前,我们非常有必要了解它在现实世界中的应用。快速排序提供了一种快速且有条理的(methodical)方法来对任何事物的列表(lists of things)进行排序。以下是一些使用快速排序的应用程序。

  • 商业计算Commercial computing):用于各种政府和私人机构,用于对各种数据进行排序,如按姓名或任何给定ID对账户/简介进行排序,按时间或地点对交易进行排序,按名称或创建日期对文件进行排序等。
  • 信息搜索Information search):排序算法有助于更好地搜索信息,还有什么比使用快速排序更快的方法呢?

时间复杂度分析

  • 最好情况:最好的情况发生在划分(partitions)尽可能均衡的情况下,即它们在枢轴两边的个数要么相等,要么相差1。

    • 情况1:当数组具有奇数个元素并且分区后枢轴正好位于中间时,会出现枢轴任一侧的子列表元素个数相等的情况。每个子数组将有 (n-1)/2 个元素。
    • 情况2:如果数组的元素个数是偶数 n,主元两边的两个子数组之间的大小差值为1。 一个分区有 n/2 个元素,另一个分区有 (n/2)-1 个元素。

    在这两种情况下,每个分区最多有n/2个元素,子问题大小的树表示如下:

在这里插入图片描述

快速排序算法的最佳情况复杂度是O(n logn)

  • 最坏情况:当我们遇到最不平衡的分区时会发生这种情况,那么第一次调用需要 n 时间,对 n-1 个 元素的递归调用将需要 n-1时间,对 n-2 元素的递归调用将需要 n -2)时间等等。快速排序的最坏情况时间复杂度为 O ( n 2 ) O(n^{2}) O(n2)

在这里插入图片描述

  • 平均时间复杂度

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

空间复杂度分析

空间复杂度是根据递归堆栈中使用的空间来计算的。 最坏情况下使用的空间是O(n)。 所使用的平均情况空间的阶为O(log n)。最坏情况空间的复杂性为O(n),当算法遇到最坏情况时,即为了获得一个排序列表,我们需要进行n次递归调用。

常见问题

  • 快排是原地(inplace)算法吗?

​ 快速排序是一种原地算法。所有元素的排序都是在原本的数组中完成的。

  • 什么是随机化快排?为什么要随机化

    • 有时候,返回的枢轴可能总是最右边的元素(想升序,但是原本是降序序列)可能会导致最坏的情况。
    • 在这种情况下,在每一步中选择一个随机元素作为枢轴将降低触发最坏情况行为的概率。 我们更有可能选择靠近数组中心的支点,当这种情况发生时,递归分支更均匀,因此算法终止得更快。
    • 运行时复杂度预计为O(n log n),因为所选的随机点应该避免最坏情况的行为。
  • 为什么快排比归并(merge sort)排序好

    • 辅助空间(Auxiliary Space):快速排序是一种就地排序算法,而归并排序使用额外的空间。就地排序意味着不使用额外的存储空间来执行排序(递归堆栈除外)。归并排序需要一个新的临时数组来合并已排序的数组,从而使快速排序成为更好的选择。
    • 最坏情况:快速排序的最坏情况运行时是 O ( n 2 ) O(n^{2}) O(n2),可以通过使用随机快速排序来避免,如前所述。 通过选择随机枢轴来获得平均情况的行为可以提高性能。
    • 缓存友好(Cache Friendly):快速排序也是一种缓存友好的排序算法,因为它在使用数组时具有良好的引用局部性(good locality of reference)。

参考文献

  1. Quick Sort Algorithm

  2. 知乎用户:百度知道:摘自《数据结构与算法分析——C语言描述》

### 三种排序算法的差别 #### 基本思想 - **堆排序**:是利用堆这种数据结构所设计的一种排序算法,它将数组构建成一个最大堆或最小堆,然后依次取出堆顶元素并调整堆,从而实现排序。 - **快速排序**:采用分治法的思想,通过选择一个基准元素,将数组分为两部分,使得左边部分的元素都小于等于基准元素,右边部分的元素都大于等于基准元素,然后递归地对左右两部分进行排序[^3]。 - **归并排序**:同样基于分治法,将已有序的子序列合并,得到完全有序的序列。即先使每个子序列有序,再使子序列段间有序,若将两个有序表合并成一个有序表,称为二路归并[^4]。 #### 性能特点 - **时间复杂度**:堆排序的平均、最好和最坏时间复杂度均为$O(n log n)$;快速排序平均和最好时间复杂度为$O(n log n)$,但最坏情况下会达到$O(n^2)$;归并排序在所有情况下时间复杂度都是$O(n log n)$。 - **空间复杂度**:堆排序的空间复杂度为$O(1)$,是一种原地排序算法;快速排序的空间复杂度平均为$O(log n)$,最坏为$O(n)$;归并排序的空间复杂度为$O(n)$,因为需要额外的空间来合并子序列。 - **稳定性**:堆排序和快速排序是不稳定的排序算法,而归并排序是稳定的排序算法。 #### 适用场景 - **堆排序**:适用于数据量较大且对空间要求较高的场景,例如在处理海量数据时,不需要额外的大量空间。 - **快速排序**:在大多数情况下效率较高,尤其是数据分布比较随机时,但对于已经有序或接近有序的数据,性能会下降。 - **归并排序**:适合处理大规模数据,并且对稳定性有要求的场景,不过需要额外的存储空间。 ### 面试常见题 #### 算法原理类 - 请简要描述堆排序、快速排序、归并排序的基本思想。 - 解释快速排序中基准元素的选择对算法性能的影响。 - 说明归并排序中合并操作的具体实现过程。 #### 性能分析类 - 比较堆排序、快速排序、归并排序的时间复杂度和空间复杂度。 - 分析快速排序在最坏情况下时间复杂度为$O(n^2)$的原因,并说明如何避免。 #### 代码实现类 - 请写出堆排序、快速排序、归并排序的代码。 - 实现快速排序的非递归版本。以下是快速排序非递归的 Java 代码示例: ```java //快速排序非递归 public static void quickSort1(int[] array) { Stack<Integer> stack = new Stack<>(); int left = 0; int right = array.length - 1; //先将最右和最左放入栈中 stack.push(right); stack.push(left); while (!stack.empty()) { //弹出我们的左右两个位置的下标 left = stack.pop(); right = stack.pop(); //当我们的左下标大于等于右下标的时候则跳过当前的排序 if (left >= right) { continue; } int midIndex = findMidIndex(array, left, right); swap(array, left, midIndex); int pivot = partition(array, left, right); stack.push(right); stack.push(pivot + 1); stack.push(pivot - 1); stack.push(left); } } ``` #### 应用场景类 - 给定一个具体的应用场景,如对海量数据进行排序,应该选择哪种排序算法,为什么? - 在需要稳定排序的情况下,应该选择堆排序、快速排序还是归并排序,为什么?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

培之

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值