数据结构——快速排序的三种方法和非递归实现快速排序

数据结构——快速排序的三种方法和非递归实现快速排序(升序)

  • 快速排序的单趟排序
    • hoare法
    • 挖坑法
    • 前后指针法
  • 快速排序的实现
    • key基准值的选取
    • 快速排序代码
    • 快速排序的优化
  • 快速排序(非递归)

快速排序的单趟排序

hoare法

思路:从给定数组中选一个基准值key,然后定义两个下标L和R,分别从数组的两端开始向中间遍历,lL找大,R找小。R找到比key小的数就停下,L找到比key大的数就停下,然后交换两个数的值;接下来继续遍历,直到L和R相遇,按此规律你会发现相遇位置的值一定是小于等于key的,此时交换相遇位置和key的值。最后返回基准值的位置。
详细看下面的动图:
在这里插入图片描述
代码:

//单趟排序结束后将key对应得下标keyi返回,方便下一次递归得调用
int PartSort1(int* a, int left, int right)
{//三数取中选key,这里其实就是在数组中选取了一个较为中间的值作为key然后放在了数组得最左边//后面会讲为什么int keyi = GetMidNum(a, left, right);if (keyi != left){Swap(&a[keyi], &a[left]);keyi = left;}while (left < right){//key在左边右边先走   最后相遇的位置一定是比key小的或者等于key的//右边找比key小的while (left < right && a[right] >= a[keyi])//如果为顺序结构right会一直自减到-1,所以加left < right控制以一下{right--;}//左边找比key大的while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[right], &a[left]);//找到后交换}Swap(&a[keyi], &a[left]);//此时left为相遇的位置,相遇的位置小于key然后交换key和相遇的位置的值//此时key的左边都是比key小的 右边都是比key大的return left;//此时left为keyi的位置
}

挖坑法

思路:选一个基准值放在最左边,然后将将坑也放在最左边,然后将基准值放在临时变量key中形成坑位a[hole],然后从左右两边向中间遍历,右边先遍历,找到比key小的就停下来,将该数据放在坑的位置,形成新的坑位;然后左边遍历,找到比key大的数就停下来,将该数据放在坑的位置,形成新的坑位。最后将坑的位置返回。
在这里插入图片描述
代码实现:

