一 真题2010-10
2010-10. 采用递归方式对顺序表进行快速排序。下列关于递归次数的叙述中,正确的是( )。
A. 递归次数与初始数据的排列次序无关
B. 每次划分后,先处理较短的分区可以减少递归次数
C. 每次划分后,先处理较长的分区可以减少递归次数
D. 递归次数与每次划分后得到的分区的处理顺序无关
二 题目要素解析
核心概念 :快速排序(Quick Sort) 的递归过程。
- 基本思想:分治法。选取一个枢轴(Pivot),将数组划分为两部分,左部分小于等于枢轴,右部分大于等于枢轴,然后分别对两部分递归排序。
- 递归树:递归过程可以看作是一棵二叉树。根节点是整个数组,左孩子是左分区,右孩子是右分区。
递归次数的定义:
- 指的是递归函数被调用的总次数(或者递归栈的深度,视具体语境,但在本题选项分析中主要指逻辑上的调用次数)。
关键区别:
- 递归次数 vs 递归深度 vs 时间复杂度。
三 哔哔详解
A. 递归次数与初始数据的排列次序无关
❌ 错误
- 若初始序列已有序,且选首元素为基准 → 每次划分极不平衡(如
[1], [2,3,...,n]) - 递归次数多(接近 nn 次)
- 若初始序列随机 → 划分较平衡 → 递归次数少(约 lognlogn 层)
- 结论 :递归次数与初始排列密切相关
B. 每次划分后,先处理较短的分区可以减少递归次数
❌ 错误(常见误解)
- 先处理短分区 是一种工程优化技巧 ,用于减少最大递归深度(栈空间)
- 但它不改变总的递归调用次数
- 无论先处理左还是右,最终都要处理两个子分区(只要长度 ≥ 2)
- 总递归次数不变
💡 举例:
划分后得到
[a](短)和[b,c,d,e](长)
- 先排
[a]:递归深度小(因为大分区在栈底)- 先排
[b,c,d,e]:递归深度大(可能栈溢出)- 但总递归调用次数相同
C. 每次划分后,先处理较长的分区可以减少递归次数
❌ 错误
- 同上,处理顺序不影响总次数
- 反而可能导致栈溢出,是更差的选择
D. 递归次数与每次划分后得到的分区的处理顺序无关
✅ 正确
- 递归次数由需要排序的子问题数量决定
- 处理顺序(先左后右 or 先右后左)只是执行顺序不同
- 所有子问题都必须被处理,因此总递归次数不变
✅ 类比:遍历二叉树,先序/中序/后序遍历的节点访问总数相同,只是顺序不同
四 参考答案
D✅
五 考点精析
5.1 快速排序基本概念和性质
5.1.1 基本概念
1. 定义
快速排序基于分治法思想,通过选取一个「枢轴(Pivot)」将待排序数组划分为两部分:左部分元素≤枢轴,右部分元素≥枢轴;然后递归地对左右两部分重复上述过程,直到子数组长度为 1(天然有序),最终整个数组有序。
2. 核心术语
- 枢轴(Pivot):划分的基准元素,选取方式直接影响快排性能(如选第一个元素、最后一个元素、中间元素、随机元素);
- 划分(Partition):快排的核心操作,将数组按枢轴分成左右两部分的过程;
- 递归划分:对划分后的子数组重复划分,直到子数组有序。
5.1.2 基本性质
| 性质 | 说明 |
|---|---|
| 时间复杂度 | - 最好/平均: O(n \\log n) - 最坏: O(n\^2) (如已排序数组选首元素为 pivot) |
| 空间复杂度 | - 平均: O(\\log n) (递归栈深度) - 最坏: O(n) |
| 稳定性 | ❌ 不稳定(相同元素可能因交换改变相对位置) |
| 原地排序 | ✅ 是(仅需 O(1) 额外空间,不计递归栈) |
| 适用场景 | 大规模数据、通用排序(工业界主流) |
💡 优化策略:
- 随机选 pivot → 避免最坏情况
- 三数取中(median-of-three)
- 小数组切换为插入排序
5.2 算法思想及代码实现
5.2.1 算法思想(Hoare 分区法)
步骤:
- 选基准(Pivot):通常选首、尾、中位数或随机元素
- 分区(Partition):
- 设置左右指针
i,j i从左向右找 ≥ pivot 的元素j从右向左找 ≤ pivot 的元素- 交换
arr[i]与arr[j] - 直到
i >= j,返回分割点
- 设置左右指针
- 递归排序:
- 对
[low, pivot_index-1]和[pivot_index+1, high]递归调用
- 对
✅ 关键 :每次 partition 后,pivot 就位于其最终排序位置
5.2.2 代码实现
5.2.2.1 C 语言(经典 Hoare 分区 + 递归)
c
#include <stdio.h>
// 分区函数(Hoare 版)
int partition(int arr[], int low, int high) {
int pivot = arr[low]; // 选首元素为基准
int i = low - 1;
int j = high + 1;
while (1) {
do { i++; } while (arr[i] < pivot);
do { j--; } while (arr[j] > pivot);
if (i >= j) return j;
// 交换
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 快速排序主函数
void quickSort(int arr[], int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi); // 注意:Hoare 返回 j,左区间为 [low, pi]
quickSort(arr, pi + 1, high); // 右区间为 [pi+1, high]
}
}
// 测试
int main() {
int arr[] = {10, 80, 30, 90, 40, 50, 70};
int n = sizeof(arr) / sizeof(arr[0]);
quickSort(arr, 0, n - 1);
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
return 0;
}
⚠️ 注意:Hoare 分区返回的
pi与 Lomuto 不同,递归区间也不同
5.2.2.2 Java(Lomuto 分区 + 随机 pivot)
java
import java.util.Random;
public class QuickSort {
private static Random rand = new Random();
// Lomuto 分区(更易理解)
private static int partition(int[] arr, int low, int high) {
// 随机选 pivot 并移到末尾
int randomIndex = low + rand.nextInt(high - low + 1);
swap(arr, randomIndex, high);
int pivot = arr[high];
int i = low - 1; // 小于 pivot 区的末尾
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr, i, j);
}
}
swap(arr, i + 1, high); // pivot 归位
return i + 1;
}
public static void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
private static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 测试
public static void main(String[] args) {
int[] arr = {10, 80, 30, 90, 40, 50, 70};
quickSort(arr, 0, arr.length - 1);
for (int x : arr) System.out.print(x + " ");
}
}
✅ 推荐使用 Lomuto + 随机 pivot,逻辑清晰且避免最坏情况
5.2.2.3 Python(简洁递归版)
python
import random
def quick_sort(arr):
if len(arr) <= 1:
return arr
# 随机选 pivot
pivot = random.choice(arr)
left = [x for x in arr if x < pivot]
mid = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + mid + quick_sort(right)
# 测试
if __name__ == "__main__":
arr = [10, 80, 30, 90, 40, 50, 70]
print(quick_sort(arr))
✅ 优点:代码极简;缺点:非原地排序,空间开销大
📌 面试可用,工程慎用(内存敏感场景)
六 考点跟踪
| 年份 | 题号 | 考查内容 | CSDN 参考链接 | VX参考链接 |
|---|---|---|---|---|
| 2010 | 第10题 | 快速排序特征 | ||
| 2012 | 第10题 | 快速排序特征 |
说明 :本文内容基于公开资料整理,参考了包括但不限于《数据结构》(严蔚敏)、《计算机操作系统》(汤小丹)、《计算机网络》(谢希仁)、《计算机组成原理》(唐朔飞)等国内高校经典教材,以及其他国际权威著作。同时,借鉴了王道、天勤、启航等机构出版的计算机专业考研辅导系列丛书 中的知识体系框架与典型题型分析思路。文中所有观点、例题解析及文字表述均为作者结合自身理解进行的归纳与重述,未直接复制任何出版物原文。内容仅用于学习交流,若有引用不当或疏漏之处,敬请指正。