【排序算法】快速排序详解--附详细流程代码

快速排序算法

介绍

快速排序(Quick Sort)是一种高效的分治排序算法,由英国计算机科学家 Tony Hoare 于 1960 年提出。它是实际应用中最常用的排序算法之一。快速排序的基本思想是:选择一个"基准"(pivot)元素,通过一次排序将待排序列分割成独立的两部分,一部分所有元素均小于基准,另一部分所有元素均大于基准,然后递归地对这两部分分别进行快速排序。分治策略的运用让快速排序在平均情况下能达到 O(nlogn) 的时间复杂度,大大优于简单排序算法的 O(n²) 性能。

算法步骤

  1. 从数列中选择一个元素作为"基准"(pivot),本文采用最左侧元素作为基准
  2. 将所有比基准值小的元素放到基准前面,所有比基准值大的元素放到基准后面(分区操作)
  3. 对基准左右两个子序列分别重复步骤1和2,直到子序列只有一个元素或为空

核心特性

  • 分治策略:将问题分解为更小的子问题,逐步解决
  • 原地排序:只需要 O(logn) 的额外空间复杂度(主要用于递归调用的栈空间)
  • 时间复杂度:平均情况为 O(nlogn),最坏情况为 O(n²),最好情况为 O(nlogn)
  • 不稳定性:相等元素的相对位置在排序后可能会改变
  • 高效性:在实际应用中,快速排序通常是最快的排序算法之一

基础实现

接下来大家一起看下快速排序的部分主流语言实现:

代码实现

Java实现

public class QuickSort {public static void quickSort(int[] arr, int low, int high) {if (low < high) {int pivotIndex = partition(arr, low, high);quickSort(arr, low, pivotIndex - 1);quickSort(arr, pivotIndex + 1, high);}}private static int partition(int[] arr, int low, int high) {int pivot = arr[low];int i = low + 1;for (int j = low + 1; j <= high; j++) {if (arr[j] < pivot) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;i++;}}int temp = arr[low];arr[low] = arr[i - 1];arr[i - 1] = temp;return i - 1;}public static void printArray(int[] arr) {for (int i : arr) {System.out.print(i + " ");}System.out.println();}public static void main(String[] args) {int[] arr = {10, 7, 8, 9, 1, 5};System.out.println("排序前的数组:");printArray(arr);quickSort(arr, 0, arr.length - 1);System.out.println("排序后的数组:");printArray(arr);}
}

注意,在上述代码里,通过:

for (int j = low + 1; j <= high; j++) {if (arr[j] < pivot) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;i++;}
}

将小于基准的元素移到左侧,然后通过:

int temp = arr[low];
arr[low] = arr[i - 1];
arr[i - 1] = temp;

修正基准的位置,实现基准左侧都小于基准、基准右侧大于等于基准。

优化策略

随机选择基准元素

使用最左侧元素作为基准可能会在数组已经排序或接近排序时导致最坏性能,随机选择基准可以降低这种风险:

private static int randomPartition(int[] arr, int low, int high) {int randomIndex = low + (int)(Math.random() * (high - low + 1));int temp = arr[randomIndex];arr[randomIndex] = arr[low];arr[low] = temp;return partition(arr, low, high);
}

三数取中法

选择左端、中间和右端三个元素的中值作为基准,可以进一步优化快速排序:

private static int medianOfThreePartition(int[] arr, int low, int high) {int mid = low + (high - low) / 2;if (arr[mid] < arr[low])swap(arr, mid, low);if (arr[high] < arr[low])swap(arr, high, low);if (arr[high] < arr[mid])swap(arr, high, mid);swap(arr, mid, low);return partition(arr, low, high);
}

优缺点

优点

  • 平均情况下非常高效,时间复杂度为 O(nlogn)
  • 原地排序,空间复杂度低
  • 缓存友好,数据局部性好
  • 适合处理大规模数据
  • 在许多实际应用中表现优秀

缺点

  • 最坏情况下性能退化至 O(n²),比如当数组已经排序时
  • 不稳定的排序算法
  • 对于小数组,快速排序可能比其他基础排序慢
  • 递归实现可能导致栈溢出(可以使用迭代方式解决)

应用场景

  • 需要高效排序大量数据的情况
  • 作为系统库中的排序函数(如 C++ 的 std::sort、Java 的 Arrays.sort)
  • 需要就地排序且对空间复杂度敏感的场景
  • 需要平均情况下高性能的应用

扩展

双轴快速排序

Java 的 Arrays.sort() 使用的是双轴快速排序,它使用两个枢轴(基准),可以进一步提高性能:

public static void dualPivotQuickSort(int[] arr, int low, int high) {if (high <= low) return;if (arr[low] > arr[high]) {int temp = arr[low];arr[low] = arr[high];arr[high] = temp;}int pivot1 = arr[low];int pivot2 = arr[high];int lt = low + 1;    int gt = high - 1;   int i = low + 1;     while (i <= gt) {if (arr[i] < pivot1) {int temp = arr[lt];arr[lt] = arr[i];arr[i] = temp;lt++;i++;} else if (arr[i] > pivot2) {int temp = arr[i];arr[i] = arr[gt];arr[gt] = temp;gt--;} else {i++;}}arr[low] = arr[lt - 1];arr[lt - 1] = pivot1;arr[high] = arr[gt + 1];arr[gt + 1] = pivot2;dualPivotQuickSort(arr, low, lt - 2);dualPivotQuickSort(arr, lt, gt);dualPivotQuickSort(arr, gt + 2, high);
}

测验

这里准备了一些测试题,方便大家判断自己的掌握情况:

  1. 快速排序的平均时间复杂度是多少?
  2. 快速排序是稳定的排序算法吗?
  3. 使用最左侧元素作为基准的快速排序在什么情况下会出现最坏性能?
  4. 快速排序的空间复杂度是多少?为什么?

测验答案

  1. O(nlogn)。
  2. 不是,因为基准元素的移动可能会改变相等元素的相对位置。
  3. 当数组已经排序或接近排序时,每次分区只能减少一个元素,时间复杂度退化为O(n²)。
  4. O(logn),主要是递归调用占用的栈空间,最坏情况下为O(n)。

相关的 LeetCode 热门题目

给大家推荐一些可以用来练手的 LeetCode 题目:

  • 215. 数组中的第K个最大元素 - 可以使用快速选择算法(快速排序的变体)求解,练习分区思想
  • 912. 排序数组
  • 75. 颜色分类 - 一个经典的荷兰国旗问题,可以使用类似快速排序的分区思想解决

来自: 快速排序 - 算法导航

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82657.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【监控】Prometheus中的告警机制介绍

prometheus实战之三&#xff1a;告警规则_验证prometheus告警规则-CSDN博客 Prometheus是一款开源的系统监控和告警工具&#xff0c;其告警功能是保障系统稳定运行的重要部分。以下将从告警的整体架构、核心概念、规则配置以及具体的通知流程等方面对Prometheus中的告警进行介…

53、用例(Use Case)详解

1. 定义与核心概念 用例&#xff08;Use Case&#xff09; 是软件工程中用于描述系统功能需求的核心工具&#xff0c;它通过结构化的方式定义系统与外部参与者&#xff08;用户、其他系统&#xff09;之间的交互行为&#xff0c;以实现具体的业务目标。用例强调从用户视角出发…

对比Redis与向量数据库(如Milvus)在AI中的应用

对比Redis与向量数据库&#xff08;如Milvus&#xff09;在AI中的应用 在AI架构中&#xff0c;缓存系统的设计直接影响响应速度、资源成本以及推理路径是否高效。而面对不同的AI业务诉求&#xff0c;选用什么类型的缓存系统、如何搭配&#xff0c;往往是系统架构设计中必须深入…

Oracle 的 MOVE 操作是否重建表?

Oracle 的 MOVE 操作是否重建表&#xff1f; Oracle 的 ALTER TABLE ... MOVE 操作实质上是重建表的物理存储结构&#xff0c;但保留表的逻辑定义不变。 MOVE 操作的本质 物理重建&#xff1a; 创建新的数据段&#xff08;物理存储结构&#xff09;将原表数据按顺序重新插入到…

数据库中表的设计规范

表的结构 列&#xff1a;由多个字段构成&#xff0c;每个字段存储单一数据项&#xff0c;列的先后顺序对表没有影响 行&#xff1a;记录&#xff0c;一个表中不能存在完全相同的两行&#xff0c;行的顺序对表没有影响 主键&#xff1a;primary key 表中的一列或多列组合起来…

[学习]C语言指针函数与函数指针详解(代码示例)

C语言指针函数与函数指针详解 文章目录 C语言指针函数与函数指针详解一、引言二、指针函数&#xff08;函数返回指针&#xff09;定义与语法典型应用场景注意事项 三、函数指针&#xff08;指向函数的指针&#xff09;定义与声明初始化与调用赋值方式调用语法 高级应用回调函数…

Python 实现桶排序详解

1. 核心原理 桶排序是一种非比较型排序算法&#xff0c;通过将数据分配到多个“桶”中&#xff0c;每个桶单独排序后再合并。其核心步骤包括&#xff1a; 分桶&#xff1a;根据元素的范围或分布&#xff0c;将数据分配到有限数量的桶中。桶内排序&#xff1a;对每个非空桶内的…

brep2seq 论文笔记

Brep2Seq: a dataset and hierarchical deep learning network for reconstruction and generation of computer-aided design models | Journal of Computational Design and Engineering | Oxford Academic 这段文本描述了一个多头自注意力机制&#xff08;MultiHead Attenti…

