跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava算法

Java 七大排序算法(中篇):冒泡与快速排序实现

冒泡排序通过相邻元素比较交换逐步将最大元素移至末尾,优化后可在最好情况下达到 O(N)。快速排序基于分治思想,选取基准值划分左右子序列。本文详细讲解 Hoare 法、挖坑法及前后指针法的分区逻辑,并引入三数取中和小数组插入排序优化递归深度,提升整体性能。

刀狂发布于 2026/3/21更新于 2026/5/55 浏览
Java 七大排序算法(中篇):冒泡与快速排序实现

数据结构:冒泡排序与快速排序详解

1. 冒泡排序

1.1 算法思路

冒泡排序的核心在于通过相邻元素的比较和交换,将较大的元素逐步'浮'到数组末尾。具体过程如下:

  1. 从前往后依次比较相邻元素,若前一个比后一个大则交换。
  2. 一趟遍历后,当前未排序部分的最大元素会到达末尾。
  3. 重复上述过程,直到所有元素有序。

冒泡排序动图

1.2 实现步骤

我们定义两个索引变量:i 表示已排序的趟数,j 为当前比较的起始位置。 每一趟遍历未排序区间,比较 array[j] 与 array[j+1],若前者大则交换,否则 j 自增。

1.3 基础代码

// 交换下标分别为 i 和 j 的两个元素
public static void swap(int[] array, int i, int j) {
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}

// 冒泡排序
public static void bubbleSort(int[] array) {
    for (int i = 0; i < array.length - 1; i++) {
        for (int j = 0; j < array.length - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                swap(array, j, j + );
            }
        }
    }
}
1

1.4 优化策略

如果某一趟遍历中没有发生任何交换,说明数组已经有序,可以提前结束。 我们可以引入一个标记位 flag,若发生交换则置为 true。若一趟结束 flag 仍为 false,直接返回。

优化逻辑示意

优化后的代码:

/**
 * 冒泡排序优化版
 * 时间复杂度:最坏 O(N^2),最好 O(N)
 * 空间复杂度:O(1)
 * 稳定性:稳定
 */
public static void bubbleSortOptimized(int[] array) {
    for (int i = 0; i < array.length - 1; i++) {
        boolean flag = false;
        for (int j = 0; j < array.length - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                swap(array, j, j + 1);
                flag = true;
            }
        }
        // 没发生交换,说明已经有序
        if (!flag) {
            return;
        }
    }
}

public static void swap(int[] array, int i, int j) {
    int tmp = array[i];
    array[i] = array[j];
    array[j] = tmp;
}

1.5 特性总结

  1. 理解难度:低,非常适合入门。
  2. 时间复杂度:平均和最坏情况均为 O(N^2)。
  3. 空间复杂度:O(1),原地排序。
  4. 稳定性:稳定,相等元素不会改变相对顺序。

2. 快速排序

快速排序由 Hoare 于 1962 年提出,是一种基于二叉树结构的交换排序方法。其核心思想是分治:选取基准值,将序列分割为小于基准值的左子序列和大于基准值的右子序列,然后递归处理。

2.1 递归框架

快速排序的递归结构与二叉树的前序遍历非常相似。在处理时,可以先写出递归骨架,再专注于如何划分区间。

/**
 * @param start 待排序区间起始下标
 * @param end   待排序区间结束下标
 */
private static void quick(int[] array, int start, int end) {
    if (start >= end) return; // 区间无效或只剩一个元素
    
    // 分区操作,返回基准值最终位置
    int pivot = partition(array, start, end);
    
    // 递归排左序列
    quick(array, start, pivot - 1);
    // 递归排右序列
    quick(array, pivot + 1, end);
}

2.2 分区实现方式

2.2.1 Hoare 法

这是最经典的版本。以首元素为基准 key,先从右向左找比 key 小的数,再从左向右找比 key 大的数,交换它们。当左右指针相遇时,将 key 与相遇点交换。

注意:为什么先走右边?如果先走左边,相遇点的值可能比 key 大,导致基准值放错位置;先走右边能保证相遇点一定小于等于 key。等号必须取,防止指针不动越界。

private static int partitionHoare(int[] array, int left, int right) {
    int key = array[left];
    int i = left;
    while (left < right) {
        // 先走右边,找比 key 小的
        while (left < right && array[right] >= key) {
            right--;
        }
        // 再走左边,找比 key 大的
        while (left < right && array[left] <= key) {
            left++;
        }
        // 交换
        swap(array, left, right);
    }
    // 相遇后,将 key 放到正确位置
    swap(array, i, left);
    return left;
}
2.2.2 挖坑法

将首元素作为基准 key 取出,此时 left 位置成为一个'坑'。从右边找小填坑,再从左边找大填右边的坑,循环往复,最后将 key 填入最终的坑位。

