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

请添加图片描述

文章目录

  • 🚀前言
  • 🚀快排的核心过程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.hqwc.cn/news/442909.html

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

相关文章

nop-entropy可逆计算入门(1)

第1步&#xff1a;从大佬的gitee&#xff1a;https://gitee.com/canonical-entropy/nop-entropy下载源码&#xff0c;进行本地编译&#xff0c;具体编译看项目下的readme,想偷懒的可以下载我编译后的jar&#xff0c;放到自己的maven仓库 https://pan.baidu.com/s/15qANnrCh5RV…

C语言字符串操作函数详解①strlen函数的讲解与三种模拟实现方法(建议三连收藏)

目录 ​编辑 前言 1.strlen函数介绍 2.模拟实现strlen 2.1计数器做法 2.2不创建临时变量&#xff0c;通过递归的方法 2.3利用两个指针相减 3.结语 前言 C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没有字符串类型的&#xff0c;字符串通常放在常量…

Linux文本三剑客-grep

1.grep简介&#xff1a; grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具&#xff0c;它能使用正则表达式搜索文本&#xff0c;并把匹配的行打印出来&#xff0c;都是按行处理的。 grep 最主要…

Linux内核源码

记得看目录哦&#xff01; 1. 为什么要阅读Linux内核2. Linux0.01内核源码3. 阅读linux内核源码技巧4. linux升级内核5. linux的备份和恢复5.1 安装dump和restore5.2 使用dump完成备份5.3 使用restore完成恢复 1. 为什么要阅读Linux内核 2. Linux0.01内核源码 3. 阅读linux内核…

基于Java SSM框架实现影院购票系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现影院购票系统演示 摘要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#…

人工智能与机器学习——开启智能时代的里程碑

写在前面 前言人工智能与机器学习的概述监督学习、无监督学习和强化学习的基本原理监督学习&#xff1a;无监督学习&#xff1a;强化学习&#xff1a; 机器学习的算法和方法常见的机器学习算法和方法线性回归&#xff1a;决策树&#xff1a;支持向量机&#xff1a;神经网络&…

React、React Router、JSX 简单入门快速上手

React、React Router、JSX 简单入门快速上手 介绍特点 JSX使用js表达式渲染列表样式控制注意事项 入门脚手架创建react项目安装目录介绍入口文件解析 组件解析介绍函数式组件类组件 事件绑定注意点定义使用事件对象事件处理函数接收额外参数 组件状态状态的定义使用 组件通信父…

dp优化----单调队列解决定长连续区间最小值。 例题:Cashback--- 题解

E. Cashback 题目大意&#xff1a; 思路解析&#xff1a; 如果c1&#xff0c;那么无论如何 答案都为0. 如果c!1&#xff0c;我们考虑如果最优答案有一段区间长度小于c&#xff0c;那么它对答案的贡献值&#xff0c;等于将这个区间划分为长度为1的多个小段&#xff0c; 如果有…

【Midjourney】关于标准模型的几个按钮都有什么用

当用户在Midjourney Bot所在的服务发送/settings命令时就能调出设置窗口&#xff0c;本文将介绍该窗口中的各个按钮都有什么作用。 1.RAW Mode 依照官方的描述来看V5.2模型似乎带有自动优化功能&#xff0c;会对用户输入的关键词空白描述进行补全和优化&#xff0c;以便修复所…

与音乐共同奔跑:南卡/韶音/墨觉三款骨传导耳机的真实试用体验

作为一个热爱运动的音乐迷&#xff0c;跑步与听歌对我来说是一场每天必须的灵魂邂逅。没有音乐&#xff0c;我跑步的脚步就仿佛失去了节奏&#xff0c;每一步都沉重异常&#xff1b;有了音乐&#xff0c;每一次呼吸都充满了动力&#xff0c;仿佛我能一跃而过山丘&#xff0c;跃…

【日常总结】如何快速迁移Navicat中的全部连接设置到新安装的Navicat中?

一、场景 二、需求 三、解决方案 Stage 1&#xff1a;“文件”-->“导出连接”。 Stage 2&#xff1a;获取备份文件 connections.ncx Stage 3&#xff1a;导入connections.ncx 四、不足 一、场景 公司电脑换新&#xff0c;所有软件需要重装&#xff0c;包括navicat 1…

机器学习 | 掌握线性回归的实战技巧

目录 初识线性回归 损失和优化 欠拟合与过拟合 正则化线性模型 模型的保存与加载 初识线性回归 线性回归(Linearregression)是利用回归方程(函数)对一个或多个自变量(特征值)和因变量(目标值)之间关系进行建模的一种分析方式。特点是&#xff1a;有一个自变量的情况称为单…