当前位置: 首页 > article >正文

【算法系列】快速排序详解

文章目录

  • 快速排序的多种实现方式
    • 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 步骤

  1. 选择基准:通常选择数组的最后一个元素作为基准。
  2. 初始化指针:设置一个指针 i,用于追踪当前小于基准的最后一个元素的位置。
  3. 遍历数组
    • 遍历数组中的每个元素(除了基准元素),如果当前元素小于等于基准,则将该元素与 i 指针所指向的元素交换,并将 i 向右移动一位。
  4. 放置基准:最后将基准元素与 i + 1 位置的元素交换,使得基准元素处于正确的位置。
  5. 递归排序:对基准两侧的子数组分别递归执行上述过程,直到每个子数组只剩下一个元素或为空。

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 步骤

  1. 选择基准:通常选择数组的第一个元素作为基准。
  2. 初始化双指针:设置两个指针,分别指向数组的起始位置和结束位置。
  3. 移动指针:
    • 左指针向右移动,直到找到一个大于基准的元素。
    • 右指针向左移动,直到找到一个小于基准的元素。
  4. 交换元素:当左右指针都停止时,交换这两个元素的位置。
  5. 重复步骤3和4:继续移动指针并交换元素,直到左右指针相遇。
  6. 放置基准:最后将基准元素与右指针的位置交换,使得基准元素处于正确的位置。

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 步骤

  1. 选择基准:选择数组的第一个、中间和最后一个元素中的中位数作为基准。
  2. 分区操作:根据选择的基准进行分区操作,可以使用 Lomuto 或 Hoare 分区方案。
  3. 递归排序:对基准两侧的子数组分别递归执行上述过程,直到每个子数组只剩下一个元素或为空。

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 步骤

  1. 选择基准:选择数组的某个元素作为基准。
  2. 分区操作:根据选择的基准进行分区操作,可以使用 Lomuto 或 Hoare 分区方案。
  3. 尾递归优化:每次递归只对较小的子数组进行递归调用,较大的子数组则通过循环继续处理,从而减少递归调用栈的深度。

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. 基本快速排序&#xff08;Lomuto 分区方案&#xff09;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.…...

电脑键盘知识

1、键盘四大功能区 1. 功能区 2. 主要信息输入区 3. 编辑区 4. 数字键盘区 笔记本电脑键盘的功能区&#xff0c;使用前需先按Fn键 1.1、功能区 ESC&#xff1a;退出 F1&#xff1a;显示帮助信息 F2&#xff1a;重命名 F4&#xff1a;重复上一步操作 F5&#xff1a;刷新网页 …...

Grok 3 vs. DeepSeek vs. ChatGPT:2025终极AI对决

2025 年,AI 领域的竞争愈发激烈,三个重量级选手争夺霸主地位:Grok 3(由 xAI 开发)、DeepSeek(国内 AI 初创公司)和 ChatGPT(OpenAI 产品)。每个模型都有自己独特的优势,无论是在深度思考、速度、编程辅助、创意输出,还是在成本控制方面,都展现出强大的实力。但究竟…...

【MySQL篇】数据库基础

目录 1&#xff0c;什么是数据库&#xff1f; 2&#xff0c;主流数据库 3&#xff0c;MySQL介绍 1&#xff0c;MySQL架构 2&#xff0c;SQL分类 3&#xff0c;MySQL存储引擎 1&#xff0c;什么是数据库&#xff1f; 数据库&#xff08;Database&#xff0c;简称DB&#xf…...

vscode java环境中文乱码的问题

先说我的结论&#xff1a; 由于我的系统是windows的&#xff0c;所以vscode使用的是默认gbk的编码进行的。 但是我的目的是全部都使用utf-8&#xff0c;因为我的程序始终是要去linux上去运行的&#xff0c;总不能在本地是好的&#xff0c;然后到服务器上就不行了吧&#xff0c;…...

基于SpringBoot+mybatisplus+vueJS的Cosplay文化展示与交流社区设计与实现

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…...

组件传递props校验

注意&#xff1a;prop是只读的&#xff01;不可以修改父组件的数据。 可以检验传过来的内容是否类型没问题。 App.vue <template><div><!-- <parentDemo/> --><componentA/></div></template> <script> import ComponentA …...

数据结构与算法-图论-最短路-拓展运用

选择最佳路线 分析&#xff1a; 这是一道图论中的最短路径问题&#xff0c;目标是在给定的公交网络中&#xff0c;找到从琪琪家附近的车站出发&#xff0c;到她朋友家附近车站&#xff08;编号为 s &#xff09;的最短时间。以下是对该问题的详细分析&#xff1a; 问题关键信息…...