在 LangGraph 中集成 Mem0 记忆系统教程

简介 LangGraph 是一个强大的对话流程编排框架&#xff0c;而 Mem0 则是一个高效的记忆系统。本教程将介绍如何将两者结合&#xff0c;创建一个具有记忆能力的客服助手系统。 环境准备 首先安装必要的依赖&#xff1a; pip install langgraph mem0 langchain openai基础配置…

ceph 报错 full ratio(s) out of order

full ratio(s) out of order你遇到的错误信息: full ratio(s) out of order说明你设置的 OSD 空间使用阈值之间的数值顺序不正确,即: nearfull_ratio ≤ backfillfull_ratio ≤ full_ratio ≤ osd_failsafe_full_ratio如果它们的关系不满足这个顺序,Ceph 就会报这个错误。…

NB-IoT NPUSCH(三)-资源映射

资源映射单独做一章节&#xff0c;是因为NPUSCH的资源映射比较复杂。与LTE不同&#xff0c;为了提高数据传输的质量&#xff0c;NB-IoT的数据会有重复传输。NPUSCH一开始生成的TBS只与子载波个数、RU个数有关&#xff0c;与重复次数没有关系。初始产生的数据为 个时隙&#xff…

华为OD机试真题——荒岛求生(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 B卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

centos7安装MySQL(保姆级教学)

在 Linux 系统的软件管理中&#xff0c;YUM&#xff08;Yellowdog Updater, Modified&#xff09;包管理器是不可或缺的工具&#xff0c;而 YUM 源的选择与配置直接影响着软件安装与更新的效率。本文将深入解析网络 YUM 源的分类&#xff0c;详细介绍如何使用知名平台提供的 YU…

DeepSeek 赋能教育游戏化:AI 重构学习体验的技术密码

目录 一、引言&#xff1a;教育游戏化与 DeepSeek 的相遇二、DeepSeek 技术剖析2.1 核心架构2.2 关键技术 三、教育游戏化设计的奥秘3.1 概念与意义3.2 常见方法与元素3.3 成功案例借鉴 四、DeepSeek 在教育游戏化设计中的多面应用4.1 个性化学习路径打造4.2 智能教学辅助工具4…

WPF命令与MVVM模式:打造优雅的应用程序架构

🎮 打造优雅的应用程序架构 1. 🧩 命令系统基础1.1 🤔 为什么需要命令?1.2 🏗️ ICommand接口1.3 🛠️ 实现基本命令2. 🏛️ MVVM模式详解2.1 🧱 MVVM三大组件2.2 🏗️ 创建ViewModel基类2.3 🎯 典型ViewModel示例3. 🧩 命令绑定实战3.1 🎨 View中的命令…

真实案例拆解:智能AI客服系统中的两类缓存协同

真实案例拆解:智能客服系统中的两类缓存协同 在AI客服系统中,“响应速度”与“语义准确性”是一对天然的矛盾体。为了实现秒级应答与智能理解的双重目标,系统需要在技术架构中融合精确命中的缓存系统(如Redis)与模糊语义识别的向量数据库(如Milvus)。这两种能力的结合,…

FastAPI与MongoDB分片集群:异步数据路由与聚合优化

title: FastAPI与MongoDB分片集群:异步数据路由与聚合优化 date: 2025/05/26 16:04:31 updated: 2025/05/26 16:04:31 author: cmdragon excerpt: FastAPI与MongoDB分片集群集成实战探讨了分片集群的核心概念、Motor驱动配置技巧、分片数据路由策略、聚合管道高级应用、分片…

一起学数据结构和算法(三)| 字符串(线性结构)

字符串&#xff08;String&#xff09; 字符串是由字符组成的有限序列&#xff0c;在计算机中通常以字符数组形式存储&#xff0c;支持拼接、查找、替换等操作。 简介 字符串是计算机科学中最常用的数据类型之一&#xff0c;由一系列字符组成的有限序列。在大多数编程语言中&…

2025电工杯数学建模竞赛A题 光伏电站发电功率日前预测问题 保姆级教程讲解|模型讲解

完整内容请看文章最下面的推广群 2025电工杯数学建模竞赛 A题保姆级分析完整思路代码数据教学 2025电工杯 A题保姆级教程思路分析 DS数模-全国大学生电工数学建模&#xff08;电工杯&#xff09; A题保姆级教程思路分析 A题&#xff1a;光伏电站发电功率日前预测问题 下面我…

React Native 拼音及拼音首字母搜索组件开发

写在前面 “用户说找不到联系人&#xff1f;拼音搜索功能必须安排上&#xff01;” —— 当产品经理第N次提出这个需求时&#xff0c;我意识到需要开发一个强大的拼音搜索组件。本文将详细介绍如何开发一个支持拼音匹配、首字母搜索的React Native搜索组件&#xff0c;让你的应…