“快速排序:一种美丽的算法混沌”(1.hoare)

 欢迎来到我的博客!在今天的文章中,我将采用一种独特且直观的方式来探讨我们的主题:我会使用一幅图像来贯穿整篇文章的讲解。这幅精心设计的图表不仅是我们讨论的核心,也是一个视觉辅助工具,帮助你更深入地理解和掌握本文的内容。

通过这种方式,我们可以一步步深入本文的主题,每个阶段都将图像作为参考。这样不仅可以增加信息的吸收和理解,还能让学习过程更加生动和有趣。无论你是刚入门的新手还是寻求更深层次理解的老手,这幅图都将是你理解本文内容的有力工具。

所以,让我们开始这一独特的旅程,一边阅读文章,一边参考这幅将贯穿全文的图表,深入探索我们今天的话题!

想象一下,你走进了一个充满了成千上万本书的巨大图书馆。这些书籍横七竖八地堆放着,没有任何顺序。你需要找到一本特定的书,但在这混乱中,这看似是一个不可能完成的任务。这时,一位图书管理员出现了。这位管理员不仅知道每本书的具体位置,而且还能以令人难以置信的速度将这些书籍分类和组织起来,让你轻松地找到所需的书籍。这正是快速排序算法在数据世界中的角色:一个高效、精准的“信息管理员”,能迅速将数据整理成有序的结构。

快速排序,这个由英国计算机科学家Tony Hoare在1960年发明的算法,至今仍是计算机科学中最受欢迎和广泛使用的排序算法之一。它之所以名为“快速”,是因为它比其他传统排序算法(如冒泡排序或插入排序)要快得多,尤其是在处理大量数据时。快速排序的基本思想是选择一个元素作为“基准”,然后将数组分为两部分,一边是小于基准的元素,另一边是大于基准的元素,接着递归地在这两部分进行同样的操作。

在现代计算的世界里,快速排序不仅是一个算法的范例,它也是处理大规模数据不可或缺的工具。从数据库的查询优化到大数据分析,再到我们日常使用的搜索引擎,快速排序的影响无处不在。它的效率和优雅使它成为了计算机科学中的一个经典,一个在不断变化和发展的数字时代依然屹立不倒的算法巨人。

快速排序有三种方法。他们分别是“hoare版本”  “挖坑法” 和 “前后指针版本”本文只介绍第一种,剩下两种请挪步下一篇文章!

1.hoare版本

一张图片带你秒懂过程! 

 

快速排序的基本原理

快速排序是一种高效的排序算法,使用分而治之的策略来排序数据。以下是它的基本步骤:

  1. 选择基准值(Pivot)

    从数组中选择一个元素作为基准值。这个选择可以是随机的,通常选择第一个或最后一个元素。
  2. 分区(Partitioning)

    重新排列数组,使得所有小于基准值的元素都排在基准之前,而所有大于基准值的元素都排在基准之后。这个步骤结束后,基准元素就处于其最终位置。
  3. 递归排序子数组

    对基准左侧和右侧的子数组分别递归地执行快速排序。
  4. 终止条件

    当子数组的大小减至1或0时,递归结束,因为每个元素都已经被排序。

 

// 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right)
{if(right - left <= 1)return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分int div = partion(array, left, right);// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)// 递归排[left, div)QuickSort(array, left, div);// 递归排[div+1, right)QuickSort(array, div+1, right);
}

上述为快速排序递归实现的主框架。

分区(Partitioning)

我们首先选择基准值将其定义为key  一般为最左或最右,此时,对其进行遍历,L向右找比key大的值,R向左找比key小的值,找到了就相互交换, 直到第一次相遇,将key与相遇点进行交换

 

 

 此时可能会有人疑问,为什么相遇就一定要交换,如果我们相遇后这个值比key大的情况下,是否违背我们要排序的初衷呢?  这里先给出答案:相遇位置一定比key小

 原因:

 如果左边先走就无法保证一定比key小 

 接下来我们实现初步代码,如何让他先进行左右交换并让key挪动到相遇位置

