【排序算法】C语言实现随机快排,巨详细讲解

news2024/11/15 11:19:45

请添加图片描述

文章目录

  • 🚀前言
  • 🚀快排的核心过程partition(划分过程)
  • 🚀快排1.0
  • 🚀随机快速排序
  • 🚀稳定性

🚀前言

铁子们好啊!继续我们排序算法今天要讲的是快排,通常大家所说的快排都是指随机快速排序,这里阿辉会详细的讲快排及其优化以及复杂度和稳定性的分析,话不多说开始我们今天的学习吧!!!

🚀快排的核心过程partition(划分过程)

在整个快排的过程中,快排最为核心的过程就是划分过程

划分过程:就是给定一个数作为划分值,将待划分的数组分成小于划分值的部分放在数组左边、等于划分值的部分在中间和大于划分值的部分在右边(为了方便,下文阿辉就直接简称为小于区、等于区和大于区)

对于划分过程是怎么样的思路呢?
对于一个数组的划分,我们需要三个指针来控制整个划分过程
用指针i来控制整个数组的遍历,用指针left来管理小于区域,用指针right来管理大于区域
请添加图片描述
假设我们取3作为划分值,i从左向右遍历数组,要分三种情况:

  • i指向的元素小于划分值时,i指向的数要与left指向的数字交换,然后++i同时++left
  • i指向的元素大于划分值时,i指向的数要与right指向的数字交换,然后--righti不变
  • i指向的元素等于划分值时,仅有i自增

为什么这么设计呢?
left控制着小于区,在left左边的元素都属于小于区;right控制着大于区,在right的右边的元素都属于大于区,而等于区的左边界是left右边界是rightleftright自身以及他们俩之间的元素都属于等于区。
++left代表小于划分值的元素发货到小于区,--right代表大于划分值的元素发货到大于区。为什么发货到小于区++i因为i是从左向右遍历的,left的数换到i位置不需要再看一遍了,而发货到大于区的时候,right的数换到i位置还没有看过,还得再看一遍它该发货到哪个区域。
对于上面的数组划分完是下面这样的:
请添加图片描述
让数组这样划分成这样后,对于中间的等于区域就不需要在管了,因为就算排好序它也应该在这个位置,前面都是小于它的后面都是大于大于它的,你说它是不是该在这!!!
附上partition函数的代码:

void partition(int a[],int l,int r,int x){//划分函数
        //l是待划分区域左边界,r是待划分过程右边界,x是划分值
        int i = l;//遍历偏移量
        while(i <= end){//i>end时说明要划分的区域已经划分完成
            if(a[i] == x){//遇到等于区域数不用管,i直接遍历下一位置
                ++i;
            }else if(a[i] < x){//遇到小于区域数,发货到小于区域
                swap(a[i++],a[l++]);//l、i同时去到下一个位置
            }else{//遇到大于区域数发货到大于区域
                swap(a[i],a[r--]);//r同时去到下一位置
            }
        }
    }

划分过程是O(N)的时间复杂度,因为划分过程会遍历数组整个元素,跳出循环的条件是i>r,在每一次循环过程中都是i++或者--r,所以时间复杂度是O(N)

🚀快排1.0

有了上面的划分过程,就好办了,请出我们的老伙计——递归,当我们对于原数组等于区排好了,我们给原数组小于区域和大于区再次划分然后递归下去,递归到数组只有1个元素时数组天然有序作为我们的base case也就是递归的限制条件。
现在我们的重心就是要选定划分值,但是对于固定流程的程序,我们一定可以找到让这个程序最难受的数据状况,比如数组中只有1~9这9个元素,如果我们划分值选在数组最右的元素,对于下面这个数组
请添加图片描述
我们每次都会选到数组最大的元素,导致没有大于区域,而且一次递归只能排好一个位置的数,对于划分过程partition函数的时间复杂度是O(N),然后每次待排序数组递减,明显和冒泡、插入一个级别的O(N2)的时间复杂度,也就是说快排1.0的时间复杂度是O(N2)
代码:

  int begin,end;
   void quicksort(int a[],int l,int r){//快排主逻辑
       if(l >= r) return;//base case终止条件
       int x = a[r];//以数组最右元素作为划分值
       partition(a,l,r,x);//给数组根据随机出的划分值,做划分
       //用临时变量捕捉当前的边界,全局变量会被子递归过程更改
       int left = begin;
       int right = end;
       quicksort(a,l,left - 1);//左部分递归
       quicksort(a,right + 1,r);//右部分递归
   }
   void partition(int a[],int l,int r,int x){//划分函数
       //l是划分区域左边界,r是划分过程右边界
       begin = l;//全局变量记录等于区域左边界
       end = r;//全局变量记录等于区域右边界
       int i = l;//遍历偏移量
       while(i <= end){//i>end时说明要划分的区域已经划分完成
           if(a[i] == x){//遇到等于区域数不用管,i直接遍历下一位置
               ++i;
           }else if(a[i] < x){//遇到小于区域数,发货到小于区域
               swap(a[i++],a[begin++]);//begin、i同时去到下一个位置
           }else{//遇到大于区域数发货到大于区域
               swap(a[i],a[end--]);//end、i同时去到下一位置
           }
       }
   }

