Java面试之常用的排序算法及其复杂度分析

发布时间 - 2026-01-06 00:00:00    点击率:
冒泡排序因逻辑直观、易暴露边界漏洞而常被面试考察,时间复杂度恒为O(n²),空间复杂度O(1);关键优化是添加swapped标志实现提前终止,使最好情况达O(n)。

冒泡排序为什么在面试里总被问,但实际几乎不用

因为它的逻辑最直观,适合考察对「比较-交换」过程的理解,也容易暴露边界处理漏洞。时间复杂度固定是 O(n²),无论最好、最坏、平均情况;空间复杂度是 O(1)(原地排序)。面试手写时,常被忽略的点是提前终止优化——如果某轮没发生任何交换,说明已有序,应直接退出。

public static void bubbleSort(int[] arr) {
    int n = arr.length;
    for (int i = 0; i < n - 1; i++) {
        boolean swapped = false;
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
            }
        }
        if (!swapped) break; // 关键优化,否则无法达到最好情况 O(n)
    }
}
  • 测试用例务必包含已排序数组,验证 swapped 优化是否生效
  • 注意内层循环上界是 n - 1 - i,不是 n - i,否则可能越界
  • Java 中传入的是数组引用,无需返回值,但需明确说明这是原地修改

快速排序的 partition 实现有哪两种主流写法

面试高频考点:lomutohoare 分区方案。前者以最后一个元素为 pivot,代码简洁但对重复元素不友好;后者用首尾双指针,效率略高且天然更稳定(相同元素分布更均匀),但边界条件易错。两者平均时间复杂度都是 O(n log n),最坏退化到 O(n²)(如已排序数组选端点作 pivot),必须强调随机化 pivot 或三数取中来缓解。

// Lomuto 风格(推荐面试写,逻辑清晰)
private static int partition(int[] arr, int low, int high) {
    int pivot = arr[high]; // 取末尾为 pivot
    int i = low - 1;       // i 指向小于 pivot 的右边界
    for (int j = low; j < high; j++) {
        if (arr[j] <= pivot) {
            i++;
            swap(arr, i, j);
        }
    }
    swap(arr, i + 1, high);
    return i + 1;
}
  • hoare 版本中,两个指针从两端向中间走,首次相遇位置不一定是 pivot 最终位置,需额外判断
  • 递归调用时,lomuto 的左右区间是 [low, pi-1][pi+1, high]hoare[low, pi][pi+1, high],容易混淆
  • Java 8 的 Arrays.sort(int[]) 对基本类型用的是双轴快排(Dual-Pivot Quicksort),不是单 pivot,这点可作为延伸加分项

归并排序如何体现“稳定”和“非原地”的本质特征

稳定性意味着相等元素的相对顺序不变,归并排序天然满足——合并时若 left[i] == right[j],优先取 left[i] 即可。但它需要额外 O(n) 空间存临时数组,不是原地算法。时间复杂度严格是 O(n log n),不受输入数据影响,适合对最坏性能有要求的场景(比如实时系统)。

private static void mergeSort(int[] arr, int[] temp, int left, int right) {
    if (left >= right) return;
    int mid = left + (right - left) / 2;
    mergeSort(arr, temp, left, mid);
    mergeSort(arr, temp, mid + 1, right);
    merge(arr, temp, left, mid, right);
}