void QuickSort(int* a, int begin, int end)
{int keyi = begin;int right = end;int left = begin ;while (left < right){//左边找大while(left < right && a[left] < a[keyi])//加入&&的原因是如果到相遇点时,left还是小于kei,他不会在相遇点停下,并且再次交换就颠倒顺序了{++left;}//右边找小while (left < right && a[right] < a[keyi]){--right;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;}

 此时我们已经完成初步交换

 那我们是不是可以得出,如果接下来让左边变得有序,右边也有序。整体就有序了

 递归排序子数组

接下来我们对左右子数组进行递归处理,就可以得出一个有序数组

接下来我们先对左子树进行递归

 

 进行处理后,3就是key就是相遇处,接着再对左子数组进行处理递归处理直到只剩一个数组时返回

 接着再对右子树进行处理,以此类推,这里不多做赘述

终止条件

这里涉及到递归子分治问题,所以我们进行修改代码,将第一步的key也就是图中的6保留,keyi-1就是左边的数组个数,keyi+1就是右边的数组个数,我们进行递归,返回条件就是只有一个节点时返回。

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int keyi = begin;int right = end;int left = begin+1 ;while (left < right){//左边找大while(left < right && a[left] < a[keyi])//加入&&的原因是如果到相遇点时,left还是小于kei,他不会在相遇点停下,并且再次交换就颠倒顺序了{++left;}//右边找小while (left < right && a[right] < a[keyi]){--right;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;//左半  [begin , keyi-1]     keyi     [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

快速排序的优化

对以上代码进行测试,我们会发现在处理有序数组时,快速排序反而是最慢的排序

为什么有序的情况下快排会很吃亏呢? 因为我们采取了固定选Key 

理想状态下,快排时间复杂度是O(N*logN)

所以我们采取三数取中法 可以避开所有最坏的情况,越接近二分,就越快

三数取中法是快速排序算法中一种改进的基准(pivot)选择方法。它的目的是减少快速排序在最坏情况下的时间复杂度,这通常发生在选择的基准值是最小或最大元素时。通过选择一个更接近中间值的基准,可以提高分区的均匀性,从而使算法更加高效。

在传统的快速排序中,基准通常被选择为数组的第一个元素、最后一个元素或随机一个元素,这样在面对某些特定的数据集时(比如已经排序或接近排序的数组)可能会导致算法效率低下。

三数取中法是通过考虑数组的第一个元素、中间元素和最后一个元素,然后选择这三个元素的中位数作为基准。这种方法在实践中通常能给出一个较好的基准,从而使得分区更加平衡。

 

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int keyi = begin;int right = end;int left = begin ;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){--right;}//左边找大while(left < right && a[left] <= a[keyi])//加入&&的原因是如果到相遇点时,left还是小于kei,他不会在相遇点停下,并且再次交换就颠倒顺序了{++left;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;//左半  [begin , keyi-1]     keyi     [keyi+1, end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}
  1. begin 和 end: 这两个变量通常表示正在处理的数组或子数组的开始和结束索引。在数组的第一次排序时,begin 通常是 0(数组的第一个元素的索引),而 end 是数组的最后一个元素的索引。

  2. 计算中点: (begin + end) / 2 这个表达式的目的是找到 beginend 之间的中间索引。这是通过将 beginend 的值相加,然后除以 2 来实现的。由于这里使用的是整数除法,所以即使 begin + end 是奇数,结果也会被自动向下取整到最接近的整数。

  3. 数组索引: 在 C/C++ 中,数组的索引是从 0 开始的。因此,计算出的 midi 一定是一个有效的数组索引,只要 beginend 是有效的。这意味着 a[midi] 是数组中的一个实际元素。

总结来说,int midi = (begin + end) / 2; 确保 midi 是位于 beginend 之间的一个有效索引,而 a[midi] 则是数组在这个索引位置的元素。这是一种常见的找到数组中间元素索引的方法。

 

结论:Hoare版本快速排序的效率与优势

Hoare版本的快速排序算法是计算机科学中的一个经典例子,它展示了高效算法设计的重要性。通过其独特的分区策略,该算法能够快速、高效地处理各种大小的数据集。Hoare版本的快速排序在最佳情况下的时间复杂度为 O(n log n),并且即使在平均情况下也能维持这一高效表现。虽然它在最坏情况下的时间复杂度为 O(n^2),但通过智能选择枢轴点,如使用三数取中法或随机选取,可以大幅减少这一风险。

此外,Hoare版本相较于其他变体如Lomuto分区方法,通常需要更少的数据交换操作,这使得它在处理大数据集时更为高效。然而,它的实现相比一些其他版本更为复杂,可能需要更细致的理解和调试。

总的来说,Hoare版本的快速排序因其高效性和对大数据集友好的特性,在算法领域和实际应用中仍然占有一席之地。它不仅是排序算法的一个优秀代表,也是计算机算法设计和分析的一个经典案例。

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

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

相关文章

学习深度强化学习---第2部分----RL动态规划相关算法

文章目录 2.1节 动态规划简介2.2节 值函数与贝尔曼方程2.3节 策略评估2.4节 策略改进2.5节 最优值函数与最优策略2.6节 值迭代与策略迭代2.7节 动态规划求解最优策略 本部分视频所在地址&#xff1a;深度强化学习的理论与实践 2.1节 动态规划简介 态规划有两种思路&#xff1…

前端 Web Workers 简介

简介 以前我们总说&#xff0c;JS 是单线程没有多线程&#xff0c;当 JS 在页面中运行长耗时同步任务的时候就会导致页面假死影响用户体验&#xff0c;从而需要设置把任务放在任务队列中&#xff1b;执行任务队列中的任务也并非多线程进行的&#xff0c;然而现在 HTML5 提供了…

App备案、ios备案Bundle ID查询、公钥信息、SHA-1值

App备案、ios备案Bundle ID查询、公钥信息、SHA-1值 Bundle ID这个就不说了&#xff0c;都知道是啥&#xff0c;主要说公钥信息和SHA-1值的获取 打开钥匙串访问&#xff0c;找到当前需要备案App的dis证书&#xff0c;如下&#xff1a; #####右键点击显示简介 #####可以看…

03.仿简道云公式函数实战-QLExpress初探

1. 前言 在上一篇文章中&#xff0c;我们简单介绍了一下表达式引擎&#xff0c;并引出我们的主角QLExpress.在这篇文章中&#xff0c;我们先来一个QLExpress的热身。 2. 初探QLExpress 源码地址&#xff1a;https://github.com/alibaba/qlExpress 笔者下载源码的版本是3.3.…

STL源码剖析笔记——适配器(adapters)

系列文章目录 STL源码剖析笔记——迭代器 STL源码剖析笔记——vector STL源码剖析笔记——list STL源码剖析笔记——deque、stack&#xff0c;queue STL源码剖析笔记——Binary Heap、priority_queue STL源码剖析笔记——AVL-tree、RB-tree、set、map、mutiset、mutimap STL源…

【Spring 基础】00 入门指南

【Spring 基础】00 入门指南 文章目录 【Spring 基础】00 入门指南1.简介2.概念1&#xff09;控制反转&#xff08;IoC&#xff09;2&#xff09;依赖注入&#xff08;DI&#xff09; 3.核心模块1&#xff09;Spring Core2&#xff09;Spring AOP3&#xff09;Spring MVC4&…

php实现截取姓名中的第一个字作为头像的实战记录

php 截取中文字符串第一个字 substr 函数 在 PHP 中&#xff0c;使用 substr 函数来截取中文字符串的第一个字。由于 PHP 默认的字符编码是 UTF-8&#xff0c;它可以正确处理中文字符。 $chineseString "你好世界"; $firstChar substr($chineseString, 0, 1); e…

vue2 组件内路由守卫使用

1、beforeRouteEnter 进入页面 to – 即将要跳转到的页面 form – 跳转前的页面&#xff0c;从哪个页面跳转过来的 next – 下一步&#xff0c;若无指定跳转的路由&#xff0c;设置为空 next() 即可 beforeRouteEnter(to, from, next) {next() }, 使用 beforeRouteEnter 时&…

中文分词演进(查词典,hmm标注,无监督统计)新词发现

查词典和字标注 目前中文分词主要有两种思路&#xff1a;查词典和字标注。 首先&#xff0c;查词典的方法有&#xff1a;机械的最大匹配法、最少词数法&#xff0c;以及基于有向无环图的最大概率组合&#xff0c;还有基于语言模型的最大概率组合&#xff0c;等等。 查词典的方法…

知识产权服务企业网站建设效果如何

知识产权服务也有较高的市场需求度&#xff0c;尤其如今互联网深入到各个行业&#xff0c;无论个人还是企业都会以不同的方式经营&#xff0c;相应的为保障自身权益&#xff0c;注册商标、专利等自然不可少&#xff0c;而对普通小白来说&#xff0c;想要完成这些流程也是有些难…

Python实现获取b站视频的弹幕内容

前言 本文是该专栏的第39篇,后面会持续分享python的各种干货知识,值得关注。 在本专栏之前,有详细介绍使用python增加b站视频的播放量方法,感兴趣的同学可往前翻阅《Python-增加b站视频播放量》。而本文,笔者再来单独的详细介绍,通过python来获取b站视频的弹幕内容。如下…

CGAL的3D皮肤表面网格

1、介绍 Edelsbrunner 引入的皮肤表面和具有丰富而简单的组合和几何结构&#xff0c;使其适合在生物计算中模拟大分子。 对这些表面进行网格划分通常是进一步处理其几何形状所必需的&#xff0c;例如在数值模拟和可视化中。 皮肤表面由一组加权点&#xff08;输入球&#xff09…

力扣labuladong——一刷day70

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、力扣814. 二叉树剪枝二、力扣1325. 删除给定值的叶子节点 前言 这道题的难点在于要一直剪枝&#xff0c;直到没有值为 0 的叶子节点为止&#xff0c;只有从…

RecursionError: maximum recursion depth exceeded in comparison

诸神缄默不语-个人CSDN博文目录 这个bug的产生原因是运行rouge包时句子太长&#xff0c;所以递归次数过多了。完整的报错信息懒得粘了&#xff0c;总之很长&#xff0c;解决方案就是手动在程序开始处就增大递归次数&#xff1a; import sys sys.setrecursionlimit(100000)具体…

html通过CDN引入Vue使用Vuex以及Computed、Watch监听

html通过CDN引入Vue使用Vuex以及Computed、Watch监听 近期遇到个需求&#xff0c;就是需要在.net MVC的项目中&#xff0c;对已有的项目的首页进行优化&#xff0c;也就是写原生html和js。但是咱是一个写前端的&#xff0c;写html还可以&#xff0c;.net的话&#xff0c;开发也…

K8S学习指南-minikube的安装

简介 Minikube 是一个用于在本地开发环境中运行 Kubernetes 集群的工具。它允许开发人员在单个节点上体验 Kubernetes&#xff0c;无需配置复杂的生产环境。本指南将详细介绍在 Windows、CentOS 和 Ubuntu 系统上安装 Minikube 的步骤。 1. Windows 系统安装 1.1 &#xff1…

期末速成数据库极简版【查询】(3)

目录 多表查询 【8】多表连接——内连接 &#x1f642;等值连接 &#x1f642;自然连接 &#x1f642;非等值连接 【9】多表连接——外连接 【10】交叉连接不考 【11】联合查询 【12】扩展多表连接 【13】嵌套查询 &#x1f642; 多表查询 【8】多表连接——内连…

HIVE学习(hive基础)

HIVE基础介绍 一、HIVE简介二、hive的数据类型1、基本数据类型2、复合数据类型 三、HIVE的DDL操作四、创建一个表1. 建表语句 五、修改表结构1.修改表名2. 列修改或增加3. 修改分区 五、常见函数六、一对一关联left join左关联right join 右关联内连接全连接查询只有A表的数据 …

计算机视觉-机器学习-人工智能顶会 会议地址

计算机视觉-机器学习-人工智能顶会 会议地址 最近应该要整理中文资料的参考文献&#xff0c;很多会议文献都需要补全会议地点&#xff08;新国标要求&#xff09;。四处百度感觉也挺麻烦的&#xff0c;而且没有比较齐全的网站可以搜索。因此自己整理了一下计算机视觉-机器学习…

OSPF路由协议

随着Internet技术在全球范围的飞速发展&#xff0c;OSPF已成为目前应用最广泛的路由协议之一。OSPF&#xff08;Open Shortest Path First&#xff09;路由协议是由IETF&#xff08;Internet Engineering Task Force&#xff09;IGP工作组提出的&#xff0c;是一种基于SPF算法的…