【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)

 🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:《数据结构》https://blog.csdn.net/qinjh_/category_12536791.html?spm=1001.2014.3001.5482

9efbcbc3d25747719da38c01b3fa9b4f.gif​​​​

目录

 交换排序

快速排序

 hoare版代码呈现

快排优化

三数取中法

 小区间优化

 挖坑法

 前后指针版本

非递归版本快排


前言

    💬 hello! 各位铁子们大家好哇。

             今日更新了快速排序的内容
    🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝

 交换排序

快速排序

快排的过程图如下: 

 hoare版代码呈现

void QuickSort(int* a, int begin,int end)
{if (begin >= end){return;}int left = begin, right =end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;// [begin,keyi-1] keyi [keyi+1,end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi+1, end);
}

 分析:快排过程就是左边找比key大的值,右边找比key小的值,找到后就交换。直到left与right相遇,就交换keyi和left对应的值。这是单趟的,后续过程重复,可以思考二叉树的递归过程,快排递归与其相似(见下图)。

下图中,划红线的地方是容易出错的地方。 

 

 理解了前面,这里解释一下为什么相遇位置比keyi位置的值要小?

因为右边先走。

相遇有2种情况

  1.  R遇L->R没有找到比key小,一直走,直到遇到L,相遇位置是L,比key小。
  2. L遇R->R先走,找到小的停下来了,L找大,没有找到,遇到R停下来了,相遇位置是R,比key小。

如果左边做key,R先走。

如果右边做key,L先走。

快排优化

  1. 三数取中法选key
  2. 递归到小的子区间时,可以考虑使用插入排序

三数取中法

快排对于有序的数据,效率不是很高。

如上图,我们前面的快排是固定选key的,也就是左边第一幅图,效率很低。理想情况下,每一次都二分,这样效率就能提高。这时就用到三数取中法。

三数取中法指三个数里面取中间大小的数,然后将他与key交换位置,让这个中间大小的数作key。

完整代码如下: 

int GetMidi(int* a, int begin, int end)
{int midi = (begin + end) / 2;//begin end midi三个数中选中位数if (a[begin] < a[midi]){if (a[midi] < a[end])return midi;else if (a[begin] > a[end])return begin;elsereturn end;}else{if (a[midi] > a[end])return midi;else if (a[begin] < a[end])return begin;elsereturn end;}
}void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;// [begin,keyi-1] keyi [keyi+1,end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

 小区间优化

假设在理想情况下,每次递归都像二叉树那样,递归到最后面几层时,假设还剩7个数,我们还得递归7次,这样明显不好。我们就可以在最后几层时,使用其他排序方法进行。这里使用插入排序。 

完整代码如下:

void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;if (end - begin + 1 <= 10){InsertSort(a+begin, end - begin + 1);}else {int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);keyi = left;// [begin,keyi-1] keyi [keyi+1,end]QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);}
}

 挖坑法

我们把不同方法的单趟排序重新弄成一个函数。

hoare版本:

