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

深入解析三路快排:一种高效的排序算法

在数据结构和算法的世界中,快排(Quick Sort)无疑是最受欢迎的排序算法之一。今天,探讨一种优化的快排变体——三路快排(3-Way Quick Sort),它在处理具有重复元素的数组时展现出了令人惊叹的效率。

1. 快排的基本概念

在深入三路快排之前,我们首先复习一下经典的快排算法。快排由C.A.R. Hoare于1960年提出,它采用分治策略,通过选择一个基准元素(pivot),将数组分成两部分:一部分小于基准,另一部分大于基准,然后递归地对这两部分进行排序。

快排的基本步骤如下:

  • 选择基准元素:通常选择数组的第一个、最后一个或中间的元素作为基准。
  • 分区操作:将数组分成两部分,一部分包含所有小于基准的元素,另一部分包含所有大于基准的元素。
  • 递归排序:对分出的两部分分别递归地应用快排。

2. 三路快排的动机与改进

在经典的快排中,当遇到大量重复元素时,分区操作会变得非常不高效,导致性能下降。三路快排则通过对重复元素进行特殊处理来解决这个问题。

为什么需要三路快排?

在处理大量重复元素的数组时,经典的快排可能会表现得非常低效,因为重复元素会导致很多不必要的比较和交换。三路快排通过将数组分为三个区域来优化这一点:

  • 小于基准元素的区域
  • 等于基准元素的区域
  • 大于基准元素的区域

这种分区方法减少了重复元素的处理次数,提高了效率。如图:
在这里插入图片描述

三路快排的步骤

  1. 选择基准元素:选择一个基准元素(随机选取,并与第一个元素交换)。
  2. 分区操作:使用三个指针(lt, i, gt)来划分数组:
    • lt指向小于基准的区域的边界
    • i是当前元素的索引
    • gt指向大于基准的区域的边界
  3. 交换元素:
    • 如果当前元素小于基准,将其移动到小于区域,更新lt和i。
    • 如果当前元素等于基准,将其保持在中间区域,更新i。
    • 如果当前元素大于基准,将其移动到大于区域,更新gt。

举例说明

原始数组如下:
在这里插入图片描述
步骤1: 假设我的随机取的下标是3,将它与第一个元素交换位置,交换后原数组不变
在这里插入图片描述
步骤二:初始化三个指针
在这里插入图片描述
步骤三:交换元素
2比基准值5小,交换位置swap(i,lt),然后lt,i分别向右移动
在这里插入图片描述
3比基准值5小,交换位置swap(i,lt),然后lt,i分别向右移动
在这里插入图片描述
5和基准值5相等,向右移动i
在这里插入图片描述
在这里插入图片描述
7比基准值5大,交换位置swap(i,gt),gt向左移动
在这里插入图片描述
8比基准值5大,交换位置swap(i,gt),gt向左移动
在这里插入图片描述
至此,我们把比基准值小的数放在索引小于lt的位置,比基准值大的放在索引>gt的位置上,[lt, gt]是等于基准值的
在这里插入图片描述

3. 代码实现:

/*** 三路快速排序* 三路快速排序是快速排序的一种优化,它将数组分为三个部分:* 小于基准值的元素、等于基准值的元素和大于基准值的元素。* 这样可以减少递归深度,提高排序效率。* * @param arr 待排序的数组* @param low 子区间的起始索引* @param high 子区间的结束索引*/
public void threeWayQuickSort(int[] arr, int low, int high) {// 当起始索引大于等于结束索引时,无需排序,直接返回if (low >= high) {return;}// 随机选择一个索引作为基准值的索引int v = (int)(Math.random() * (high - low + 1) + low);// 将基准值交换到区间起始位置swap(arr, low, v);// 获取基准值int pivot = arr[low];// 初始化小于基准值的边界int lt = low;// 初始化当前比较位置int i = low + 1;// 初始化大于基准值的边界int gt = high;// 遍历数组,直到当前比较位置与大于基准值的边界相遇while (i <= gt) {if (arr[i] < pivot) {// 当前元素小于基准值,将其与小于边界后的元素交换,并移动小于边界和当前比较位置swap(arr, lt, i);lt++;i++;} else if (arr[i] > pivot) {// 当前元素大于基准值,将其与大于边界的元素交换,并移动大于边界swap(arr, i, gt);gt--;} else {// 当前元素等于基准值,直接移动到下一个位置i++;}}// 递归地对小于基准值的部分和大于基准值的部分进行排序threeWayQuickSort(arr, low, lt - 1);threeWayQuickSort(arr, gt + 1, high);
}

4. 性能分析

时间复杂度

在最坏情况下(例如所有元素相等),三路快排的时间复杂度仍然是O(n log n),与经典的快排相同。但在处理重复元素时,它通常比经典快排更具优势。

空间复杂度

三路快排的空间复杂度为O(log n),因为它使用了递归栈来进行排序。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 数据结构+二叉排序树+哈希表
  • 【设计模式】组合模式
  • 从快到慢学习Git指令
  • 如何编写一个CMakeLists.txt文件(由简到难,较详细)
  • RS®ZN-Z8x 开关矩阵
  • 映客基于Apache SeaTunnel 打造高效的一站式数据集成平台
  • 自然语言处理顶会​​​​ACL 2024录用阿里云38篇论文,通义团队披露多项大模型前沿技术
  • html+css 实现hover 3D按钮特效
  • 王道数据结构 | 第五章 树与二叉树【未完成】
  • ubuntu 20.04 右键新建空白文档;输入即定位文件或文件夹,而非出现搜索框
  • 0813,引用,函数重载,内存布局叭叭叭
  • C++的内存管理是怎样的?
  • 最小二乘法求拟合曲线(中线)的斜率和截距:数据背后的温柔对话
  • Python实例化指南之对象创建与初始化的实用技巧详解
  • 前端踩坑DOMException: Failed to execute ‘querySelector‘ on ‘Document‘: ‘#091.....‘
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • 【node学习】协程
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • AngularJS指令开发(1)——参数详解
  • Bytom交易说明(账户管理模式)
  • go append函数以及写入
  • If…else
  • Java 最常见的 200+ 面试题:面试必备
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • vue从创建到完整的饿了么(11)组件的使用(svg图标及watch的简单使用)
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 从零搭建Koa2 Server
  • 基于webpack 的 vue 多页架构
  • 简单数学运算程序(不定期更新)
  • 排序算法之--选择排序
  • 前端技术周刊 2019-02-11 Serverless
  • 微信小程序--------语音识别(前端自己也能玩)
  • 1.Ext JS 建立web开发工程
  • Semaphore
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 昨天1024程序员节,我故意写了个死循环~
  • # C++之functional库用法整理
  • # 数论-逆元
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • ###51单片机学习(1)-----单片机烧录软件的使用,以及如何建立一个工程项目
  • (1)(1.13) SiK无线电高级配置(五)
  • (10)ATF MMU转换表
  • (C语言)求出1,2,5三个数不同个数组合为100的组合个数
  • (补)B+树一些思想
  • (附源码)ssm捐赠救助系统 毕业设计 060945
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (一)UDP基本编程步骤
  • (转)JAVA中的堆栈
  • (转)VC++中ondraw在什么时候调用的
  • .htaccess 强制https 单独排除某个目录
  • .NET COER+CONSUL微服务项目在CENTOS环境下的部署实践
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)