0—QT ui界面一览

2025.2.26&#xff0c;感谢gpt4 1.控件盒子 1. Layouts&#xff08;布局&#xff09; 布局控件用于组织界面上的控件&#xff0c;确保它们的位置和排列方式合理。 Vertical Layout&#xff08;垂直布局&#xff09; &#xff1a;将控件按垂直方向排列。 建议&#xff1a;适…...

纷析云:赋能企业财务数字化转型的开源解决方案

在企业数字化转型的浪潮中&#xff0c;财务管理的高效与安全成为关键。纷析云凭借其开源、安全、灵活的财务软件解决方案&#xff0c;为企业提供了一条理想的转型路径。 一、开源的力量&#xff1a;自主、安全、高效 纷析云的核心优势在于其100%开源的财务软件源码。这意味着…...

P8716 [蓝桥杯 2020 省 AB2] 回文日期

1 题目说明 2 题目分析 暴力不会超时&#xff0c;O(n)的时间复杂度&#xff0c; < 1 0 8 <10^8 <108。分析见代码&#xff1a; #include<iostream> #include<string> using namespace std;int m[13]{0,31,28,31,30,31,30,31,31,30,31,30,31};// 判断日期…...

(十)趣学设计模式 之 外观模式!

目录 一、 啥是外观模式&#xff1f;二、 为什么要用外观模式&#xff1f;三、 外观模式的实现方式四、 外观模式的优缺点五、 外观模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;可以多多支…...

apache-maven-3.2.1

MAVEN_HOME D:\apache-maven-3.2.1 PATH D:\apache-maven-3.2.1\bin cmd mvn -v <localRepository>d:\localRepository</localRepository> setting.xml <?xml version"1.0" encoding"UTF-8"?><!-- Licensed to the Apache Soft…...

编程题-连接两字母单词得到的最长回文串(中等)

题目&#xff1a; 给你一个字符串数组 words 。words 中每个元素都是一个包含 两个 小写英文字母的单词。 请你从 words 中选择一些元素并按 任意顺序 连接它们&#xff0c;并得到一个 尽可能长的回文串 。每个元素 至多 只能使用一次。 请你返回你能得到的最长回文串的 长度…...

react 新手入门指南,常用命令

React 是一个用于构建用户界面的 JavaScript 库,它通过组件化的方式构建应用程序的 UI,适用于构建单页应用(SPA)。以下是一个详细的 React 新手入门指南,包括常用命令和基本概念。 1. 环境准备 在开始之前,确保你已经安装了 Node.js 和 npm。可以通过以下命令检查版本:…...

论文笔记(七十二)Reward Centering(三)

Reward Centering&#xff08;三&#xff09; 文章概括摘要3 基于值的奖励中心化4 案例研究&#xff1a; 以奖励为中心的 Q-learning5 讨论、局限性与未来工作致谢 文章概括 引用&#xff1a; article{naik2024reward,title{Reward Centering},author{Naik, Abhishek and Wan…...

【论文笔记-ECCV 2024】AnyControl:使用文本到图像生成的多功能控件创建您的艺术作品

AnyControl&#xff1a;使用文本到图像生成的多功能控件创建您的艺术作品 图1 AnyControl的多控制图像合成。该研究的模型支持多个控制信号的自由组合&#xff0c;并生成与每个输入对齐的和谐结果。输入到模型中的输入控制信号以组合图像显示&#xff0c;以实现更好的可视化。 …...

Vue3核心编译库@vuecompiler-core内容分享

vue/compiler-core 是 Vue 3 中的一个核心编译库&#xff0c;主要用于编译 Vue 的模板。它为 Vue 3 提供了处理模板编译的功能&#xff0c;包含了将模板转换为抽象语法树&#xff08;AST&#xff09;、生成渲染函数以及与响应式系统进行集成等功能。 vue/compiler-core 的主要…...

Redisson使用场景及原理

目录 一、前言 二、安装Redis 1、Windows安装Redis ​2、启动方式 3、设置密码 三、项目集成Redission客户端 1、引入依赖 四、实用场景 1、操作缓存 2、分布式锁 3、限流 3.1 创建限流器 3.2 设置限流参数 3.3 获取令牌 3.4 带超时时间获取令牌 3.5 总结 一、…...

【二分查找 图论】P8794 [蓝桥杯 2022 国 A] 环境治理|普及

