数据排序的艺术:快速排序及其优化之旅

快速排序:从基础到优化

快速排序是计算机科学中最著名的排序算法之一,由C. A. R. Hoare在1960年提出。它是一种分治法策略的应用,广泛用于各种场景,从数据库的查询优化到大数据处理,再到我们日常使用的文件排序。

快速排序的基本原理

快速排序的核心思想是选择一个元素作为基准(pivot),然后将数组分为两部分:一部分包含小于pivot的元素,另一部分包含大于pivot的元素。这个过程称为分区(partitioning)。接着,递归地对这两部分进行快速排序,直到整个数组有序。

时间复杂度和空间复杂度

快速排序的平均时间复杂度为O(n log n),在大多数情况下表现出色。然而,在最坏的情况下,如当数组已经有序或者所有元素相等时,其时间复杂度会退化到O(n^2)。为了避免这种情况,通常会采用随机化或者其他策略来选择pivot。

在空间复杂度方面,快速排序是原地排序算法,它不需要额外的存储空间来创建数组的副本。但是,由于它是递归的,所以它的空间复杂度取决于递归调用栈的深度,最坏情况下为O(n),平均情况下为O(log n)。

稳定性

快速排序不是一个稳定的排序算法。这意味着相等的元素在排序后可能会改变它们原始的顺序。

实现
普通版:快速排序
需要额外的空间复杂度
func QuickSort(arr []int) []int {if len(arr) <= 1 {return arr}pivot := arr[0]var left []intvar right []intfor i := 1; i < len(arr); i++ {if arr[i] < pivot {left = append(left, arr[i])} else {right = append(right, arr[i])}}return append(append(QuickSort(left), pivot), QuickSort(right)...)
}

这个版本引入了额外的空间 left、right 存储排序中间数据,我们可以通过原地排序进一步优化。