我们想要的是O(NlogN)的快排,可不是这个和三大最挫排序一样效率的排序,好,让我们见识一下全盛时期的快排吧!!!

🚀随机快速排序

其实随机快排就比上面的快排1.0只换了一行代码,就让快拍的时间复杂度达到了O(NlogN)
代码:

  int begin,end;
   void quicksort(vector<int>& a,int l,int r){//快排主逻辑
       if(l >= r) return;//base case终止条件
       int x = a[l + rand() % (r - l + 1)];//随机一个划分值
       partition(a,l,r,x);//给数组根据随机出的划分值,做划分
       //用临时变量捕捉当前的边界,全局变量会被子递归过程更改
       int left = begin;
       int right = end;
       quicksort(a,l,left - 1);//左部分递归
       quicksort(a,right + 1,r);//右部分递归
   }
   void partition(vector<int>& a,int l,int r,int x){//划分函数
       //l是划分区域左边界,r是划分过程右边界
       begin = l;//全局变量记录等于区域左边界
       end = r;//全局变量记录等于区域右边界
       int i = l;//遍历偏移量
       while(i <= end){//i>end时说明要划分的区域已经划分完成
           if(a[i] == x){//遇到等于区域数不用管,i直接遍历下一位置
               ++i;
           }else if(a[i] < x){//遇到小于区域数,发货到小于区域
               swap(a[i++],a[begin++]);//begin、i同时去到下一个位置
           }else{//遇到大于区域数发货到大于区域
               swap(a[i],a[end--]);//end、i同时去到下一位置
           }
       }
   }

就改了对与划分值的选取,用了随机的方式,在数组中随机选取一个元素作为划分值,随机快速排序的时间复杂度是O(NlogN),这里是为什么呢,阿辉也只是记住,并不知道原理,这是数学家把每一种结果的概率都求出来,然后算出期望,随机快排的时间复杂度收敛于O(NlogN),在算法导论上有证明,感兴趣的小伙伴可以去研究

🚀稳定性

随机快排是没有稳定性的,换句话说是划分过程没有稳定性,比如:
请添加图片描述
对于这样的数组,i位置与right一换,最后位置的2一下子跨越他前面那么多2,所以快排没有稳定性,但是快排比归并以及堆排序都快,是常数时间上快


请添加图片描述

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

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

相关文章

❤搭建一个Springboot项目(ltbjava)

❤从0实现一个项目 搭建好我们的java环境和运行的IDEA软件以后&#xff0c;接下来我们就应该实现一个自己的项目了 0 项目描述 基于jdk17 的学习&#xff0c;因为据说最新的spring框架的最低要求是jdk17Maven 3.8.7PS&#xff1a;springboot3.0版本以上必须用jdk171、 项目创…

STM32F407移植OpenHarmony笔记6

继上一篇笔记&#xff0c;编译好STM32的裸机程序&#xff0c;能点亮LED灯了。 下一步就是启动liteos_m内核了。 不过为了更好的调试代码&#xff0c;需要先把printf重定向到串口&#xff0c;基于gcc的printf重定向和Keil不一样。 直接新建printf.c&#xff0c;在里面重写printf…

摄像头监控系统/视频监控云平台EasyCVR接入单兵设备后如何配置移动规矩

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体控可实现视频监控直播、视频轮播、视频录像…

C#学习笔记_类(Class)

类的定义 类的定义是以关键字 class 开始&#xff0c;后跟类的名称。类的主体&#xff0c;包含在一对花括号内。 语法格式如下&#xff1a; 访问标识符 class 类名 {//变量定义访问标识符 数据类型 变量名;访问标识符 数据类型 变量名;访问标识符 数据类型 变量名;......//方…

氢气泄漏检测仪使用方法:守护安全,从细节开始

随着科技的发展&#xff0c;我们的生活和工作环境中充满了各种潜在的危险。其中&#xff0c;氢气作为一种清洁能源&#xff0c;其使用日益广泛&#xff0c;但同时也带来了泄漏的风险。为了确保我们的安全&#xff0c;了解并正确使用氢气泄漏检测仪至关重要。下面将详细介绍氢气…

yum一直出现正在尝试其它镜像... 解决方法

更改yum镜像源地址为阿里云镜像即可 1. 去ail云yum镜像源官网 阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 (aliyun.com) 2. 选择自己对应的操作系统这里以centos7演示&#xff1a; 3. 先备份原yum配置文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/Ce…

OC源码 - FailureDetectionPeriodBlockMinutes参数解读

FailureDetectionPeriodBlockMinutes 看看官方文档中对该参数如何描述 orchestrator will detect failures to your topology, always. As a matter of configuration you may set the polling frequency and specific ways for orchestrator to notify you on such detectio…

Selenium处理Alert弹窗