int PartSort2(int* a, int left, int right)//(挖坑法)
{int keyi = GetMidNum(a, left, right);//三数取中拿到基准值的下标int key = a[keyi];//将基准值放在key中,形成坑位//判断基准值是否在左边,若不在放在左边,也就是坑位放在最左边if (keyi != left){Swap(&a[keyi], &a[left]);//基准值放左边}int hole = left;//最初将坑放在左边while (left < right){while (left<right && a[right] >= key){right--;}a[hole] = a[right];hole = right;//坑的位置移到找到的比key小的那个位置while (left < right && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}

前后指针法

思路:从数组中选定一个基准值,放在数组的最左边。定义两个指针prev和cur,prev指向第一个数的位置,cur指向第二个数的位置,然后cur开始向后遍历,如果cur所指向的位置小于key那么,prev和cur同时++(自增1),也就是prev和cur同时向后遍历;如果cur所指向的位置大于key的话,cur向后遍历,prev不遍历,直到cur找到比key小的数,然后向后遍历一位,再交换此时cur和prev所指向位置的数,也就是保证prev在遍历的时候,遍历过的位置都是比key小的数,所以遍历结束后prev所指向的位置以及prev遍历过的位置(prev的左边)都是比key小的值,接下来交换key和prev所指向的数即可,并返回交换后key的下标。动图:
在这里插入图片描述
代码实现:

int PartSort3(int* a, int left, int right)//(前后指针法 下标法)
{int keyi = GetMidNum(a, left, right);if (keyi != left){Swap(&a[keyi], &a[left]);keyi = left;}int prev = left;int cur = prev + 1;while (cur <= right){if (a[cur] < a[keyi] && ++prev != cur)//cur找到比a[keyi]小的值的时候,prev++,若prev和cur在同一位置那么就不需要交换{Swap(&a[cur], &a[prev]);//上面判断条件中prev++过了}cur++;//两中情况cur都需要++}Swap(&a[prev], &a[keyi]);//key和prev的值交换return prev;
}

快速排序的实现

函数形参分别为待排序数组a,待排序数组的最左边数的下标left,待排序数组的最右边数的下标right。

void QuickSort(int* a, int left, int right);

不管是哪种方法都是用递归来实现的,就用hoare法来举例分析吧。
第一趟排序完后,key的左边全为比key小的数,右边全为比key大的数。接下来把数组分为key左(即下标区间为[left,key-1]的数组)和key右(下标区间为[key+1,right]的数组)两组分别进行单趟排序,然后不断递归,当递归到数组中只有一个值的时候,也就是left>=right的时候,这时候直接返回即可。
d在这里插入图片描述

key基准值的选取

一般我们会取最左边的数为基准值,这样容易理解但是,这样有时候会使递归深度很大,所以我们会选取较为中间的数来当基准值,用三数取中法来解决该问题。
递归深度对比图:
在这里插入图片描述
三数取中法:选取数组最左边最右边和中间这三个数,然后返回三数中间大小的下标。
代码实现:

int GetMidNum(int* a, int left, int right)//三个数取中间值  返回中间值的下标
{int midi = (left + right) / 2;if (a[left] < a[midi]){if (a[midi] < a[right]){return midi;//a[left[<a[midi]<a[right]}else if (a[right] < a[left]){return left;//a[right]<a[left]<a[midi]}else//a[right]>=a[left] && a[right]<=a[midi]{return right;//a[left]<a[right]<a[midi]}}else// a[left] >= a[midi]{if (a[midi] > a[right]){return midi;//a[left[<a[midi]<a[right]}else if (a[right] > a[left]){return left;//a[right]<a[left]<a[midi]}else//a[right]>=a[left] && a[right]<=a[midi]{return right;//a[left]<a[right]<a[midi]}}
}

快速排序代码

void QuickSort(int* a, int left, int right)//快速排序
{if (left >= right)return ;int begin = left;int end = right;//int keyi = PartSort1(a, begin, end);//hoare法//int keyi = PartSort2(a, begin, end);//挖坑法int keyi = PartSort3(a, begin, end);//前后指针法QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

快速排序的优化

当快排递归到待排数组是一些短数组的时候,由于短数组的个数很多,再加上这些短数组都需要递归到数组中只有一个数的情况才可以返回,这时候会不断的创建函数栈帧,然后导致时间复杂度降低。因为这些待排的短数组都是一个接近于有序的的数组,用直接插入来优化更为合适。
代码实现:

void QuickSort(int* a, int left, int right)//快速排序
{if (left >= right)return ;int begin = left;int end = right;if (end - begin + 1 < 10){InsertSort(a, end - begin + 1);return;}//int keyi = PartSort1(a, begin, end);//hoare法int keyi = PartSort2(a, begin, end);//挖坑法//int keyi = PartSort3(a, begin, end);//前后指针法QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);

快速排序(非递归)

任何一个递归程序在执行的时候,都是操作系统底层开辟了一个栈,每执行一个函数调用,那么就将当前函数的栈帧压栈,如果当前函数内部存在递归调用,那么就继续压入新的该函数代表的栈帧,直到某个函数内部没有再进行递归调用,该函数计算完毕后,将其出栈,并将计算结果返回给下一层栈帧。所以我们可以尝试着自己手搓一个栈,来实现函数的非递归调用。

要点1:我们要明白函数栈桢中存放的是什么,存放的是数组区间
要点2:数组区间只有一个值或者不存在值得时候不入栈

思路:首先将数组得左右下标压入栈中,这里注意顺序,栈得性质得后入先出,所以先将right压入栈中,在将left压入栈中,接下来相当于创建了函数栈桢,然后开始调用函数,因为递归函数中是不断得调用同一个函数,但是函数栈桢中得数组区间不同,所以接下来要通过循环来反复调用单趟排序,并且在循环中重复定义数组区间得值,调用完单趟排序后将子区间压栈,要判断一下数组区间至少有两个元素。
代码实现:

void QuickSortNonR(int* a, int left, int right)//非递归
{ST st;STInit(&st);//初始化STPush(&st, right);//先插入right,栈是后入先出STPush(&st, left);while (!STEmpty(&st)){//每次循环重置数组区间int begin = STTop(&st);STPop(&st); int end = STTop(&st);STPop(&st);int keyi = PartSort3(a, begin, end);//先排序后半部分,栈是后入先出if (keyi + 1 < end)//数组区间有一个数或者没有数不压栈{//排序完该区间后将子区间压入栈当中去STPush(&st, end);STPush(&st, keyi+1);}if (begin < keyi - 1){STPush(&st, keyi-1);STPush(&st, begin);}}STDestory(&st);
}

大致图解:
这里没有考虑到三数取中选key,选取最左边的数为基准值进行的单趟排序
在这里插入图片描述

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

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

相关文章

后端前行Vue之路(二):模版语法之插值与指令

1.概述 Vue.js的模板语法是一种将Vue实例的数据绑定到HTML文档的方法。Vue的模板语法是一种基于HTML的扩展&#xff0c;允许开发者将Vue实例中的数据绑定到HTML元素&#xff0c;以及在HTML中使用一些简单的逻辑和指令。Vue.js 基于 HTML 的模板语法允许开发者声明式地将 DOM 绑…

Windows11系统缺少解决办法

一.缺少msvcp120.dll 下载Mircrosoft Visual C 2015等系统关键组件 Microsoft Visual C 2015-2022 Redistributable (x86) - 14.34.31931 Installation Error etc.. - Microsoft Q&A 二.缺少python27.dll 重新下载python2.7进行安装(选择Windows x86-64 MSI installer)…

三级等保建设技术方案-Word

1信息系统详细设计方案 1.1安全建设需求分析 1.1.1网络结构安全 1.1.2边界安全风险与需求分析 1.1.3运维风险需求分析 1.1.4关键服务器管理风险分析 1.1.5关键服务器用户操作管理风险分析 1.1.6数据库敏感数据运维风险分析 1.1.7“人机”运维操作行为风险综合分析 1.2…

IP如何异地共享文件?

【天联】 组网由于操作简单、跨平台应用、无网络要求、独创的安全加速方案等原因&#xff0c;被几十万用户广泛应用&#xff0c;解决了各行业客户的远程连接需求。采用穿透技术&#xff0c;简单易用&#xff0c;不需要在硬件设备中端口映射即可实现远程访问。 异地共享文件 在…

腾讯云2核2G服务器CVM S5和轻量应用服务器优惠价格

腾讯云2核2G服务器多少钱一年&#xff1f;轻量服务器61元一年&#xff0c;CVM 2核2G S5服务器313.2元15个月&#xff0c;腾讯云2核2G服务器优惠活动 txyfwq.com/go/txy 链接打开如下图&#xff1a; 腾讯云2核2G服务器价格 轻量61元一年&#xff1a;轻量2核2G3M、3M带宽、200GB月…

AXI Memory Mapped to PCI Express 学习笔记(五)—— Test Bench

本文包含有关Vivado Design Suite环境中提供的测试平台&#xff08;Test Bench&#xff09;的信息。 一、Endpoint的Root Port模型测试平台 PCI Express Root Port Model是一个强大的测试平台环境&#xff0c;它提供了一个测试程序接口&#xff0c;可以与提供的PIO设计&#…

洛谷_P4995 跳跳!_python写法

P4995 跳跳&#xff01; - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) n int(input()) data list(map(int,input().split())) data.append(0) data.sort()sum 0 l 0 r len(data)-1 flag 1 while l<r:sum (data[l]-data[r])**2if flag:l 1flag 0else:r - 1flag 1…

LinkedList讲解指南

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE相关知识点了&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好…

教育数字化调研团走进锐捷,共议职业教育数字化转型新思路

为贯彻落实国家教育数字化战略行动部署和2024年全国教育工作会议精神,加快推进职业教育数字化转型与发展,梳理职业教育数字化转型的现状、问题及发展趋势,并总结展示职业教育数字化转型的好经验、好做法,培育职业教育数字化创新成果,推动数字技术与职业教育深度融合、提高数字化…

ensp的PPP实验报告

实验要求&#xff1a; 1、R1和R2使用PPP链路直连&#xff0c;R2和R3把2条PPP链路捆绑为PPP MP直连 2、按照图示配置IP地址 3、R2对R1的PPP进行单向chap验证 4、R2和R3的PPP进行双向chap验证 1、配置ip地址 R1&#xff1a; [R1] int Serial 3/0/0 [Rl-Seria13/0/0] ip add 192…

机器学习——聚类算法-DBSCAN

机器学习——聚类算法-DBSCAN DBSCAN&#xff08;Density-Based Spatial Clustering of Applications with Noise&#xff09;是一种基于密度的聚类算法&#xff0c;可以发现任意形状的簇&#xff0c;并能有效处理噪声数据。本文将介绍DBSCAN算法的核心概念、算法流程、优缺点…

Kali远程操纵win7

一.准备 1.介绍 攻击方&#xff1a;kali IPV4:192.168.92.133 被攻击方&#xff1a;win7 IPV4:192.168.92.130 2.使用永恒之蓝漏洞 (1.使用root权限 (2.进入msfconsole (3.添加rhosts (4.run进行一下 二.进行远程操作 1.获取用户名和密码 在cmd5查询 2.获取syste…

抓包工具charles修改请求和返回数据

数据篡改的主要使用场景&#xff1a; &#xff08;1&#xff09;mock场景&#xff0c;mock入参和返回值参数&#xff0c;实现mock测试 &#xff08;2&#xff09;安全测试&#xff0c;对于支付金额等比较重要的字段&#xff0c;可以修改请求参数来进行安全测试 1.首先选择要…

Qt中QIcon图标设置(标题、菜单栏、工具栏、状态栏图标)

1 exe程序图标概述 在 Windows 操作系统中&#xff0c;程序图标一般会涉及三个地方&#xff1b; &#xff08;1&#xff09; 可执行程序&#xff08;以及对应的快捷方式&#xff09;的图标 &#xff08;2&#xff09; 程序界面标题栏图标 &#xff08;3&#xff09;程序在任务…

[激光原理与应用-77]:基于激光器加工板卡的二次开发软件的系统软硬件架构

目录 一、1个板卡、1个激光器、1个振镜的应用架构、1个工位 &#xff08;1&#xff09;PLC &#xff08;2&#xff09;MES &#xff08;3&#xff09;加工板卡 &#xff08;4&#xff09;激光加工板卡与激光器之间的转接卡 &#xff08;5&#xff09;DB25、DB15 &#x…

Typecho如何去掉/隐藏index.php

Typecho后台设置永久链接后&#xff0c;会在域名后加上index.php&#xff0c;很多人都接受不了。例如如下网址&#xff1a;https://www.jichun29.cn/index.php/archives/37/&#xff0c;但我们希望最终的形式是这样&#xff1a;https://www.jichun29.cn/archives/37.html。那么…

图神经网络实战(6)——使用PyTorch构建图神经网络

图神经网络实战&#xff08;6&#xff09;——使用PyTorch构建图神经网络 0. 前言1. 传统机器学习与人工智能2. 人工神经网络基础2.1 人工神经网络组成2.2 神经网络的训练 3. 图神经网络4. 使用香草神经网络执行节点分类4.1 数据集构建4.2 模型构建4.3 模型训练 5. 实现香草图神…

大话设计模式之装饰模式

装饰模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许向现有对象动态地添加新功能&#xff0c;同时又不改变其结构。装饰模式通过将对象放入包装器中来实现&#xff0c;在包装器中可以动态地添加功能。 在装饰模式中&#xff0c;通常会有…

【教程】JavaScript代码混淆及优化

摘要 本文将介绍常见的JavaScript代码混淆技术&#xff0c;包括字符串转十六进制、Unicode编码、Base64加密、数值加密、数组混淆、花指令、逗号表达式、控制流程平坦化和eval执行。通过对这些混淆技术的理解和应用&#xff0c;可以提高代码的安全性和保护知识产权。 引言 随…

(4)(4.3) Kogger Sonar

文章目录 前言 1 推荐硬件 2 配置回声探测仪模块 3 连接ArduPilot硬件 4 参数说明 前言 KOGGER 声纳(KOGGER Sonar)是一款结构紧凑、成本低廉的水下回声测深仪模块&#xff0c;带有 UART 接口&#xff0c;电源电压为 5-14v。 1 推荐硬件 CP210x USB->UART 转换器和安装…