private static void merge(int[] arr, int[] temp, int left, int mid, int right) { System.arraycopy(arr, left, temp, left, right - left + 1); int i = left, j = mid + 1, k = left; while (i <= mid && j <= right) { if (temp[i] <= temp[j]) { // 注意这里是 <=,保证稳定性 arr[k++] = temp[i++]; } else { arr[k++] = temp[j++]; } } while (i <= mid) arr[k++] = temp[i++]; while (j <= right) arr[k++] = temp[j++]; }

  • 面试手写常漏掉 System.arraycopy 这一步,直接在原数组上合并会导致数据覆盖
  • 递归入口需传入辅助数组 temp,避免每层都 new,否则空间复杂度变成 O(n log n)
  • 堆排序虽也是 O(n log n),但不稳定,且实际性能通常不如优化后的快排或归并

堆排序建堆过程为何是 O(n),而不是直觉上的 O(n log n)

关键在自底向上调整(siftDown)的代价分析:叶子节点不用调整;倒数第二层最多下沉 1 层;倒数第三层最多下沉 2 层……数学推导可得总操作数上限是 2n。而如果用 ninsert(即自顶向下 siftUp),才是 O(n log n)。Java 中没有内置二叉堆类支持索引访问,手写时要注意数组下标从 0 开始时,左子节点是 2*i + 1,不是 2*i

  • 建堆后,每次取出最大值(arr[0])与末尾交换,再对剩余部分 siftDown(0),共 n-1 次,每次 O(log n)
  • 堆排序是原地的,但不稳定——相同元素在下沉过程中可能跨过彼此改变顺序
  • 面试若被问“什么时候选堆排序”,答:内存受限 + 要求最坏 O(n log n) + 不关心稳定性(如 Top-K 问题)

实际编码中,除非特定约束,否则直接用 Arrays.sort()。手写排序算法的价值不在替代它,而在暴露你对数据移动、比较次数、内存访问模式的直觉——这些细节,往往比背下复杂度公式更重要。


# java  # 编码  # app  # 排序算法  # 冒泡排序  # 为什么 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: EditPlus中的正则表达式 实战(4)  惠州网站建设制作推广,惠州市华视达文化传媒有限公司怎么样?  Midjourney怎么调整光影效果_Midjourney光影调整方法【指南】  EditPlus中的正则表达式 实战(1)  深圳网站制作培训,深圳哪些招聘网站比较好?  如何在阿里云ECS服务器部署织梦CMS网站?  如何确保FTP站点访问权限与数据传输安全?  如何快速搭建安全的FTP站点?  Laravel如何配置任务调度?(Cron Job示例)  太平洋网站制作公司,网络用语太平洋是什么意思?  Android使用GridView实现日历的简单功能  Laravel如何使用Guzzle调用外部接口_Laravel发起HTTP请求与JSON数据解析【详解】  如何在不使用负向后查找的情况下匹配特定条件前的换行符  Mybatis 中的insertOrUpdate操作  如何在腾讯云服务器快速搭建个人网站?  BootStrap整体框架之基础布局组件  Claude怎样写结构化提示词_Claude结构化提示词写法【教程】  深圳网站制作平台,深圳市做网站好的公司有哪些?  laravel怎么配置Redis作为缓存驱动_laravel Redis缓存配置教程  Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】  西安市网站制作公司,哪个相亲网站比较好?西安比较好的相亲网站?  如何在IIS中新建站点并解决端口绑定冲突?  如何在搬瓦工VPS快速搭建网站?  Win11怎么设置虚拟桌面 Win11新建多桌面切换操作【技巧】  Laravel如何生成API文档?(Swagger/OpenAPI教程)  Laravel如何与Docker(Sail)协同开发?(环境搭建教程)  PHP怎么接收前端传的文件路径_处理文件路径参数接收方法【汇总】  如何用腾讯建站主机快速创建免费网站?  Laravel控制器是什么_Laravel MVC架构中Controller的作用与实践  Laravel如何使用查询构建器?(Query Builder高级用法)  php打包exe后无法访问网络共享_共享权限设置方法【教程】  如何生成腾讯云建站专用兑换码?  香港服务器网站卡顿?如何解决网络延迟与负载问题?  Laravel如何处理异常和错误?(Handler示例)  HTML5段落标签p和br怎么选_文本排版常用标签对比【解答】  Win10如何卸载预装Edge扩展_Win10卸载Edge扩展教程【方法】  如何正确下载安装西数主机建站助手?  动图在线制作网站有哪些,滑动动图图集怎么做?  如何在IIS服务器上快速部署高效网站?  Laravel如何实现全文搜索功能?(Scout和Algolia示例)  Swift开发中switch语句值绑定模式  如何快速辨别茅台真假?关键步骤解析  Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】  如何获取免费开源的自助建站系统源码?  长沙企业网站制作哪家好,长沙水业集团官方网站?  高防网站服务器:DDoS防御与BGP线路的AI智能防护方案  Laravel如何与Pusher实现实时通信?(WebSocket示例)  如何在阿里云购买域名并搭建网站?  如何解决hover在ie6中的兼容性问题  javascript如何操作浏览器历史记录_怎样实现无刷新导航