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

漫画:什么是快速排序算法?

这篇文章,以对话的方式,详细着讲解了快速排序以及排序排序的一些优化。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一禅:归并排序是一种基于分治思想的排序,处理的时候可以采取递归的方式来处理子问题。我弄个例子吧,好理解点。例如对于这个数组arr[] = { 4,1,3,2,7,5,8,0}。
在这里插入图片描述
我们把它切割成两部分。

在这里插入图片描述
把左半部分和右半部分分别排序好。

在这里插入图片描述
之后再用一个临时数组,把这两个有序的子数组汇总成一个有序的大数组
在这里插入图片描述
排好之后在复制原源arr数组
在这里插入图片描述
这时,源数组就排序完毕了

在这里插入图片描述
在这里插入图片描述
一禅:左半部分和右半部分的排序相当于一个原问题的一个子问题的,也是采取同样的方式,把左半部分分成两部分,然后…

直到分割子数组只有一个元素或0个元素时,这时子数组就是有序的了(因为只有一个元素或0个,肯定是有序的啊),就不用再分割了,直接返回就可以了(当然,我在讲解这个归并排序的过程中,是假设你大致了解归并排序的前提下的了)

在这里插入图片描述
在这里插入图片描述
一禅:把一个n个元素的数组分割成只有一个元素的数组,那么我需要切logn次,每次把两个有序的子数组汇总成一个大的有序数组,所需的时间复杂度为O(n)。所以总的时间复杂度为O(nlogn)

快速排序

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
小白:那倒不是,快速排序的平均时间复杂度也是O(nlogn),不过他不需要像归并排序那样,还需要一个临时的数组来辅助排序,这可以节省掉一些空间的消耗,而且他不像归并排序那样,把两部分有序子数组汇总到临时数组之后,还得在复制回源数组,这也可以节省掉很多时间。

在这里插入图片描述
在这里插入图片描述
小白:快速排序也是和归并排序差不多,基于分治的思想以及采取递归的方式来处理子问题。例如对于一个待排序的源数组arr = { 4,1,3,2,7,6,8}。

在这里插入图片描述
我们可以随便选一个元素,假如我们选数组的第一个元素吧,我们把这个元素称之为”主元“吧。
在这里插入图片描述
然后将大于或等于主元的元素放在右边,把小于或等于主元的元素放在左边。
在这里插入图片描述
通过这种规则的调整之后,左边的元素都小于或等于主元,右边的元素都大于或等于主元,很显然,此时主元所处的位置,是一个有序的位置,即主元已经处于排好序的位置了。

主元把数组分成了两半部分。把一个大的数组通过主元分割成两小部分的这个操作,我们也称之为分割操作(partition)。

接下来,我们通过递归的方式,对左右两部分采取同样的方式,每次选取一个主元 元素,使他处于有序的位置。
在这里插入图片描述

那什么时候递归结束呢?当然是递归到子数组只有一个元素或者0个元素了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分割操作:单向调整

一禅:就按照你说的,选一个主元,你刚才选的是第一个元素为主元,这次我选最后一个为主元吧,哈哈。假设数组arr的范围为[left, right],即起始下标为left,末尾下标为right。源数组如下
在这里插入图片描述
然后可以用一个下标 i 指向 left,即 i = left ;用一个下标 j 也指向l eft,即j = left
在这里插入图片描述
接下来 j 从左向右遍历,遍历的范围为 [left, right-1] ,遍历的过程中,如果遇到比主元小的元素,则把该元素与 i 指向的元素交换,并且 i = i +1
在这里插入图片描述
当j指向1时,1比4小,此时把i和j指向的元素交换,之后 i++。
在这里插入图片描述
就这样让j一直向右遍历,直到 j = right
在这里插入图片描述
遍历完成之后,把 i 指向的元素与主元进行交换,交换之后,i 左边的元素一定小于主元,而 i 右边的元素一定大于或等于主元。这样,就 i 完成了一次分割了。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一禅一言不合就把代码撸好了,第一版代码如下:

