1. 常见排序算法对比(必背表)?

算法 平均 最坏 空间 稳定
冒泡O(n²)O(n²)O(1)✅
选择O(n²)O(n²)O(1)❌
插入O(n²)O(n²)O(1)✅
希尔O(n^1.3)O(n²)O(1)❌
快排O(n log n)O(n²)O(log n)❌
归并O(n log n)O(n log n)O(n)✅
堆排O(n log n)O(n log n)O(1)❌
计数O(n+k)O(n+k)O(n+k)✅
桶O(n+k)O(n²)(数据全落一个桶)O(n+k)取决于桶内排序
基数O(d(n+k))O(d(n+k))O(n+k)✅

🎯 面试要点

  • 稳定性含义:相等元素的相对顺序不变。归并稳定、快排/堆排不稳定
  • 快排最坏 O(n²) 的原因:每次基准都是最值(如已有序数组选第一个)→ 随机基准/三数取中缓解
  • JDK 的 Arrays.sort:基本类型用双轴快排,对象用 TimSort(归并优化版)——稳定且利用有序性

2. 手写快速排序(含 partition)?

思路:选基准 → partition 把小于基准的放左边、大于的放右边 → 递归左右。partition 是核心(荷兰国旗/挖坑法)。

快排 + 挖坑法 partition
void quickSort(int[] a, int lo, int hi) {
    if (lo >= hi) return;
    int p = partition(a, lo, hi);
    quickSort(a, lo, p - 1);
    quickSort(a, p + 1, hi);
}

int partition(int[] a, int lo, int hi) {
    int pivot = a[lo];          // 基准挖坑
    while (lo < hi) {
        while (lo < hi && a[hi] >= pivot) hi--;   // 右边找小的
        a[lo] = a[hi];                                  // 填左边的坑
        while (lo < hi && a[lo] <= pivot) lo++;   // 左边找大的
        a[hi] = a[lo];                                  // 填右边的坑
    }
    a[lo] = pivot;                  // 基准归位
    return lo;
}

🎯 面试要点

  • 快排的延伸:TopK 问题用 partition 剪枝——partition 后基准下标 == k 即答案,平均 O(n)(比排序 O(n log n) 快)
  • 双指针(左右扫描)和挖坑是 partition 两种主流写法,会一种+理解即可
  • 随机基准:swap(a, lo, lo + random) 再 partition,防有序数组退化

3. 归并排序和堆排序的实现要点?

归并:分治——拆到单元素,再两两合并有序数组。合并两个有序数组是高频子题(面试常单考)。外部排序(大数据文件排序)也是归并思想。

堆排:建堆(从最后一个非叶子节点下沉)→ 反复把堆顶(最值)与末尾交换并收缩堆。关键:下沉(siftDown)操作。

归并排序(合并步骤)
void mergeSort(int[] a, int lo, int hi) {
    if (lo >= hi) return;
    int mid = (lo + hi) >>> 1;
    mergeSort(a, lo, mid);
    mergeSort(a, mid + 1, hi);
    merge(a, lo, mid, hi);           // 合并两个有序段
}

void merge(int[] a, int lo, int mid, int hi) {
    int[] tmp = new int[hi - lo + 1];
    int i = lo, j = mid + 1, k = 0;
    while (i <= mid && j <= hi) tmp[k++] = a[i] <= a[j] ? a[i++] : a[j++];
    while (i <= mid) tmp[k++] = a[i++];
    while (j <= hi)  tmp[k++] = a[j++];
    System.arraycopy(tmp, 0, a, lo, tmp.length);   // 拷贝回原数组
}

🎯 面试要点

  • 归并的额外空间 O(n):临时数组;注意 (lo+hi)>>>1 防溢出
  • 堆排不稳定、原地 O(1);实现长易错,考手写概率低于快排,但"用堆做 TopK/中位数"必考思路
  • 海量数据 TopK:堆方案 O(n log k),内存只存 k 个——必答场景

4. 二分查找的模板与变体?

