快排三种递归及其优化,非递归和三路划分

个人主页:Lei宝啊 

愿所有美好如期而遇


目录

快排简介:

快排的三种递归实现:

Hoare:

挖坑:

双指针:

小区间优化:

三数取中优化:

快排非递归实现:

快排的三路划分实现:


快排简介:

快速排序,参见: qsort详解及其模拟实现


快排的三种递归实现:

Hoare:

此法乃Hoare大佬所创,我等一个不注意便就掉入陷阱,大坑于代码处自有注释,诸君慢品:

//Hoare版本  right传数组下标
void QuickSort_Binary(int* arr, int left, int right)
{if (left >= right)return;//选定一个数keyi,最好是不大也不小,上面加个三数取中int keyi = left;//快排开始的区间,都是闭区间int begin = left;int end = right;while (begin < end){//一坑在=,若无此,循环不止//二坑在begin < end,若无此,有越界之忧,end或减减不止//三坑在要从右先行,以此保证begin与end相遇时//					二者所指处值小于keyi所指值while (begin < end && arr[end] >= arr[keyi]){end--;}while (begin < end && arr[begin] <= arr[keyi]){begin++;}Swap(&arr[end], &arr[begin]);}Swap(&arr[begin], &arr[keyi]);QuickSort_Binary(arr, left, begin - 1);QuickSort_Binary(arr, begin + 1, right);}

挖坑:

此法则无关左右矣

//挖坑法  传数组下标
void QuickSort_Binary(int* arr, int left, int right)
{if (left >= right)return;int hole = left;int temp = arr[left];//定义这两变量主要是为了区分后面递归时的区间int begin = left;int end = right;while(begin < end){while(begin < end && arr[end] >= temp){end--;}//交换爽啊,赋值的话循环结束后,还要把temp的值赋给hole位置Swap(&arr[hole], &arr[end]);hole = end;while (begin < end && arr[begin] <= temp){begin++;}Swap(&arr[hole], &arr[begin]);hole = begin;}QuickSort_Binary(arr, left, begin - 1);QuickSort_Binary(arr, begin + 1, right);
}

双指针:

//双指针法 传数组下标
void QuickSort_Binary(int* arr, int left, int right)
{if (left >= right)return;int temp = arr[left];int prev = left;int cur = left;while (cur <= right){while (arr[cur] < temp  && ++prev != cur){Swap(&arr[prev], &arr[cur]);}cur++;}//想法大致都是keyi位置的值不动,从下一个位置开始,最后交换keyi位置和停止位置//停止位置的值一定比keyi位置的值要小Swap(&arr[left], &arr[prev]);QuickSort_Binary(arr, left, prev - 1);QuickSort_Binary(arr, prev + 1, right);}

小区间优化:

我们可以发现的是,递归像一座金字塔,越是到下面,递归次数越多,而我们通过计算得知,一颗满二叉树节点数为2^n-1,最后一层节点数为2^(n-1),也就是说,最后三层节点数占到总数的近87.5%,

也就是说,剩余的节点小于15就不要递归了,可以使用插入排序,这个还是比较好的,插入排序参见:插入排序与希尔排序

以Hoare大佬的排序为例:

//Hoare版本  right传数组下标
void QuickSort_Binary(int* arr, int left, int right)
{if (left >= right)return;if(right-left+1 >= 15){//选定一个数keyi,最好是不大也不小,上面加个三数取中int keyi = left;//快排开始的区间,都是闭区间int begin = left;int end = right;while (begin < end){//一坑在=,若无此,循环不止//二坑在begin < end,若无此,有越界之忧,end或减减不止//三坑在要从右先行,以此保证begin与end相遇时//					二者所指处值小于keyi所指值while (begin < end && arr[end] >= arr[keyi]){end--;}while (begin < end && arr[begin] <= arr[keyi]){begin++;}Swap(&arr[end], &arr[begin]);}Swap(&arr[begin], &arr[keyi]);QuickSort_Binary(arr, left, begin - 1);QuickSort_Binary(arr, begin + 1, right);}else{InsertSort(arr,right-left+1);}
}

三数取中优化:

再一个,如果说一个序列已然有序,我们再使用快排就很难受,此时时间复杂度直达O(N^2),所以如果我们加上三数取中,就不会出现最坏情况,但是力扣老贼针对快排,快排的三数取中我们仍要修改,改为随机数取中。

int GetMidNum(int* a, int left, int right)
{int mid = left + (rand() % (right - left));if (a[left] > a[mid]){if (a[mid] > a[right]){return mid;}else if (a[left] > a[right]){return right;}else{return left;}}else{if (a[left] > a[right]){return left;}else if (a[mid] > a[right]){return right;}else{return mid;}}
}

这样我们返回这个中间值坐标后,这样做:

int mid = GetMidNum(arr, left, right);
Swap(&arr[left], &arr[mid]);

 

快排非递归实现:

快排掌握递归并不够,虽然说他的空间复杂度不高,尽管我们有了上述优化,但是仍然难以保证他不会爆栈,所以掌握非递归还是很有必要的。

快速排序的非递归类似于二叉树的前序遍历,我们在这里需要借助于栈,当然队列也可,但是这样的话就类似于二叉树的层序遍历了。

栈和队列参考:栈和队列的实现

二叉树的前序遍历参考:二叉树的几个递归问题

二叉树的层序参考:二叉树的层序遍历及判断完全二叉树

void QuickSortNonR(int* a, int left, int right)
{Stack stack;Init(&stack);Push(&stack, right - 1);Push(&stack, left);while (!Empty(&stack)){int begin = GetTop(&stack);Pop(&stack);int end = GetTop(&stack);Pop(&stack);int mid = SortWay_two(a, begin, end);if (mid + 1 < end){Push(&stack, end);Push(&stack, mid + 1);			}if (begin < mid){Push(&stack, mid);Push(&stack, begin);		}}
}

这里注意栈的特性是先进后出。 

快排的三路划分实现:

在力扣的针对下,有大佬推出了这个算法,使得快排终于能够通过。

我们的快排是大等于或小等于,而三路划分是小的在左,相等于keyi的在中间,大的在右,使得我们直接递归相等数的左边和右边就可。

//快排三路划分
void QuickSort_ThrDiv(int *arr,int left,int right)
{if (left >= right)return;srand((unsigned int)time(NULL));int mid = GetMidNum(arr, left, right);Swap(&arr[left], &arr[mid]);int begin = left;int end = right;int keyi = arr[left];int cur = left + 1;	while (cur <= right){if (arr[cur] < keyi){Swap(&arr[cur], &arr[left]);left++;cur++;}else if (arr[cur] > keyi){Swap(&arr[cur], &arr[right]);right--;}else{cur++;}}QuickSort_ThrDiv(arr, begin, left - 1);QuickSort_ThrDiv(arr, right + 1, end);}

今晚的风,吹得好浪漫~

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

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

相关文章

axios和fetch的区别

axios和fetch都是用于发起HTTP请求的工具&#xff0c;但是它们有一些区别&#xff1a; 语法和用法&#xff1a;axios是一个基于Promise的HTTP客户端&#xff0c;具有更简洁和直观的语法&#xff0c;可以方便地发送GET、POST、PUT等各种请求&#xff0c;并提供了更多的请求配置选…

Ubuntu Linux下安装 TensorFlow等开发环境

1. 安装基本工具 sudo apt-get install build-essential sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev…

基于PSO算法的功率角摆动曲线优化研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【设计模式】五、原型模式

文章目录 概述示例传统的方式的优缺点原型模式原理结构图-uml 类图 原型模式解决克隆羊问题的应用实例Sheep类实现clone()运行原型模式在 Spring 框架中源码分析 深入讨论-浅拷贝和深拷贝浅拷贝的介绍 小结 概述 示例 克隆羊问题 现在有一只羊 tom&#xff0c;姓名为: tom, 年…

Java基础面试,什么是面向对象,谈谈你对面向对象的理解

前言 马上就要找工作了&#xff0c;从今天开始一天准备1~2道面试题&#xff0c;来打基础&#xff0c;就从Java基础开始吧。 什么是面向对象&#xff0c;谈谈你对面向对象的理解&#xff1f; 谈到面向对象&#xff0c;那就不得不谈到面向过程。面向过程更加注重的是完成一个任…

V4L2 驱动架构介绍

V4L2 简介 Video for Linux two(Video4Linux2)简称 V4L2&#xff0c;是 V4L 的改进版。V4L2 是 linux操作系统下用于视频和音频数据采集设备的驱动框架&#xff0c;为驱动和应用程序提供了一套统一的接口规范。 在 Linux 下&#xff0c;所有外设都被看成一种特殊的文件&#xf…

freertos简介与移植

freertos是一个可裁剪的小型rtos系统&#xff0c;特点&#xff1a; 支持抢占式&#xff0c;合作式和时间片调度saferos衍生自freertos&#xff0c;更完整提供了一个用于低功耗的tickless模式系统的组件在创建时可以选择动态或者静态的ram&#xff0c;例如任务&#xff0c;消息…

MARS: An Instance-aware, Modular and Realistic Simulator for Autonomous Driving

● MARS: An Instance-aware, Modular and Realistic Simulator for Autonomous Driving&#xff08;基于神经辐射场的自动驾驶仿真器&#xff09; ● https://github.com/OPEN-AIR-SUN/mars ● https://arxiv.org/pdf/2307.15058.pdf ● https://mp.weixin.qq.com/s/6Ion_DZGJ…

【新版】系统架构设计师 - 层次式架构设计理论与实践

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 架构 - 层次式架构设计理论与实践考点摘要层次式体系结构概述表现层框架设计MVC模式MVP模式MVVM模式使用XML设计表现层表现层中UIP设计思想 中间层架构设计业务逻辑层工作流设计业务逻辑层设计 数据访问层…

代码随想录算法训练营第五十一天 |309.最佳买卖股票时机含冷冻期、714.买卖股票的最佳时机含手续费、总结

一、309.最佳买卖股票时机含冷冻期 题目链接/文章讲解&#xff1a;代码随想录 视频讲解&#xff1a;动态规划来决定最佳时机&#xff0c;这次有冷冻期&#xff01;| LeetCode&#xff1a;309.买卖股票的最佳时机含冷冻期_哔哩哔哩_bilibili 思考&#xff1a; 1.确定dp数组&…

【改造中序遍历】230. 二叉搜索树中第K小的元素

230. 二叉搜索树中第K小的元素 解题思路 改造中序遍历算法针对中序遍历 每次遍历到一个节点的时候 rank 表示当前遍历元素的顺序 同时记录当前遍历节点的值 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* Tree…

怎样给Ubuntu系统安装vmware-tools

首先我要告诉你&#xff1a;Ubuntu无法安装vmware-tools&#xff0c;之所以这么些是因为我一开始也是这样认为的&#xff0c;vmware-tools是给Windows系统准备的我认为&#xff0c;毕竟Windows占有率远远高于Linux&#xff0c;这也可以理解。 那么怎么样实现Ubuntu虚拟机跟Wind…

MySQL SQL性能分析(SQL优化 一)

在开发和维护数据库应用程序时&#xff0c;优化SQL查询的性能是至关重要的。MySQL提供了一些强大的工具和技术&#xff0c;帮助我们进行SQL性能分析&#xff0c;找出潜在的瓶颈并进行相应的优化。 查看SQL的执行频率 show [ session| global ] status 命令查看服务器状态信息…

Android Live Edit 给 Android开发者带来的福音

Android Live Edit 是一个允许开发者实时更新模拟器和物理设备中的可组合内容的功能。 微信公众号【biglead】的每日提醒 随时随记 每日积累 此功能最大限度地减少了编写和构建应用程序之间的上下文切换&#xff0c;让开发者专注于编写代码更长时间而不会中断。 在AndroidStu…

【VIM】VIm初步使用

玩转Vim-从放弃到入门_哔哩哔哩_bilibili

Qt中INI 配置文件的读写操作

int文件对于大多数人来说应该不陌生&#xff0c;一般大型的软件中有些配置文件使用的是ini格式文件。Qt也提供了ini文件的创建&#xff0c;元素的增加和删除获取等功能。 ini中的数据一般包含组&#xff0c;组中包含的key和value值。 在Qt中使用的是类QSettings&#xff0c;这…

传输层协议—UDP协议

传输层协议—UDP协议 文章目录 传输层协议—UDP协议传输层再谈端口号端口号范围划分pidofnetstat UDP协议端格式UDP报文UDP特点UDP缓冲区基于UDP的应用层协议 传输层 在学习HTTP/HTTPS等应用层协议时&#xff0c;为了方便理解&#xff0c;可以简单认为HTTP将请求和响应直接发送…

【CV】各种库安装报错及解决办法

目录 1.Error&#xff1a;Cannot unpack file… 1.Error&#xff1a;Cannot unpack file… 使用命令pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn 包名安装 参考&#xff1a;解决Python使用pip安装库文件出现“Error&a…

643. 子数组最大平均数I(滑动窗口)

目录 一、题目 二、代码 一、题目 643. 子数组最大平均数 I - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution { public:double findMaxAverage(vector<int>& nums, int k) {double Average INT_MIN;double sum nums[0];int left 0, right 0…

oracle-使用PLSQL工具自行修改用户密码

1、使用PLSQL工具&#xff0c;输入用户名和原密码登录&#xff0c;如下图 2、登录后&#xff0c;在会话下拉菜单中找到”Change password..” 3、在跳出的窗口中配置新密码&#xff0c;修改完成后单击”确认”&#xff0c;后退出PLSQL 4、重新打开PLSQL&#xff0c;使用新密码登…