//分割操作:方法一,单向调整
int partion(int a[], int left, int right)
{int temp,pivot;//pivot存放主元int i,j;i = left;pivot = a[right];for(j = left;j < right;j++){if(a[j] < pivot){  //交换值temp = a[i];a[i] = a[j];a[j] = temp;i++;}}a[right] = a[i];a[i] = pivot;return i;//把主元的下标返回
}
//快速排序
void QuickSort(int a[], int left, int right)
{int center;int i,j;int temp;if(left < right){center = partion(a,left,right);QuickSort(a,left,center-1);//左半部分QuickSort(a,center+1,right);//右半部分}
}

分割操作:双向调整

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
小白:对啊,因为你这调整方法,可能会出现对同一个元素,进行多次交换,例如刚才你在演示的那组元素,在j向右遍历交换的过程中:

第一次:8和1交换

第二次:8和3交换

第三次:8和2交换

8被重复交换了很多次
在这里插入图片描述
在这里插入图片描述
小白:其实,我们可以这样来调整元素。我还是用我的第一个元素充当主元吧。哈哈

源数组如下
在这里插入图片描述
然后用令变量i = left + 1,j = right。然后让 i 和 j 从数组的两边向中间扫描。
在这里插入图片描述
i 向右遍历的过程中,如果遇到大于或等于主元的元素时,则停止移动,j向左遍历的过程中,如果遇到小于或等于主元的元素则停止移动
在这里插入图片描述
当i和j都停止移动时,如果这时i < j,则交换 i, j 所指向的元素。此时 i < j,交换8和3
在这里插入图片描述
然后继续向中间遍历,直到i >= j。
在这里插入图片描述
此时i >= j,分割结束。

最后在把主元与 j 指向的元素交换(当然,与i指向的交换也行)。
在这里插入图片描述
这个时候,j 左边的元素一定小于或等于主元,而右边则大于或等于主元。

到此,分割调整完毕

代码如下:

方法二:双向扫描
int partition2( int[] arr, int left, int right)
{int pivot = arr[left];int i = left + 1;int j = right;while(true){  //向右遍历扫描while(i <= j && arr[i] <= pivot) i++;//向左遍历扫描while(i <= j && arr[j] => pivot) j--;if(i >= j)break;//交换int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}//把arr[j]和主元交换arr[left] = arr[j];arr[j] = povit;return j;
}

时间复杂度

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
小白:因为快速排序的最坏时间复杂度是O(n2)。

例如有可能会出现一种极端的情况,每次分割的时候,主元左边的元素个数都为0,而右边都为n-1个。这个时候,就需要分割n次了。而每次分割整理的时间复杂度为O(n),所以最坏的时间复杂度为O(n2)。

最好的情况就是每次分割都能够从数组的中间分割了,这样分割logn次就行了,此时的时间复杂度为O(nlogn)。

而平均时间复杂度,则是假设每次主元等概率着落在数组的任意位置,最后算出来的时间复杂度为O(nlogn),至于具体的计算过程,我就不展开了。

不过显然,像那种极端的情况是极少发生的。
在这里插入图片描述
在这里插入图片描述
小白:哈哈,之所以说它快,是因为它不像归并排序那样,需要额外的辅助空间,而且在分割调整的时候,不像归并排序那样,元素还要在辅助数组与源数组之间来回复制。

稳定性

在这里插入图片描述
在这里插入图片描述
一禅:不是啊,例如,在排序的过程中,主元在和j交换的时候是有可能破坏稳定性的,例如
在这里插入图片描述
把主元与j指向的元素进行交换
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//随机选取主元
int random_partition(int[] arr, int left, int right)
{i = random(left, right);//随机选取一个位置//在把这个位置的元素与ar[left]交换swap(arr[i], arr[left]);return partition(arr, left, right);
}

终于写完,这个快排写了挺长时间,觉得有收获的话,可以转发支持一波哦(´-ω-`)。

更多排序算法文章

1. 漫画:什么是冒泡排序算法?

2. 漫画:什么是选择排序算法?

3. 漫画:什么是插入排序算法?

4. 漫画:什么是希尔排序算法?

5. 漫画:什么是归并排序算法?

6. 漫画:什么是快速排序算法?

7. 漫画:什么是堆排序算法?

8. 漫画:什么是基数排序算法?

9. 漫画:什么是外部排序?

10. 什么是计数排序?

11. 十大排序算法极简汇总篇

推荐阅读

下载破 2w+,在校生必看,《程序员内功修炼》第二版出炉

从双非到大厂,帅地写了一本原创PDF送给大家

一个帮你拿offer的校招网站

算法刷题路线(系统+全面)

作者简介:我是帅地,校招拿到过不少大厂offer,毕业去了腾讯研发岗,毕业半年整到人生第一个 100 万,目前专注于写大学规划 + 校招求职相关的内容,著有个人原创网站 PlayOffer。

相关文章:

漫画:什么是快速排序算法?

这篇文章&#xff0c;以对话的方式&#xff0c;详细着讲解了快速排序以及排序排序的一些优化。 一禅&#xff1a;归并排序是一种基于分治思想的排序&#xff0c;处理的时候可以采取递归的方式来处理子问题。我弄个例子吧&#xff0c;好理解点。例如对于这个数组arr[] { 4&…...

vue 3.0组件(下)

文章目录前言&#xff1a;一&#xff0c;透传属性和事件1. 如何“透传属性和事件”2.如何禁止“透传属性和事件”3.多根元素的“透传属性和事件”4. 访问“透传属性和事件”二&#xff0c;插槽1. 什么是插槽2. 具名插槽3. 作用域插槽三&#xff0c;单文件组件CSS功能1. 组件作用…...

双指针 -876. 链表的中间结点-leetcode

开始一个专栏&#xff0c;写自己的博客 双指针&#xff0c;也算是作为自己的笔记吧&#xff01; 双指针从广义上来说&#xff0c;是指用两个变量在线性结构上遍历而解决的问题。狭义上说&#xff0c; 对于数组&#xff0c;指两个变量在数组上相向移动解决的问题&#xff1b;对…...

Linux之运行级别

文章目录一、指定运行级别基本介绍CentOS7后运行级别说明一、指定运行级别 基本介绍 运行级别说明: 0:关机 1:单用户【找回丢失密码】 2:多用户状态没有网络服务 3:多用户状态有网络服务 4:系统未使用保留给用户 5:图形界面 6:系统重启 常用运行级别是3和5&#xff0c;也可以…...

python搭建web服务器

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…...

【SpringCloud】SpringCloud Feign详解

目录前言SpringCloud Feign远程服务调用一.远程调用逻辑图二.两个服务的yml配置和访问路径三.使用RestTemplate远程调用四.构建Feign五.自定义Feign配置六.Feign配置日志七.Feign调优八.抽离Feign前言 微服务分解成多个不同的服务&#xff0c;那么多个服务之间怎么调用呢&…...

更改Hive元数据发生的生产事故

今天同事想在hive里用中文做为分区字段。如果用中文做分区字段的话&#xff0c;就需要更改Hive元 数据库。结果发生了生产事故。导致无法删除表和删除分区。记一下。 修改hive元数据库的编码方式为utf后可以支持中文&#xff0c;执行以下语句&#xff1a; alter table PARTITI…...

《Netty》从零开始学netty源码(八)之NioEventLoop.selector

目录java原生的WEPollSelectorImplnetty的SelectionKey容器SelectedSelectionKeySetnetty的SelectedSelectionKeySetSelectorSelectorTupleopenSelector每一个NioEventLoop配一个选择器Selector&#xff0c;在创建NioEventLoop的构造函数中会调用其自身方法openSelector获取sel…...

TCP UDP详解

文章目录TCP UDP协议1. 概述2. 端口号 复用 分用3. TCP3.1 TCP首部格式3.2 建立连接-三次握手3.3 释放连接-四次挥手3.4 TCP流量控制3.5 TCP拥塞控制3.6 TCP可靠传输的实现3.7 TCP超时重传4. UDP5.TCP与UDP的区别TCP UDP协议 1. 概述 TCP、UDP协议是TCP/IP体系结构传输层中的…...

超详细淘宝小程序的接入开发步骤

本文是向大家介绍的关于工作中遇到的如何对接淘宝小程序开发的步骤&#xff0c;它能够帮助大家省略在和淘宝侧对接沟通过程中的一些繁琐问题&#xff0c;便捷大家直接快速开展工作~~一、步骤演示1、首先我们打开淘宝开放平台&#xff0c;进入控制台2、进入控制台后&#xff0c;…...

【Python】正则表达式re库

文章目录函数re.match函数re.search函数re.findall函数re.compile函数re.sub函数re.split函数修饰符正则表达式模式正则表达式实例函数 re.match函数 re.match()函数用于尝试从字符串的 起始位置 匹配一个模式&#xff0c;匹配成功返回一个匹配对象&#xff0c;否则返回None。…...

JDK8使用Visual VM根据Dump文件排查OutOfMemoryError生产问题思路

文章目录1. 前言2. 堆内存溢出3. GC执行异常4. 元空间内存溢出5. 创建线程异常6. 内存交换问题7. 数组长度过大8. 系统误杀异常1. 前言 当系统异常产生了dump文件需要我们对其进行排查时&#xff0c;其本质上考验的是我们对于Java运行时内存结构的知识掌握是否牢固以及对业务代…...

2023年网络安全比赛--网络安全事件响应中职组(超详细)

一、竞赛时间 180分钟 共计3小时 二、竞赛阶段 1.找出黑客植入到系统中的二进制木马程序,并将木马程序的名称作为Flag值(若存在多个提交时使用英文逗号隔开,例如bin,sbin,…)提交; 2.找出被黑客修改的系统默认指令,并将被修改的指令里最后一个单词作为Flag值提交; 3.找出…...

【半监督学习】3、PseCo | FPN 错位对齐的高效半监督目标检测器

文章目录一、背景二、方法2.1 基础框架结构2.2 带噪声的伪边界框学习2.3 多视图尺度不变性学习三、实验论文&#xff1a;PseCo: Pseudo Labeling and Consistency Training for Semi-Supervised Object Detection 代码&#xff1a;https://github.com/ligang-cs/PseCo 出处&a…...

Tomcat+Servlet初识

文章目录Tomcat什么是TomcatTomcat的安装启动tomcat静态页面的访问动态页面的访问一个Servlet程序的部署流程Tomcat 什么是Tomcat Tomcat是一个HTTP服务器&#xff0c;在开发或调试Servlet代码时应用广泛&#xff1b;使用Tomcat&#xff0c;实际就是将用户浏览器输入的http请…...

ChatGPT-4 终于来了(文末附免费体验地址)

大家好&#xff0c;我是小钱学长。 ChatGPT4.0 重磅来袭&#xff0c;今天一打开plus页面出现的就是这个GPT-4的体验界面&#xff01;现在就带大家一起看看GPT4.0​。 进入之后是这样的 看到最下面有一行话&#xff0c;目前应该是4个小时限制100条消息。 GPT-4有什么优势&…...

【C++学习】类和对象(中)一招带你彻底了解六大默认成员函数

前言&#xff1a;在之前&#xff0c;我们对类和对象的上篇进行了讲解&#xff0c;今天我们我将给大家带来的是类和对象中篇的学习&#xff0c;继续深入探讨【C】中类和对象的相关知识&#xff01;&#xff01;&#xff01; 目录 1. 类的6个默认成员函数 2. 构造函数 2.1概念介…...

面试——Java基础

说一说你对Java访问权限的了解 在修饰成员变量/成员方法时&#xff0c;该成员的四种访问权限的含义如下&#xff1a; private&#xff1a;该成员可以被该类内部成员访问&#xff1b; default&#xff1a;该成员可以被该类内部成员访问&#xff0c;也可以被同一包下其他的类访…...

JavaWeb——Request(请求)和Response(响应)介绍

在写servlet时需要实现5个方法&#xff0c;在一个service方法里面有两个参数request和response。 浏览器向服务器发送请求会发送HTTP的请求数据——字符串&#xff0c;这些字符串会被Tomcat所解析&#xff0c;然后这些请求数据会被放到一个对象(request)里面保存。 相应的Tom…...

JMeter压测文件上传接口和中文乱码

一、压测文件上传接口 新建测试计划&#xff0c;然后添加需要的元件。 1、添加HTTP信息头管理器 可以在测试计划中添加&#xff0c;也可以在线程组里面添加。 我的接口使用到 token信息。这里在测试计划中添加。 2、添加线程组 上图解释&#xff1a;会在 2秒钟之内启动起来 5…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

简易版抽奖活动的设计技术方案

1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...