进阶版:原地快速排序
不需要额外的空间复杂度
func QuickSort(arr []int) {quickSort(arr, 0, len(arr)-1)
}func quickSort(arr []int, low, high int) {if low < high {// Partition the array by setting the position of the pivot elementpivotIndex := partition(arr, low, high)// Recursively sort elements before partition and after partitionquickSort(arr, low, pivotIndex-1)quickSort(arr, pivotIndex+1, high)}
}func partition(arr []int, low, high int) int {pivot := arr[high] // using the last element as the pivoti := low           // place for swappingfor j := low; j < high; j++ {// If current element is smaller than or equal to pivotif arr[j] <= pivot {arr[i], arr[j] = arr[j], arr[i] // swap arr[i] and arr[j]i++                             // increment index of smaller element}}arr[i], arr[high] = arr[high], arr[i] // swap the pivot element with the element at index ireturn i                              // return the partitioning index
}
进阶二:三位取中法
优化 pivot 取数,避免取数不合理导致的时间复杂度上升
func QuickSort(arr []int) {quickSort(arr, 0, len(arr)-1)
}func quickSort(arr []int, low, high int) {if low < high {// Choose pivot using median-of-three methodpivotIndex := medianOfThree(arr, low, high)arr[pivotIndex], arr[high] = arr[high], arr[pivotIndex] // Move pivot to end// Partition the array around the pivotpivotIndex = partition(arr, low, high)// Recursively sort elements before partition and after partitionquickSort(arr, low, pivotIndex-1)quickSort(arr, pivotIndex+1, high)}
}func partition(arr []int, low, high int) int {pivot := arr[high]i := lowfor j := low; j < high; j++ {if arr[j] <= pivot {arr[i], arr[j] = arr[j], arr[i]i++}}arr[i], arr[high] = arr[high], arr[i]return i
}func medianOfThree(arr []int, low, high int) int {mid := low + (high-low)/2if arr[low] > arr[mid] {arr[low], arr[mid] = arr[mid], arr[low]}if arr[low] > arr[high] {arr[low], arr[high] = arr[high], arr[low]}if arr[mid] > arr[high] {arr[mid], arr[high] = arr[high], arr[mid]}return mid
}
进阶三:三项切分法
当有大量相同数据的时候,重复的部分无需排序,可以使用三项切分法进行优化
func QuickSort(arr []int) {quickSort(arr, 0, len(arr)-1)
}func quickSort(arr []int, low, high int) {if low < high {// Partition the array into three partslt, gt := threeWayPartition(arr, low, high)// Recursively sort elements before lt and after gtquickSort(arr, low, lt-1)quickSort(arr, gt+1, high)}
}func threeWayPartition(arr []int, low, high int) (int, int) {pivot := arr[low] // Choose the first element as pivotlt := low         // lt is the index for the last element less than pivotgt := high        // gt is the index for the first element greater than pivoti := low + 1      // Start from the element next to pivotfor i <= gt {if arr[i] < pivot {arr[lt], arr[i] = arr[i], arr[lt]lt++i++} else if arr[i] > pivot {arr[i], arr[gt] = arr[gt], arr[i]gt--} else {i++}}return lt, gt
}

快速排序的优化

Pivot选择的优化

快速排序的效率在很大程度上取决于pivot的选择。一个好的pivot可以将数组均匀地分成两部分,从而减少递归的深度。常见的优化策略包括随机选择pivot、三数取中法,以及中位数的中位数法。

三向切分的快速排序

当数组中存在大量重复元素时,传统的快速排序可能会进行许多不必要的比较和交换。三向切分的快速排序可以解决这个问题,它将数组分为三部分:小于pivot的元素、等于pivot的元素和大于pivot的元素。这种方法可以显著提高含有大量重复元素的数组的排序效率。

实际应用场景

快速排序由于其高效和易于实现的特点,在许多实际应用中得到了广泛的使用。例如:

  1. 数据库系统:在数据库管理系统中,快速排序被用于查询优化器中的排序操作。
  2. 文件系统:操作系统中的文件管理器可能会使用快速排序来对文件和目录进行排序。
  3. 大数据处理:在处理大规模数据集时,快速排序常用于分布式系统中的排序任务。
补充技术点

除了上述讨论的内容,快速排序还有其他一些值得注意的技术点:

  • 尾递归优化:在递归实现中,通过尾递归优化可以减少递归调用栈的深度,从而节省空间。
  • 非递归实现:可以使用栈来模拟递归过程,从而将快速排序转换为非递归形式。
  • 并行快速排序:在多核处理器上,可以并行地执行快速排序的不同部分,以进一步提高性能。
结论

快速排序是一种非常高效的排序算法,适用于大多数排序任务。通过合理选择pivot和使用三向切分等优化技术,可以进一步提高其性能,特别是在处理有大量重复元素的数组时。然而,由于其不稳定性,它可能不适用于需要保持元素相对顺序的场景。

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

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

相关文章

Presto 从提交SQL到获取结果 源码详解(3)

物理执行计划 回到SqlQueryExecution.startExecution() &#xff0c;执行计划划分以后&#xff0c; // 初始化连接&#xff0c;获取Connect 元数据&#xff0c;添加会话&#xff0c;初始ConnectId metadata.beginQuery(getSession(), plan.getConnectors()); // 构建物理执行…

mongodb 集合复制---聚合管道操作符$out来实现

1.集合复制 要将数据从一个集合复制到另一个集合------可以使用聚合管道操作符$out来实现。 $out操作符将聚合管道的结果写入到指定的集合中&#xff0c;可以是 已存在的集合 或者 新创建的集合 以下是一个示例聚合管道操作&#xff0c;将数据从一个集合复制到另一个集合&am…

AngularJS基础语法(2009版本)

jquery和AngularJS 数据绑定和获取对比&#xff1a; jquery&#xff0c;要操作DOM&#xff1a; angularJS&#xff0c;无需操作DOM就可以进行动态数据变化&#xff1a; 要使用Angularjs就需要在html页面先引入&#xff1a; ng-app&#xff1a; html页面中&#xff0c;需要给…

redis(17):什么是布隆过滤器?如何实现布隆过滤器?

1 布隆过滤器介绍 布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否在一个集合中。它基于位数组和多个哈希函数的原理,可以高效地进行元素的查询,而且占用的空间相对较小,如下图所示: 根据 key 值计算出它的存储位置,然后将此位置标…

如何理解SQL注入原理

基础概念 SQL注入&#xff08;SQL Inject&#xff09;指的是web应用程序对用户输入数据的合法性没有判断或过滤不严格&#xff0c;使得攻击者能够在web应用程序中事先定义好的查询语句的结尾添加额外的SQL语句。这些额外的SQL语句能够在管理员不知情的情况下执行&#…

API测试工具领域,Postman的10个最佳替换

Postman 赢得了流行且有效的 API 工具的声誉。然而&#xff0c;对于那些寻求更符合特定需求和偏好的替代方案的人来说&#xff0c;存在一些值得注意的选择。这些 Postman 替代方案提供了独特的特性和功能&#xff0c;可满足测试过程的各个方面的需求。 在本博客中&#xff0c;…

如何快速的在线编辑pdf?6个软件让你轻松编辑pdf

如何快速的在线编辑pdf&#xff1f;6个软件让你轻松编辑pdf 在线编辑PDF文件是一项非常方便的任务&#xff0c;以下是六款让您轻松进行在线PDF编辑的软件&#xff1a; 嗨动PDF编辑器&#xff1a;这是一个功能强大的PDF编辑器&#xff0c;可以帮助您快速编辑PDF文档&#xff…

Flutter 中的 SliverAnimatedList 小部件:全面指南

Flutter 中的 SliverAnimatedList 小部件&#xff1a;全面指南 Flutter 是一个由 Google 开发的跨平台 UI 框架&#xff0c;它提供了丰富的组件来帮助开发者构建高性能、美观的移动、Web 和桌面应用。在 Flutter 的滚动组件中&#xff0c;SliverAnimatedList 是一个特殊的组件…

封装了一个iOS对号成功动画

基本思路其实很简单&#xff0c;就是通过贝塞尔曲线画出路径&#xff0c;然后 使用CAShapeLayer 渲染路径&#xff0c;然后通过strokeEnd 动画实现 路径的效果&#xff0c;这里注意&#xff0c;这个过程中过遇到过一个问题&#xff0c;就是 对号动画完成之后&#xff0c;整个对…

Superset二次开发之更新 SECRET_KEY

SECRET_KEY 的作用 加密和签名:SECRET_KEY用于对敏感数据(如会话、cookie、CSRF令牌)进行加密和签名,防止数据被篡改。安全性:确保应用的安全性,防止跨站请求伪造(CSRF)攻击和会话劫持等安全问题。如何生成 SECRET_KEY openssl rand -base64 42 配置 SECRET_KEY 在sup…

【主动均衡和被动均衡】

文章目录 1.被动均衡2.主动均衡1.被动均衡 被动均衡一般通过电阻放电的方式,对电压较高的电池进行放电,以热量形式释放电量,为其他电池争取更多充电时间。这样整个系统的电量受制于容量最少的电池。充电过程中,锂电池一般有一个充电上限保护电压值,当某一串电池达到此电压…

uniapp 添加字体ttf

效果图如下 一、逻辑概述 在uniapp中使用字体&#xff0c;一共分成两种情况&#xff0c;一种是普通vue页面&#xff0c;一种是nvue页面引入字体。。 1.vue页面引入字体需要如下步骤 1. 先选择下载一种字体&#xff1a;字体格式一般为 ttf后缀名 黄凯桦律师手写体免费下载和在线…

Linux--EXT2文件系统

参考资料&#xff1a; linux之EXT2文件系统--理解block/block group/索引结点inode/索引位图_一个块组中索引节点表和数据块区最多占用字节-CSDN博客 linux环境&#xff1a; Linux version 5.15.146.1-microsoft-standard-WSL2 (root65c757a075e2) (gcc (GCC) 11.2.0, GNU ld…

Vue前端如何配合SpringBoot后端实现文件下载

从HTML页面下载文件是非常简单的&#xff0c;直接向后端发起请求&#xff0c;后端处理请求就可以了&#xff1b;但是如果前端使用Vue开发&#xff0c;那么实现文件下载就有些曲折&#xff1a;Vue前端本身作为服务端存在&#xff0c;为了实现下载就需要将请求通过代理转到后端服…

Java后端模拟面试 题集⑤

1.先作个自我介绍吧 面试官您好&#xff0c;我叫张睿超&#xff0c;来自湖南长沙&#xff0c;大学毕业于湖南农业大学&#xff0c;是一名智能科学与技术专业的统招一本本科生。今天主要过来面试贵公司的Java后端开发工程师岗位。 大学里面主修的课程是Java、Python、数字图像…

FreeRtos进阶——中断的内部逻辑

中断与非中断API的区别 BaseType_t xQueueSendToBack(QueueHandle_t xQueue,const void *pvItemToQueue,TickType_t xTicksToWait); BaseType_t xQueueSendToBackFromISR(QueueHandle_t xQueue,const void *pvItemToQueue,BaseType_t *pxHigherPriorityTaskWok…

Session+Redis,Token+Redis,JWT+Redis,用户身份认证,到底选择哪种更合适?

1三中方案的比较 在选择Session+Redis、Token+Redis、JWT+Redis这三种用户身份认证方案时,我们需要考虑各自的优势、劣势以及应用场景。以下是对这三种方案的详细分析和比较: 1. Session+Redis 优势: Session登录是一种在Web应用程序中用于跟踪用户状态的机制,通过在服务…

huggingface笔记:实例化大模型(Acclerate)

1 访问大模型的一个障碍&#xff1a;内存量 访问非常大的预训练模型的一个障碍是所需的内存量。当加载预训练的 PyTorch 模型时&#xff0c;通常会&#xff1a; 创建一个随机权重的模型。您的预训练权重。将这些预训练权重放入模型中。——>前两步都需要在内存中有模型的完…

MFC工控项目实例之二添加iPlotx控件

承接专栏《MFC工控项目实例之一主菜单制作》 在WIN10下使用Visual C 6.0 &#xff08;完整绿色版&#xff09;添加iPlotx控件的方法。 1、在资源主对话框界面点击鼠标右键如图选择插入Active控件点击进入。 2、选择iPlotx Contrlolh点击确定。 3、在对话框界面插入iPlotx控件。…

emp.dll文件丢失要怎么解决?荒野大镖客emp.dll修复方法分享

软件运行过程中经常遇到各种技术问题&#xff0c;其中之一就是动态链接库&#xff08;DLL&#xff09;文件丢失的现象。DLL文件是Windows操作系统中一个重要的组件&#xff0c;它包含运行多个应用程序所需要的代码和数据。因此&#xff0c;一个丢失的DLL文件&#xff0c;如“em…