二分前提:有序(或具备二段性)。易错点:边界(left < right vs <=)、mid 取整方向、收缩边界——背一个模板并理解。

三种查找模板
// 1. 标准查找(闭区间)
int binarySearch(int[] a, int target) {
    int lo = 0, hi = a.length - 1;
    while (lo <= hi) {                     // 闭区间:<=
        int mid = lo + ((hi - lo) >>> 1);  // 防溢出写法
        if (a[mid] < target) lo = mid + 1;
        else if (a[mid] > target) hi = mid - 1;
        else return mid;
    }
    return -1;
}

// 2. 找左边界:第一个 ≥ target 的位置(可能等于 n,表示全都小于 target)
int lo = 0, hi = a.length;      // 注意 hi 取 n(左闭右开)
while (lo < hi) {
    int mid = lo + ((hi - lo) >>> 1);
    if (a[mid] >= target) hi = mid;     // 收缩右边界
    else lo = mid + 1;
}

// 3. 找右边界:最后一个 ≤ target 的位置
int lo = 0, hi = a.length - 1;
while (lo < hi) {
    int mid = lo + ((hi - lo + 1) >>> 1);  // 上取整防死循环!
    if (a[mid] <= target) lo = mid;
    else hi = mid - 1;
}

🎯 面试要点

  • 左边界 mid 下取整、右边界 mid 上取整——上取整那行最容易写错(死循环)
  • 二分的进阶应用:旋转数组找最小值(比较 a[mid] 与 a[hi])、搜索旋转数组、二维矩阵查找
  • 二分答案:值域二分(如"最小化最大值"的分配问题)——难题常考思想

🎤 常见面试追问

  1. 快排的最坏情况是什么?怎么避免?——已有序数组 + 每次取首元素做基准 → 退化成 O(n²)。避免:随机选基准、三数取中、插入排序兜底(小数组)。
  2. 快排稳定吗?为什么归并稳定?——快排不稳定(partition 交换可能跨过相等元素);归并稳定(合并时相等元素优先取左半,顺序不变)。
  3. 海量数据(内存装不下)怎么排序?——外部排序:分块排序写磁盘 + 多路归并(K 路归并)——归并排序思想的应用。
  4. TopK 问题有几种解法?复杂度?——① 堆:O(n log k),只占 k 内存;② 快排 partition 剪枝:平均 O(n);③ 全排序 O(n log n)(最差)。
  5. Arrays.sort 底层用的什么?——基本类型用双轴快排(Dual-Pivot Quicksort),对象用 TimSort(归并优化版,稳定且利用有序性)。

📖 名词解释(本页术语)

术语 大白话解释
快速排序分治排序:随便选一个数当"基准",把比它小的放左边、大的放右边(分区),然后左右两边递归重复。平均 O(n log n),是最常用的排序。
归并排序分治排序:先不停对半拆,拆到单个元素(天然有序),再两两"合并"成有序序列。稳定,但需要额外空间 O(n)。
堆排序把数组看成"二叉堆"(父节点最大/最小的完全二叉树),反复把堆顶(最值)换到末尾并收缩。原地排序 O(1) 空间,但不稳定。
稳定性相等元素的相对顺序排序后是否保持。稳定 = 保持(归并/冒泡/插入);不稳定 = 可能变(快排/堆排/选择)。
时间复杂度数据量 n 变大时运行时间的增长速度,用大 O 表示(O(n²) 比 O(n log n) 慢得多)。
空间复杂度算法额外占用的内存大小(O(1)=原地不额外开、O(n)=开了一个和输入一样大的数组)。
二分查找在有序数组中查找:每次和中间值比,大则去右半、小则去左半,一次排除一半。O(log n)。
partition(分区)快排的核心一步:把数组按基准分成"左小右大"两部分,并返回基准的最终位置。
外部排序数据太大内存装不下时:分块在内存排好写回磁盘,再用"多路归并"合并——归并思想的实战应用。
⚠️ 本页面由 AI 生成,内容仅供参考,请以官方文档和实际源码为准。