private static int partitionPit(int[] array, int left, int right) {
    int key = array[left];
    while (left < right) {
        // 右边找小填左边坑
        while (left < right && array[right] >= key) {
            right--;
        }
        array[left] = array[right];
        
        // 左边找大填右边坑
        while (left < right && array[left] <= key) {
            left++;
        }
        array[right] = array[left];
    }
    // 填补最终空位
    array[left] = key;
    return left;
}
2.2.3 前后指针法

使用 prev 和 cur 两个指针。cur 负责寻找小于基准值的元素,找到后 prev 前进并交换。此方法逻辑较难直观理解,建议结合图示掌握。

private static int partitionPrePost(int[] array, int left, int right) {
    int prev = left;
    int cur = left + 1;
    while (cur <= right) {
        if (array[cur] < array[left] && array[++prev] != array[cur]) {
            swap(array, cur, prev);
        }
        cur++;
    }
    swap(array, prev, left);
    return prev;
}

2.3 快速排序优化

优化一:三数取中法

当数组本身有序时,快排退化为单分支递归,性能急剧下降。我们可以通过'三数取中'选择更合适的基准值,避免极端情况。

private static int midOfThree(int[] array, int left, int right) {
    int mid = (left + right) / 2;
    if (array[right] > array[left]) {
        if (array[mid] < array[left]) return left;
        else if (array[mid] > array[right]) return right;
        else return mid;
    } else {
        if (array[mid] > array[left]) return left;
        else if (array[mid] < array[right]) return right;
        else return mid;
    }
}

// 在 partition 前调用,确保基准值不是最大或最小
int index = midOfThree(array, start, end);
swap(array, index, start);
优化二:小区间插入排序

递归深度过深会增加栈开销。当子数组长度较小时(如小于 15),直接使用插入排序效率更高。

private static void insertSortRange(int[] array, int begin, int end) {
    for (int i = begin + 1; i <= end; i++) {
        int tmp = array[i];
        int j = i - 1;
        for (; j >= begin; j--) {
            if (array[j] > tmp) {
                array[j + 1] = array[j];
            } else {
                break;
            }
        }
        array[j + 1] = tmp;
    }
}

// 在主递归函数中加入判断
if (end - start + 1 <= 15) {
    insertSortRange(array, start, end);
    return;
}

2.4 完整递归实现示例

整合上述优化,以下是基于挖坑法的完整快速排序结构:

public static void quickSort(int[] array) {
    quick2(array, 0, array.length - 1);
}

private static void quick2(int[] array, int start, int end) {
    if (start >= end) return;
    
    // 小区间优化
    if (end - start + 1 <= 15) {
        insertSortRange(array, start, end);
        return;
    }
    
    // 三数取中优化
    int index = midOfThree(array, start, end);
    swap(array, index, start);
    
    int pivot = partitionPit(array, start, end);
    quick2(array, start, pivot - 1);
    quick2(array, pivot + 1, end);
}

// 辅助函数省略...(见上文)

快速排序的非递归实现在后续内容中展开。理解这些算法的关键在于动手画图模拟过程,一旦掌握了分治与划分的逻辑,编写代码自然水到渠成。

目录

  1. 数据结构:冒泡排序与快速排序详解
  2. 1. 冒泡排序
  3. 1.1 算法思路
  4. 1.2 实现步骤
  5. 1.3 基础代码
  6. 1.4 优化策略
  7. 1.5 特性总结
  8. 2. 快速排序
  9. 2.1 递归框架
  10. 2.2 分区实现方式
  11. 2.2.1 Hoare 法
  12. 2.2.2 挖坑法
  13. 2.2.3 前后指针法
  14. 2.3 快速排序优化
  15. 优化一:三数取中法
  16. 优化二:小区间插入排序
  17. 2.4 完整递归实现示例
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 基于视觉语言动作的竞速无人机自主导航 RaceVLA 深度解析
  • Open WebUI Docker 容器化部署最佳实践
  • 深入理解 HTML5 Web Workers:提升网页性能的关键技术
  • Qwen-Multiple-Angles 插件实现图像多角度生成支持 ComfyUI 与 WebUI
  • MacBook 上如何正确安装 nvm 和 Node.js
  • 八大排序算法核心解析与实战实现
  • 前端错误处理最佳实践与策略
  • 基于 Python 的汽车销售数据可视化大屏系统设计
  • 小巧的 MCPHost:命令行下让大模型调用外部工具
  • DDD 领域建模核心概念与实战步骤
  • 鸣潮 QQ 机器人部署指南:集成大语言模型与游戏功能
  • ONLYOFFICE AI 功能详解与使用指南
  • 服务器科普:什么是服务器及网站为何离不开它
  • 拆解机器人底盘 DDSM400 钕强磁外转子 65mm 伺服轮毂电机
  • 如何为前端项目编写优秀的 AI Agent Skills
  • Python 3.14.2 Windows 安装指南
  • 若依 (RuoYi) 低代码框架深度解析与选型建议
  • 微分的本质:从变化率到线性映射的飞跃 —— 可视化 Python 教程
  • Java 中高级面试核心考点解析:集合与线程同步
  • DeepSeek-R1 大模型基于 MS-Swift 框架的部署、推理与微调实践

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online