页面弹窗有 3 种类型&#xff1a; alert&#xff08;警告信息&#xff09; confirm&#xff08;确认信息&#xff09; prompt&#xff08;提示输入&#xff09; 对于页面出现的 alert 弹窗&#xff0c;Selenium 提供如下方法&#xff1a; 序号 方法/属性 描述 1 ac…

【Delphi】IDE 工具栏错乱恢复

由于经常会在4K和2K显示器上切换Delphi开发环境(IDE)&#xff0c;导致IDE工具栏错乱&#xff0c;咋样设置都无法恢复&#xff0c;后来看到红鱼儿的博客&#xff0c;说是通过操作注册表的方法&#xff0c;能解决&#xff0c;试了一下&#xff0c;果真好用&#xff0c;非常感谢分…

Linux split命令 切割文件

目录 一. 主要配置项二. 按照行数切割文件三. 按照指定大小切割文件 一. 主要配置项 ⏹将文件按照行数或者大小切割为若干份小文件&#xff0c;主要作用就是用来切割文件 -l&#xff1a;表示将文件按照行分割-d&#xff1a;表示使用数字作为分割后的文件名后缀, 而不是默认的…

java生成dll,并利用c语言使用libcurl调用http接口

本文可能需要使用的环境和工具&#xff1a; c/ c和GCC编译器 (Windows) Cygwin或MinGW 本文运行环境为windows10&#xff0c;使用MinGW-W64-builds-4.2.0 curl-8.5.0 libcurl 可以在官网 http://curl.haxx.se/ 获得。 配置MinGW 将mingw.rar解压到D:&#xff0c;修改系统…

软件压力测试:探究其目的与重要性

随着软件应用在各行各业中的广泛应用&#xff0c;确保软件在高负载和极端条件下的稳定性变得至关重要。软件压力测试是一种验证系统在不同负载条件下的性能和稳定性的方法。本文将介绍软件压力测试的目的以及为什么它对软件开发和部署过程至关重要。 验证系统性能的极限&#x…

C#: 软件任务栏托盘图标添加关闭软件菜单等

说明&#xff1a;在软件在任务栏右下角的系统托盘的图标添加个右键弹出菜单功能&#xff0c;案例实现右键弹窗菜单关闭软件功能。 1.添加系统托盘图标控件 NotifyIcon 2.右键打开控件属性菜单添加鼠标点击事件函数 3.事件函数添加代码 //右键点击任务栏图标弹出关闭菜单 priv…

python爬虫之豆瓣首页图片爬取

网址&#xff1a;https://movie.douban.com/ import requests from lxml import etree import re url https://movie.douban.com headers {User-Agent : Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.289 Safari/5…

【Java-JDK】JDK 的安装与环境变量的配置:Windows Linux

【Java-JDK】JDK的安装与环境变量的配置&#xff1a;Windows & Linux 1&#xff09;Windows安装JDK1.1.下载JDK1.1.安装JDK1.2.JDK环境配置1.3.验证环境变量是否配置成功 2&#xff09;Linux安装JDK2.1.下载JDK2.2.安装JDK2.3.JDK环境配置2.4.验证环境变量配置是否成功 1&a…

OpenFeign认证上下文信息的传递

基本思路 其中网关部分不是本章讨论的范围,网关处理与后续服务的处理类似。 发送处理:将认证信息植入到请求信息中接收处理:从请求头中获取到认证信息,并解析为用户信息,供后续业务使用。有两个思路: 将认证信息放到请求头中,向下传递。这种方式适用于用户认证上下文中信…

githacker安装详细教程,linux添加环境变量详细教程(见标题三)

笔者是ctf小白&#xff0c;这两天也是遇到.git泄露的题目&#xff0c;需要工具来解决问题&#xff0c;在下载和使用的过程中也是遇到很多问题&#xff0c;写此篇记录经验&#xff0c;以供学习 在本篇标题三中有详细介绍了Linux系统添加环境变量的操作教程&#xff0c;以供学习 …

242. 有效的字母异位词(力扣)(C语言题解)

✨欢迎来到脑子不好的小菜鸟的文章✨ &#x1f388;创作不易&#xff0c;麻烦点点赞哦&#x1f388; 所属专栏&#xff1a;刷题 我的主页&#xff1a;脑子不好的小菜鸟 文章特点&#xff1a;关键点和步骤讲解放在 代码相应位置 前提&#xff1a; 看本文章之前&#xff0c;建…

【期末】openGL基础知识+编程题

头文件的使用 若应用程序使用OpenGL核心函数&#xff0c;应包括头文件<gl/gl.h> 使用GLU库函数&#xff0c;应包括头文件<gl/glu.h> 使用AUX库函数&#xff0c;应包括头文件<gl/glaux.h> 使用WGL和Win32应包括头文件<windows.h>基本程序结构 1.定…

uniCloud快速上手

uniCloud快速上手 hello uniCloud Hello uniCloud&#xff0c;是一个示例&#xff0c;演示了 uniCloud 的各种能力。 体验示例 这个示例目前只发布了h5版本和Android app版。 Hello uniCloud部署了2套&#xff0c;分别连接uniCloud的阿里云版和腾讯云版。 h5版地址&#x…