本文涉及的基础知识点 本博文代码打包下载 C二分查找 C图论 [蓝桥杯 2022 国 A] 环境治理 题目描述 LQ 国拥有 n n n 个城市&#xff0c;从 0 0 0 到 n − 1 n - 1 n−1 编号&#xff0c;这 n n n 个城市两两之间都有且仅有一条双向道路连接&#xff0c;这意味着任意两…...

c/c++蓝桥杯经典编程题100道(22)最短路径问题

最短路径问题 ->返回c/c蓝桥杯经典编程题100道-目录 目录 最短路径问题 一、题型解释 二、例题问题描述 三、C语言实现 解法1&#xff1a;Dijkstra算法&#xff08;正权图&#xff0c;难度★★&#xff09; 解法2&#xff1a;Bellman-Ford算法&#xff08;含负权边&a…...

25中医研究生复试面试问题汇总 中医专业知识问题很全! 中医试全流程攻略 中医考研复试调剂真题汇总

各位备考中医研究生的小伙伴们&#xff0c;一想到复试&#xff0c;是不是立刻紧张到不行&#xff0c;担心老师会抛出一大堆刁钻的问题&#xff1f;别怕&#xff01;其实中医复试也是有套路可循的&#xff0c;只要看完这篇攻略&#xff0c;你就会发现复试并没有想象中那么难&…...

stm32hal库寻迹+蓝牙智能车(STM32F103C8T6)

简介: 这个小车的芯片是STM32F103C8T6&#xff0c;其他的芯片也可以照猫画虎,基本配置差不多,要注意的就是,管脚复用,管脚的特殊功能,(这点不用担心,hal库每个管脚的功能都会给你罗列,很方便的.)由于我做的比较简单,只是用到了几个简单外设.主要是由带霍尔编码器电机的车模,电机…...

centos22.04 dpkg -l 输出状态标识含义

dpkg -l 输出状态标识含义 dpkg -l 命令用于列出系统中已安装的软件包,每行输出的前两个字符是软件包状态的标识,不同的组合代表不同的状态,具体含义如下: 第一个字符:表示期望的状态(Desired state) u:未知(Unknown)i:安装(Install)r:移除(Remove)p:清除(Pu…...

前端TypeScript 面试题及参考答案

目录 解释 unknown 与 any 的区别,如何安全使用 unknown 类型? 如何用类型守卫处理联合类型变量的方法调用? 实现一个工具类型 Nullable ,使 T 可被赋值为 null/undefined 如何用 keyof 和 in 关键字实现枚举类型到联合类型的转换? 类型断言 as 与尖括号语法有何差异…...

基于 Vue.js 和 Element UI 实现九宫格按钮拖拽排序功能 | 详细教程与代码实现

在Vue.js项目中使用vue-element-template&#xff08;基于Element UI&#xff09;实现按钮的九宫格拖拽排序功能&#xff0c;可以通过以下步骤实现。我们将使用vuedraggable库来实现拖拽排序功能。 1. 安装依赖 首先&#xff0c;确保你已经安装了vuedraggable库&#xff1a; …...

Spring Framework测试工具MockMvc介绍

目录 一、基本概念 二、主要特点 三、使用场景 四、工作原理 五、示例代码 接口创建 测试类创建 六、注解解释 AutoConfigureMockMvc WebMvcTest 一、基本概念 MockMvc实现了对Http请求的模拟&#xff0c;能够直接使用网络的形式&#xff0c;转换到Controller的调用…...

nginx 正向代理与反向代理

1. 正向代理&#xff08;Forward Proxy&#xff09; 正向代理是指 代理客户端 访问目标服务器&#xff0c;通常用于访问受限资源或隐藏客户端 IP。 工作原理 客户端请求代理服务器&#xff08;如 nginx&#xff09;。代理服务器代表客户端向目标网站发起请求。目标网站返回内…...

VUE 获取视频时长,无需修改数据库,前提当前查看视频可以得到时长

第一字段处 <el-table-column label"视频时长" align"center"> <template slot-scope"scope"> <span>{{ formatDuration(scope.row.duration) }}</span> </template> </el-ta…...

使用Jenkins实现Windows服务器下C#应用程序发布

背景 在现代化的软件开发流程中&#xff0c;持续集成和持续部署&#xff08;CI/CD&#xff09;已经成为不可或缺的一部分。 Jenkins作为一款开源的自动化运维工具&#xff0c;能够帮助我们实现这一目标。 本文将详细介绍如何在Windows服务器下使用Jenkins来自动化发布C#应用…...