【算法系列】快速排序详解
文章目录
- 快速排序的多种实现方式
- 1. 基本快速排序(Lomuto 分区方案)
- 1.1 基本原理
- 1.2 步骤
- 1.3 Java 实现示例
- 2. Hoare 分区方案
- 2.1 基本原理
- 2.2 步骤
- 2.3 Java 实现示例
- 3. 三数取中法
- 3.1 基本原理
- 3.2 步骤
- 3.3 Java 实现示例
- 4. 尾递归优化
- 4.1 基本原理
- 4.2 步骤
- 4.3 Java 实现示例
- 总结
快速排序的多种实现方式
快速排序(Quick Sort)是一种高效的排序算法,采用分治法策略。它通过选择一个“基准”元素,将数组分割成两个子数组,并递归地对这两个子数组进行排序。本文将详细介绍几种常见的快速排序实现方式,并讨论它们的特点和适用场景。

1. 基本快速排序(Lomuto 分区方案)
1.1 基本原理
Lomuto 分区方案是最常见的快速排序实现之一。它选择数组的最后一个元素作为基准(pivot),然后重新排列数组,使得所有小于基准的元素位于基准的左侧,所有大于基准的元素位于基准的右侧。
1.2 步骤
- 选择基准:通常选择数组的最后一个元素作为基准。
- 初始化指针:设置一个指针
i,用于追踪当前小于基准的最后一个元素的位置。 - 遍历数组:
- 遍历数组中的每个元素(除了基准元素),如果当前元素小于等于基准,则将该元素与
i指针所指向的元素交换,并将i向右移动一位。
- 遍历数组中的每个元素(除了基准元素),如果当前元素小于等于基准,则将该元素与
- 放置基准:最后将基准元素与
i + 1位置的元素交换,使得基准元素处于正确的位置。 - 递归排序:对基准两侧的子数组分别递归执行上述过程,直到每个子数组只剩下一个元素或为空。
1.3 Java 实现示例
public static void quickSort(int[] arr) {quickSort(arr, 0, arr.length - 1);
}private static void quickSort(int[] arr, int low, int high) {if(low >= high) {return;}int pivotIndex = lomuto(arr, low, high);quickSort(arr, low, pivotIndex - 1);quickSort(arr, pivotIndex + 1, high);
}private static int lomuto(int[] arr, int low, int high) {int pivot = arr[high]; // 选择最后一个元素作为基准int i = low - 1; // 指向当前小于基准的最后一个元素for (int j = low; j < high; j ++) {if(arr[j] <= pivot) {i ++;if(i != j) {swap(arr, i, j);System.out.println(low + "|" + high + " " + i + "|" + j + " " + Arrays.toString(arr));}}}// 将基准元素放回数组正确位置swap(arr, i + 1, high);System.out.println(low + "|" + high + "\t \t" + Arrays.toString(arr));return i + 1;
}/*** 交换数组索引为i和j的两个元素* @param arr* @param i* @param j*/
private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
2. Hoare 分区方案
2.1 基本原理
Hoare 分区方案由 C.A.R. Hoare 提出,是另一种常见的快速排序实现。它通过选择一个基准元素(通常是第一个或最后一个元素),然后重新排列数组,使得所有小于基准的元素位于基准的左侧,所有大于基准的元素位于基准的右侧。
2.2 步骤
- 选择基准:通常选择数组的第一个元素作为基准。
- 初始化双指针:设置两个指针,分别指向数组的起始位置和结束位置。
- 移动指针:
- 左指针向右移动,直到找到一个大于基准的元素。
- 右指针向左移动,直到找到一个小于基准的元素。
- 交换元素:当左右指针都停止时,交换这两个元素的位置。
- 重复步骤3和4:继续移动指针并交换元素,直到左右指针相遇。
- 放置基准:最后将基准元素与右指针的位置交换,使得基准元素处于正确的位置。
2.3 Java 实现示例
public static void quickSort(int[] arr) {quickSort(arr, 0, arr.length - 1);
}private static void quickSort(int[] arr, int low, int high) {if(low >= high) {return;}int pivotIndex = hoare(arr, low, high);quickSort(arr, low, pivotIndex - 1);quickSort(arr, pivotIndex + 1, high);
}private static int hoare(int[] arr, int low, int high) {int pivot = arr[low]; // 选择一个元素作为基准int l = low; // 左索引int r = high; // 右索引while(l < r) {while(l < r && arr[r] > pivot) {r --;}while(l < r && arr[l] < pivot) {l ++;}if(l < r) {swap(arr, l, r);System.out.println(l + "|" + r + "\t\t" + Arrays.toString(arr));}}return l;
}/*** 交换数组索引为i和j的两个元素* @param arr* @param i* @param j*/
private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
3. 三数取中法
3.1 基本原理
为了减少快速排序在最坏情况下的时间复杂度(即 O(n^2)),可以使用“三数取中法”来选择基准元素。这种方法通过选择数组的第一个、中间和最后一个元素中的中位数作为基准,从而减少最坏情况的发生概率。
3.2 步骤
- 选择基准:选择数组的第一个、中间和最后一个元素中的中位数作为基准。
- 分区操作:根据选择的基准进行分区操作,可以使用 Lomuto 或 Hoare 分区方案。
- 递归排序:对基准两侧的子数组分别递归执行上述过程,直到每个子数组只剩下一个元素或为空。
3.3 Java 实现示例
public static void quickSort(int[] arr) {quickSort(arr, 0, arr.length - 1);
}private static void quickSort(int[] arr, int low, int high) {if (low >= high) {return;}int pivotIndex = lomuto(arr, low, high);quickSort(arr, low, pivotIndex - 1);quickSort(arr, pivotIndex + 1, high);
}private static int lomuto(int[] arr, int low, int high) {int pivotIndex = medianOfThree(arr, low, high); // 使用三数取中法选择基准int pivot = arr[pivotIndex];int i = low - 1; // 指向当前小于基准的最后一个元素if (pivotIndex != high) {swap(arr, pivotIndex, high); // 将基准元素移到最后System.out.println("pivot:" + pivotIndex + "|" + high + "\t" + Arrays.toString(arr));}for (int j = low; j < high; j++) {if (arr[j] <= pivot) {i++;if (i != j) {swap(arr, i, j);System.out.println(low + "|" + high + " \t" + i + "|" + j + "\t" + Arrays.toString(arr));}}}// 将基准元素放回数组正确位置if (i + 1 != high) {swap(arr, i + 1, high);System.out.println("back:" + (i + 1) + "|" + high + "\t" + Arrays.toString(arr));}return i + 1;
}/*** 选择数组的第一个、中间和最后一个元素中的中位数作为基准,返回其下标** @param arr* @param low* @param high* @return*/
private static int medianOfThree(int[] arr, int low, int high) {int mid = (low + high) / 2;if ((arr[low] <= arr[mid] && arr[mid] <= arr[high]) || (arr[low] >= arr[mid] && arr[mid] >= arr[high])) {return mid;}if ((arr[mid] <= arr[low] && arr[low] <= arr[high]) || (arr[mid] >= arr[low] && arr[low] >= arr[high])) {return low;}return high;
}/*** 交换数组索引为i和j的两个元素** @param arr* @param i* @param j*/
private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
4. 尾递归优化
4.1 基本原理
快速排序的递归实现可能导致栈溢出问题,特别是在处理大规模数据集时。为了避免这种情况,可以使用尾递归优化技术来减少递归调用栈的深度。
4.2 步骤
- 选择基准:选择数组的某个元素作为基准。
- 分区操作:根据选择的基准进行分区操作,可以使用 Lomuto 或 Hoare 分区方案。
- 尾递归优化:每次递归只对较小的子数组进行递归调用,较大的子数组则通过循环继续处理,从而减少递归调用栈的深度。
4.3 Java 实现示例
public static void quickSort(int[] arr) {quickSort(arr, 0, arr.length - 1);
}private static void quickSort(int[] arr, int low, int high) {// 使用循环代替递归while (low < high) {int pivotIndex = hoare(arr, low, high);// 对较小的分区进行递归调用if (pivotIndex - low < high - pivotIndex) {quickSort(arr, low, pivotIndex - 1);low = pivotIndex + 1;} else {quickSort(arr, pivotIndex + 1, high);high = pivotIndex - 1;}}
}private static int hoare(int[] arr, int low, int high) {int pivot = arr[low]; // 选择一个元素作为基准int l = low; // 左索引int r = high; // 右索引while (l < r) {while (l < r && arr[r] > pivot) {r--;}while (l < r && arr[l] < pivot) {l++;}if (l < r) {swap(arr, l, r);System.out.println(l + "|" + r + "\t\t" + Arrays.toString(arr));}}return l;
}/*** 交换数组索引为i和j的两个元素** @param arr* @param i* @param j*/
private static void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}
总结
快速排序有多种实现方式,每种实现方式在不同的应用场景下可能有不同的性能表现:
- Lomuto 分区方案:简单易懂,但相比 Hoare 分区方案,在某些情况下可能会有更多的交换操作,导致效率稍低。
- Hoare 分区方案:通常比 Lomuto 分区更高效,因为它减少了不必要的交换操作,从而减少了时间复杂度。
- 三数取中法:通过选择数组的第一个、中间和最后一个元素中的中位数作为基准,减少最坏情况的发生概率。
- 尾递归优化:通过优化递归调用栈的深度,避免栈溢出问题,特别适合处理大规模数据集。
相关文章:
【算法系列】快速排序详解
文章目录 快速排序的多种实现方式1. 基本快速排序(Lomuto 分区方案)1.1 基本原理1.2 步骤1.3 Java 实现示例 2. Hoare 分区方案2.1 基本原理2.2 步骤2.3 Java 实现示例 3. 三数取中法3.1 基本原理3.2 步骤3.3 Java 实现示例 4. 尾递归优化4.1 基本原理4.…...
神经网络发展简史:从感知机到通用智能的进化之路
引言 神经网络作为人工智能的核心技术,其发展历程堪称一场人类对生物大脑的致敬与超越。本文将用"模型进化"的视角,梳理神经网络发展的五大关键阶段,结合具象化比喻和经典案例,为读者呈现一幅清晰的AI算法发展图谱。 一…...
C语言番外篇(4)--------->goto语句
在C语言中,有一个很特殊的语法,这就是goto语句。goto用于实现同一函数的跳转,goto后面会有一个标志,执行goto语句时,就会跳转到标志的位置。 一、goto语句的语法 (1)goto在前,标志…...
AI 编码 2.0 分析、思考与探索实践:从 Cursor Composer 到 AutoDev Sketch
在周末的公司【AI4SE 效能革命与实践:软件研发的未来已来】直播里,我分享了《AI编码工具 2.0 从 Cursor 到 AutoDev Composer》主题演讲,分享了 AI 编码工具 2.0 的核心、我们的思考、以及我们的 AI 编码工具 2.0 探索实践。 在这篇文章中&am…...
Linux与自动化的基础
Linux简介 Linux是一种开源的类Unix操作系统,广泛应用于服务器、桌面和嵌入式设备。常见的Linux发行版包括 Ubuntu、CentOS 和 Debian,它们各有特色,但都以稳定性和安全性著称。 与图形界面相比,Linux的**命令行界面(…...
安全开发-环境选择
文章目录 个人心得虚拟机选择ubuntu 22.04python环境选择conda下载使用: 个人心得 在做开发时配置一个专门的环境可以使我们在开发中的效率显著提升,可以避免掉很多环境冲突的报错。尤其是python各种版本冲突,还有做渗透工具不要选择windows…...
【算法设计与分析】(一)介绍算法与复杂度分析
【算法设计与分析】(一)介绍算法与复杂度分析 前言一、什么是算法?二、算法的抽象机制三、描述算法四、复杂度分析4.1 时间复杂度4.2 空间复杂度 前言 从搜索引擎的高效检索,到推荐系统的个性化推荐,再到人工智能领域…...
SurfaceFlinger代码笔记
drawLayers是做client合成,合成完以后的buffer会放在RenderSurface里 FrameBufferSurface里的buffer是通过setClientTarget给到HWC的(HWC应该给client合成的buffer留了一个slot) Output.cpp这个文件非常关键,代表着具体一个Display的操作 d…...
2025 PHP授权系统网站源码
2025 PHP授权系统网站源码 安装教程: PHP7.0以上 先上传源码到服务器,然后再配置伪静态, 访问域名根据操作完成安装, 然后配置伪静态规则。 Ngix伪静态规则: location / { if (!-e $request_filename) { rewrite …...
Fisher散度:从信息几何到机器学习的隐藏利器
Fisher散度:从信息几何到机器学习的隐藏利器 在机器学习和统计学中,比较两个概率分布的差异是常见任务,比如评估真实分布与模型预测分布的差距。KL散度(Kullback-Leibler Divergence)可能是大家熟悉的选择,…...
深度学习每周学习总结Y1(Yolov5 调用官方权重进行检测 )
🍨 本文为🔗365天深度学习训练营 中的学习记录博客Y1中的内容 🍖 原作者:K同学啊 | 接辅导、项目定制 ** 注意该训练营出现故意不退押金,恶意揣测偷懒用假的结果冒充真实打卡记录,在提出能够拿到视频录像…...
实体机器人在gazebo中的映射
这一部分目的是将真实的机器人映射到gazebo中,使得gazebo中的其他虚拟机器人能识别到真实世界的wheeltec机器人。 真实机器人的型号的wheeltec旗下的mini_mec。 一、在wheeltec官方百度云文档中找到URDF原始导出功能包.zip 找到对应的包 拷贝到工作空间下 在原有…...
【学习笔记】Kubernetes
一、 概览 Kubernetes 提供了一个抽象层,是用户可以在屋里或虚拟环境中部署容器化应用,提供以容器为中心的基础架构。 Kubernetes的控制平面和工作节点都有什么组建? 分别有什么作用? 1.1 Kubernetes控制平面和工作节点的组件及…...
【网络编程】几个常用命令:ping / netstat / xargs / pidof / watch
ping:检测网络联通 1. ping 的基本功能2. ping 的工作原理3. ping 的常见用法4. ping 的输出解释5. ping 的应用场景6. 注意事项 netstat:查看网络状态 1. netstat 的基本功能2. 常见用法3. 示例4. 输出字段解释5. netstat 的替代工具6. 注意事项 xargs&…...
上海创智学院(测试)算法笔试(ACM赛制)部分例题
1.第一个题,大概题目意思是求n句话中最长的单词和最短的单词 这个题目做的有点磕巴,好几年没有写过c/c了,连string的复制都不会写了,哈哈哈,太笨了 后面一点点捡起来,还是写出来了,本身没啥&…...
【学术投稿-第四届材料工程与应用力学国际学术会议(ICMEAAE 2025】材料工程与应用力学的探讨
重要信息 官网:www.icmeaae.com 时间:2025年3月7-9日 地点:中国西安 简介 第四届材料工程与应用力学(ICMEAAE 2025)将于2025年3月7日至9日在中国西安召开。本次会议将重点讨论材料科学、应用力学等领域的最新研究进…...
2025吐槽季第一弹---腾讯云EO边缘安全加速平台服务
前言: 关于EO边缘安全加速平台服务 参照:产品概述,具体如下: 边缘安全加速平台 EO(Tencent Cloud EdgeOne,下文简称为 EdgeOne)是国内首款基于全新架构的真正一体化的边缘安全加速平台。提供全面的安全防…...
力扣-动态规划-70 爬楼梯
思路 dp数组定义:爬到第i个台阶有多少种爬法递推公式: 当前台阶可能是从前一个或者前两个来的dp数组初始化:遍历顺序:顺序遍历时间复杂度: 代码 class Solution { public:int climbStairs(int n) {if(n 1) ret…...
【DeepSeek】-macOS本地终端部署后运行DeepSeek如何分析图片
【DeepSeek】-macOS本地终端部署后运行DeepSeek如何分析图片 根据您的需求,目前需要了解以下几个关键点及分步解决方案: --- 一、现状分析 1. Ollama 的限制: - 目前Ollama主要面向文本大模型,原生不支持直接上传/处理图片 …...
使用 pytest-mock 进行 Python 高级单元测试与模拟
一、单元测试与模拟的意义 在软件开发中,单元测试用于验证代码逻辑的正确性。但实际项目中,代码常依赖外部服务(如数据库、API、文件系统)。直接测试这些依赖会导致: 测试速度变慢测试结果不可控产生副作用(如真实发送邮件)模拟(Mocking) 技术通过创建虚拟对象替代真…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
9-Oracle 23 ai Vector Search 特性 知识准备
很多小伙伴是不是参加了 免费认证课程(限时至2025/5/15) Oracle AI Vector Search 1Z0-184-25考试,都顺利拿到certified了没。 各行各业的AI 大模型的到来,传统的数据库中的SQL还能不能打,结构化和非结构的话数据如何和…...
Python竞赛环境搭建全攻略
Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...
API网关Kong的鉴权与限流:高并发场景下的核心实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中,API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关,Kong凭借其插件化架构…...
