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

个人主页: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.xdnf.cn/news/147007.html

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

相关文章

计算机网络(一):概述

参考引用 计算机网络微课堂-湖科大教书匠计算机网络&#xff08;第7版&#xff09;-谢希仁 1. 计算机网络在信息时代的作用 计算机网络已由一种通信基础设施发展成为一种重要的信息服务基础设施计算机网络已经像水、电、煤气这些基础设施一样&#xff0c;成为我们生活中不可或…

《CTFshow-Web入门》10. Web 91~110

Web 入门 索引web91题解总结 web92题解总结 web93题解 web94题解 web95题解 web96题解 web97题解 web98题解 web99题解总结 web100题解 web101题解 web102题解 web103题解 web104题解 web105题解总结 web106题解 web107题解 web108题解 web109题解 web110题解 ctf - web入门 索…

红外遥控器 数据格式,按下及松开判断

红外遥控是一种无线、非接触控制技术&#xff0c;具有抗干扰能力强&#xff0c;信息传输可靠&#xff0c;功耗低&#xff0c;成本低&#xff0c;易实现等显著优点&#xff0c;被诸多电子设备特别是家用电器广泛采用&#xff0c;并越来越多的应用到计算机系统中。 同类产品的红…

iOS 视频压缩 mov转mp4 码率

最近还是因为IM模块的功能&#xff0c;IOS录制MOV视频发送后&#xff0c;安卓端无法播放&#xff0c;迫不得已兼容将MOV视频转为MP4发送。 其中mov视频包括4K/24FPS、4K/30FPS、4K/60FPS、720p HD/30FPS、1080p HD/30FPS、1080p HD/60FPS&#xff01; 使用AVAssetExportSessi…

使用sqlmap获取数据步骤

文章目录 1.使用sqlmap获取所有数据库2.使用sqlmap获取当前连接数据库3.使用sqlmap获取当前数据库下所有表名4.使用sqlmap获取当前数据库下某个表下所有列名5.使用sqlmap获取当前数据库下某个表下指定字段的数据6.测试当前用户是否是管理员7.使用burpsqlmap批量检测8.脱库命令9…

zkLogin构建者的最佳实践和业务思考

随着zkLogin在Sui主网上线&#xff0c;构建者可以开始为其应用程序提供丝滑的帐户创建服务。与任何新技术集成一样&#xff0c;构建者需要考虑许多重要的问题&#xff0c;以降低风险并成功优化。 本文概述了其中一些考虑因素&#xff0c;并突出了zkLogin文档中提供的明确指导。…

二、机器学习基础知识:Python数据处理基础

文章目录 1、基本数据类型1.1 数字类型&#xff08;Number&#xff09;1.2 字符串类型&#xff08;String&#xff09;1.3 列表类型&#xff08;List&#xff09;1.4 元组类型&#xff08;Tuple&#xff09;1.5 字典类型&#xff08;Dictionary&#xff09;1.6 集合类型&#x…

掌动智能:替代JMeter的压力测试工具有哪些

JMeter是一个广泛使用的开源压力测试工具&#xff0c;但在实际应用中&#xff0c;也有一些其他优秀的替代品可供选择。本文将介绍几个可替代JMeter的压力测试工具&#xff0c;它们在功能、性能和易用性方面都具有独特优势&#xff0c;可以满足不同压力测试需求的选择。 一、Gat…

lvgl不能显示图片,但可以显示按键?

AT32F403A, IAR, ST7735S, LVGL8.3。 一、现象&#xff1a; 本来想着用LVGL做一个摄像头的显示功能 切换动态。可是死活实现不了功能。 因为移植好LVGL后&#xff0c;首先测试了显示按键&#xff0c;功能正常&#xff0c;以为是一切正常。在模拟器上调试效果完成后&#xf…

Scala第十三章节

Scala第十三章节 1. 高阶函数介绍 2. 作为值的函数 3. 匿名函数 4. 柯里化 5. 闭包 6. 控制抽象 7. 案例: 计算器 scala总目录 文档资料下载