//hoare版本 
int PartSort1(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int left = begin, right = end;int keyi = begin;while (left < right){//右边找小while (left < right && a[right] >= a[keyi]){right--;}//左边找大while (left < right && a[left] <= a[keyi]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);return left;
}

 挖坑法完整代码:

//挖坑法
int PartSort2(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int key = a[begin];int hole = begin;while (begin < end){//右边找小,填到左边的坑while (begin < end && a[end] >= key){end--;}a[hole] = a[end];hole = end;//左边找大,填到右边的坑while (begin < end && a[begin] <= key){begin++;}a[hole] = a[begin];hole = begin;}a[hole] = key;return hole;
} void QuickSort(int* a, int begin, int end)
{if (begin >= end)return;int keyi = PartSort2(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi+1, end);}

 分析:挖坑法其实跟hoare版本比没啥提升,只不过更易理解,本质上没变。但不同的版本,单趟排序后的结果可能会不同。

 前后指针版本

分析:

  1. cur遇到比key大的值,cur++
  2. cur遇到比key小的值,++prev,交换prev和cur位置的值,++cur

代码实现

//前后指针法
int PartSort3(int* a, int begin, int end)
{int midi = GetMidi(a, begin, end);Swap(&a[midi], &a[begin]);int keyi = begin;int prev = begin;int cur = prev + 1;while (cur <= end){if (a[cur] < a[keyi] && ++prev!=cur)Swap(&a[prev], &a[cur]);++cur;}Swap(&a[prev], &a[keyi]);keyi = prev;return keyi;
}

非递归版本快排

非递归版本的快排需要用到栈。下面给出需要的栈的函数:

void STInit(ST* pst)
{assert(pst);pst->a = NULL;pst->capacity = 0;pst->top = 0;//pst->top = -1;
}void STDestroy(ST* pst)
{free(pst->a);pst->a = NULL;pst->capacity = pst->top = 0;}//栈顶插入删除
void STPush(ST* pst, STDataType x)
{assert(pst);if (pst->top == pst->capacity){int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);if (tmp == NULL){perror("realloc fail");return;}pst->a = tmp;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}void STPop(ST* pst)
{assert(pst);assert(pst->top > 0);pst->top--;
}STDataType STTop(ST* pst)
{assert(pst);assert(pst->top > 0);return	pst->a[pst->top - 1];
}bool STEmpty(ST* pst)
{assert(pst);//if (pst->top == 0)//{//	return true;//}//else//{//	return	false;//}return pst->top == 0;
}

 非递归代码实现:

void QuickSortNonR(int* a, int begin, int end)
{ST s;STInit(&s);STPush(&s, end);STPush(&s, begin);while (!STEmpty(&s)){int left = STTop(&s);STPop(&s);int right = STTop(&s);STPop(&s);int keyi = PartSort3(a, left, right);// [left,keyi-1] keyi [keyi+1,right]if (keyi+1 < right){STPush(&s, right);STPush(&s, keyi+1);}if (left < keyi - 1){STPush(&s, keyi - 1);STPush(&s, left);}}STDestroy(&s);
}

分析:栈是后进先出,这里用栈是模拟递归的过程。先模拟递归左边,像二叉树递归那样,先入右边的数,再入左边,这样出的时候就先出左边的,然后就可以模拟先往左边递归了。

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

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

相关文章

Windows Server 2025 LTSC 预览版来了

Windows Server 2025 LTSC 预览版来了 1. 安装 Windows Server 2025 LTSC 预览版2. 安装 VMware Tools3. Windows Server 2025 LTSC 预览版4. Windows Server 2025 LTSC 预览版下载地址 1. 安装 Windows Server 2025 LTSC 预览版 使用 VMware Workstation 安装&#xff0c; 安…

推特账号被冻结怎么办?检查IP是否正常

Twitter 拥有庞大的用户群和日常内容流&#xff0c;是沟通、网络和营销的重要平台。然而&#xff0c;处理其限制和潜在的帐户问题可能很棘手。有许多跨境社媒小伙伴反馈&#xff0c;账号无故被冻结&#xff0c;导致内容与客户尽失&#xff01;其实除了账户养号、被举报、广告信…

Mac+Android Studio配置 Flutter环境

Fluttrer中文下载官网 Flutter下载官网 1、环境变量 .zshrc #Flutter export PUB_HOSTED_URL"https://pub.flutter-io.cn" export FLUTTER_STORAGE_BASE_URL"https://storage.flutter-io.cn" export FLUTTER_HOME/Users/leon/Flutter/flutter_3_10_4/f…

SpringBoot项目接入MQTT协议

mqtt是一种类似于mq的通讯技术 1、mqtt服务端搭建 创建docker网络 docker network create --driver bridge --subnet 172.18.0.0/24 --gateway 172.18.0.1 emqx-net创建容器 docker run -d \--name emqx1 \-e "EMQX_NODE_NAMEemqx172.18.0.2" \--network emqx-ne…

js实现填涂画板

文章目录 1实现效果2 实现代码 凑个数&#xff0c;存粹是好玩儿&#xff0c;哈哈... 1实现效果 最上方一栏&#xff1a; 左侧是颜色按钮&#xff0c;点击选中颜色&#xff0c; 中间是功能按钮&#xff0c;重置颜色、清空画板、回退、涂改液&#xff08;填涂色置为白色&#xff…

STM32 有源蜂鸣器

模块介绍: 结构&#xff1a;有源蜂鸣器通常由一个振膜和一个驱动电路组成。振膜是负责产生声音的部分&#xff0c;而驱动电路则负责控制振荡频率和幅度。 工作原理&#xff1a;有源蜂鸣器的驱动电路会向振膜施加电压&#xff0c;使其振动产生声音。驱动电路可以根据输入信号的…

借助gpt生成ppt:文心一言(chatgpt)、chatppt

提供一种简单的基于gpt快速生成ppt的方式。前置条件&#xff1a; 文心一言chatpptwps/office ppt Step1: 下载chatppt插件 https://chat-ppt.com/invitelinke?share_code47949695&channelchat-ppt.com 注册地址 下载完成后&#xff0c;安装即可&#xff0c;安装完成后…

如何看待开发者是否需要入坑鸿蒙?

前言 自打华为2019年发布鸿蒙操作系统以来&#xff0c;网上各种声音百家争鸣。尤其是2023年发布会公布的鸿蒙4.0宣称不再支持Android&#xff0c;更激烈的讨论随之而来。 通过本文&#xff0c;我将给大家介绍以下几点&#xff0c;让大家清楚的了解到鸿蒙开发的趋势&#xff1…

win11设置mysql开机自启

目录 命令式 1、打开命令提示符或 PowerShell&#xff1a; 2、使用管理员权限运行命令行工具&#xff1a; 3、设置 MySQL 服务为开机自启动&#xff1a; 4、启动 MySQL 服务&#xff1a; 5、 验证设置是否生效&#xff1a; 操作视图式 1、右击任务栏 ---> 选择任务管…

深入了解DRAM和SDRAM:内存带宽的计算与封装形式的奥秘

SSD SDRAM DDR SDRAM简介 动态随机存取存储器DRAM&#xff08;Dynamic Random Access Memory&#xff0c;DRAM&#xff09;是一种半导体存储器。 其主要的作用原理是利用电荷内存储电荷的数量来代表一个二进制比特&#xff08;bit&#xff09;是1还是0。 由于在现实中品体管…

进京证12次不够用怎么办?(北京进京证探头分布,进京证365,进京365)外地车在京如何行驶——躲猫猫外地车在京地图导航

其实想要在北京驾驶外地牌照的车辆主要有两种方式&#xff0c;一种是办理进京证(六环内进京证一年只能办12次&#xff0c;一次有效期7天&#xff0c;所以大多数人是不够用的);另一种就是在非监控区域行驶&#xff0c;可以借助于一些摄像头定位工具&#xff0c;有效躲避摄像头&a…

洗地机哪个牌子好?2024洗地机推荐

洗地机作为一种几乎替代了传统无线吸尘器的清洁工具&#xff0c;近年来在市场上迅速崛起。其优越的清洁效果和智能化设计使其成为许多家庭不可或缺的家电之一。在短短的几年时间里&#xff0c;市场上涌现出了各种各样的品牌和型号&#xff0c;价格也从几百元到数千元不等&#…

LVGL部件

一.标签部件 1.如何创建标签部件以及设置文本 ![2024-01-28T09:54:08.png][3] void my_lvgl(void) {lv_obj_t *lablelv_label_create(lv_scr_act()); //创建一个标签lv_label_set_text(lable,"hello"); //普通更改文字lv_label_set_text_fmt(lab…

有哪些原型图设计工具是你应该熟悉的?

今天我们将介绍 5 优秀的原型设计工具及其功能。每个软件都有不同的平台和价格范围。相信你能找到最适合你的原型工具&#xff01; 1、Sketch 以友好的用户而闻名 Sketch&#xff0c;对于设计师来说&#xff0c;有很多实用的功能。这个软件在图形编辑方面很受欢迎&#xff0c;…

【Django开发】前后端分离美多商城项目:项目准备和搭建(附代码,文档)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论django商城项目开发相关知识。本项目利用Django框架开发一套前后端不分离的商城项目&#xff08;4.0版本&#xff09;含代码和文档。功能包括前后端不分离&#xff0c;方便SEO。采用Django Jinja2模板引擎 Vue.js实现…

【webrtc】m98 : vs2019 直接构建webrtc及moduletest工程 2

字数有限制,我们继续 【webrtc】m98 : vs2019 直接构建webrtc及unitest工程 1modules_unittests 构建 Build started... 1>------ Build started: Project: modules_unittests, Configuration: GN Win32 ------ 1>ninja: Entering directory `G:\CDN\rtcCli\m98\src\o…

linux centos 查看端口是否打开与打开端口

查看端口是否打开 talnet talnet ip 端口linux查看防火墙开放情况 firewall-cmd --list-all打开端口 其中permanent表示永久生效&#xff0c;public表示作用域&#xff0c;443/tcp表示端口和类型&#xff0c;执行规则的重载 firewall-cmd --zonepublic --add-port443/tcp …

VitePress-04-文档中的表情符号的使用

说明 vitepress 的文档中是支持使用表情符号的&#xff0c;像 &#x1f602; 等常用的表情都是支持的。 本文就来介绍它的使用方式。 使用语法 语法 &#xff1a; :表情名称: 例如 &#xff1a; :joy: &#x1f602; 使用案例代码 # 体会【表情】的基本使用 > hello world …

Netty的高级用法(一)

前言 我们直到在网络通信中客户端和服务端之间除了要传输数据外&#xff0c;还会进行简单的心跳应答通信&#xff0c;使得客户端和服务端的连接处于一种活跃状态&#xff0c;那么客户端可以发送ONE_WAY和TWO_WAY两种方式的处理&#xff0c;而服务端在处理这两种类型的数据时会…

4核16G幻兽帕鲁服务器优惠价格表,阿里云和腾讯云报价

幻兽帕鲁服务器价格多少钱&#xff1f;4核16G服务器Palworld官方推荐配置&#xff0c;阿里云4核16G服务器32元1个月、96元3个月&#xff0c;腾讯云幻兽帕鲁服务器服务器4核16G14M带宽66元一个月、277元3个月&#xff0c;8核32G22M配置115元1个月、345元3个月&#xff0c;16核64…