IDEA git操作技巧大全,持续更新中

作者简介 目录 1.创建新项目 2.推拉代码 3.状态标识 5.cherry pick 6.revert 7.squash 8.版本回退 9.合并冲突 1.创建新项目 首先我们在GitHub上创建一个新的项目&#xff0c;然后将这个空项目拉到本地&#xff0c;在本地搭建起一个maven项目的骨架再推上去&#xff0…

Python集成开发环境(IDE):WingPro for Mac

WingPro for Mac是一款Python集成开发环境&#xff08;IDE&#xff09;软件&#xff0c;它提供了一系列强大的工具和功能&#xff0c;帮助Python开发人员提高开发效率和质量。 WingPro for Mac拥有直观的用户界面和强大的调试器&#xff0c;可以帮助用户快速定位问题和修复错误…

12链表-双指针

目录 LeetCode之路——21. 合并两个有序链表 分析&#xff1a; LeetCode之路——19. 删除链表的倒数第 N 个结点 分析&#xff1a; LeetCode之路——21. 合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的…

宝塔反代openai官方API接口详细教程,502 Bad Gateway问题解决

一、前言 宝塔反代openai官方API接口详细教程&#xff0c;实现国内使用ChatGPT502 Bad Gateway问题解决&#xff0c; 此方法最简单快捷&#xff0c;没有复杂步骤&#xff0c;不容易出错&#xff0c;即最简单&#xff0c;零代码、零部署的方法。 二、实现前提 一台海外VPS服务…

网络安全——自学(黑客)方法

如果你想自学网络安全&#xff0c;首先你必须了解什么是网络安全&#xff01;&#xff0c;什么是黑客&#xff01;&#xff01; 1.无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防两面性&#xff0c;例如 Web 安全技术&#xff0c;既有 Web 渗透2.也有 Web 防…

云原生之使用Docker部署RSS阅读器Huntly

云原生之使用Docker部署RSS阅读器Huntly 一、Huntly介绍1.1 Huntly简介1.2 Huntly功能 二、本次实践规划2.1 本地环境规划2.2 本次实践介绍 三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本 四、下载Huntly镜像五、部署Huntly5.1 创建挂…

Qt自定义菜单

Qt开发过程中&#xff0c;弹出菜单时我们一般使用QMenu,但是QMenu都是一条项固定的格式&#xff0c;如查想要自己的设计界面就没法使用默认的Action项了&#xff0c;因此我们得用自定义的QMenu。 本篇介绍使用自定义的QMenu设计出UI。我们使用QWidget QWidgetAction来实现。Q…

此芯科技加入百度飞桨硬件生态共创计划,加速端侧AI生态布局

近日&#xff0c;此芯科技&#xff08;上海&#xff09;有限公司&#xff08;以下简称“此芯科技”&#xff09;与百度签署硬件生态共创计划合作协议&#xff0c;正式加入由百度发起的硬件生态共创计划。双方将共同推动端侧AI和大模型在个人计算、车载计算以及元宇宙计算等领域…

拒绝水文!八大排序(二)【适合初学者】冒泡排序和选择排序

文章目录 冒泡排序选择排序 大家好&#xff0c;我是纪宁。 这篇文章介绍八大排序中思路最简单&#xff0c;但效率也是最低的两种排序算法&#xff01; 冒泡排序 冒泡排序&#xff0c;可以说是每个人在接触编程时最先学会的一种排序。 冒泡排序基本思想 冒泡排序&#xff08;…

Rust 围炉札记

文章目录 一、安装二、文档 一、安装 rust 安装 卸载&#xff08;自定义路径&#xff09; Rust配置镜像源 Rust in Visual Studio Code Rust 官网 Rust 中文网 windows系统下Rust环境搭建以及vscode调试环境配置 二、文档 Rust 菜鸟教程 Rust 官方文档中文教程 Rust 包管理…