【C语言】快速排序

文章目录

  • 一、hoare版本
  • 二、挖坑法
  • 三、前后指针法
  • 四、非递归快排
  • 五、快速排序优化
    • 1、三数取中选key值
    • 2、小区间优化
  • 六、代码测试

在这里插入图片描述

一、hoare版本

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

算法思想:

  1. 定义一个keyi存入随机一个数key的下标换到数组首元素,这里先直接默认key为数组首元素
  2. 定义一个left和一个right,分别存入数组首元素和尾元素的下标,用来移动交换
  3. 排升序我们让右边right先向左移动,找到比key的值小的元素则停下来换到left移动
  4. left向右移动,找到比key的值大的元素则停下
  5. 交换下标为left和right的元素
  6. 重复以上操作直到left与right相遇(相等)
  7. 交换key和下标为left的元素
  8. 此时key的左边都是比它小的数,右边都是比它大的数
  9. 再分别对左右序列进行以上的单趟排序,反复操作直到左右序列只有一个或者没有元素时停止操作,数列即可有序

hoare版本单趟排序图示:

在这里插入图片描述

hoare版本代码:

//交换
void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
//hoare版本
void QuickSort1(int* a, int begin, int end)
{//递归结束条件if (begin >= end){return;}int keyi = begin;int left = begin;int right = end;//每趟排序直到左右相遇while (left < right){//右边先走,找到比key值小的while (left < right && a[right] >= a[keyi]){right--;}//right找到比key值小的之后换到left走,找到比key值大的while (left < right && a[left] <= a[keyi]){left++;}//交换Swap(&a[left], &a[right]);}//将key值换到中间Swap(&a[keyi], &a[left]);//更新keykeyi = left;//对左右序列继续排序QuickSort1(a, begin, keyi - 1);QuickSort1(a, keyi + 1, end);
}

整体流程图:

在这里插入图片描述

二、挖坑法

挖坑法思想:

  1. 先将第一个数据存在变量key中,将此处作为最开始的坑位,用下标hole记录
  2. 然后right开始向前走,找到比key值小的元素后停下,将此元素放进坑里(下标为hole处),然后此处变为坑,hole变为此时的right
  3. 然后left开始向后移动,找到比key值大的元素后停下,将此元素放进坑里(下标为hole处),然后此处变为坑,hole变为此时的left
  4. 然后又换回right移动,如此反复直到left与right相遇(left与right相遇的地方一定是坑)
  5. 然后将key放入left与right相遇的位置,也就是坑的位置,此时hole左边都是小于等于它的,右边都是大于等于它的
  6. 如此单趟排序便结束,然后继续对hole左右序列继续反复执行以上操作,直到左右序列只有一个或者没有元素时停止操作,数列即可有序

挖坑法单趟排序图示:

在这里插入图片描述

挖坑法代码:

//挖坑法
void QuickSort2(int* a, int begin, int end)
{//递归结束条件if (begin >= end){return;}int left = begin;int right = end;int key = a[left];//坑最初与left一样在开始位置int hole = left;//每趟排序直到左右相遇while (left < right){//右边先走,找到比key值小的while (left < right && a[right] >= key){right--;}//将right找到的比key小的元素放进坑中a[hole] = a[right];//更新坑的位置hole = right;//然后左边走找到比key值大的元素停下来while (left < right && a[left] <= key){left++;}//将left找到的比key大的元素放进坑中a[hole] = a[left];//更新坑的位置hole = left;}//将key放入坑中a[hole] = key;//对左右序列继续排序QuickSort2(a, begin, hole - 1);QuickSort2(a, hole+1, end);
}

三、前后指针法

前后指针法思想:

  1. 定义一个keyi存入随机一个数key的下标换到数组首元素,这里先直接默认key为数组首元素
  2. 定义一个prev为开头元素的下标,定义一个cur为prev下一个元素的下标
  3. cur下标处的值与key比较,直到cur找到比key小的值则停下来
  4. prev下标后移一位然后与cur下标处的值交换,然后cur后移一位(prev相当于前面比key小的那些数的最后一个的下标,所以要先后移一位再交换)
  5. cur继续寻找比key小的值,反复执行直到cur的值大于n
  6. 将key与prev下标处的值交换,此时key左边都是小于等于它的,右边都是大于等于它的
  7. 如此单趟排序便结束,然后继续对key左右序列继续反复执行以上操作,直到左右序列只有一个或者没有元素时停止操作,数列即可有序

前后指针法单趟排序图示:

在这里插入图片描述

前后指针法代码:

//交换
void Swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
//前后指针
void QuickSort3(int* a, int begin, int end)
{//递归结束条件if (begin >= end){return;}int keyi = begin;int prev = begin;int cur = begin + 1;//每趟排序直到cur下标大于endwhile (cur <= end){//cur找比key小的值if (a[cur] < a[keyi] && ++prev != cur){Swap(&a[cur], &a[prev]);}cur++;}//将key换到中间Swap(&a[keyi], &a[prev]);//更新key的下标keyi = prev;//对左右序列继续排序QuickSort3(a, begin, keyi - 1);QuickSort3(a, keyi + 1, end);
}

快速排序是一种不稳定的排序,它的时间复杂度为O(N*logN),但最坏可以达到O(N2) ,它的空间复杂度为O(logN)

四、非递归快排

以上三种方法都是采用了分治法递归实现的快排,其实快速排序也可以非递归实现,非递归实现快排需要利用栈来实现

思路:

将数组首尾下标存入栈中,在循环中依次取出作为left和right对数组进行排序,然后对得到的key的左右两边序列也进行相同的操作,其中左边为left到keyi-1,右边为keyi+1到right,这些下标的入栈顺序需要看取出的顺序,如下面代码中是先取出后面元素下标的,所以入栈时要先入后面的,因为栈的特点是先入后出。

在这里插入图片描述

非递归快排代码:

(该代码中用到的栈需自己实现,C语言实现栈可参考:栈的实现)

//非递归快速排序
void QuickSortNonR(int* a, int begin, int end)
{//创建一个栈ST st;//初始化栈STInit(&st);//插入尾元素下标STPush(&st, end);//插入首元素下标STPush(&st, begin);//栈为空停下while (!STEmpty(&st)){//取出栈顶元素作为leftint left = STTop(&st);//取出后在栈中删除STPop(&st);//取出栈顶元素作为rightint right = STTop(&st);//取出后在栈中删除STPop(&st);int keyi = begin;//每趟排序直到左右相遇while (left < right){//右边先走,找到比key值小的while (left < right && a[right] >= a[keyi]){right--;}//right找到比key值小的之后换到left走,找到比key值大的while (left < right && a[left] <= a[keyi]){left++;}//交换Swap(&a[left], &a[right]);}//将key值换到中间Swap(&a[keyi], &a[left]);//更新key的下标keyi = left;// 当前数组下标样子  [left,keyi-1] keyi [keyi+1, right]//右边还有元素,按顺序插入right和keyi+1if (keyi + 1 < right){STPush(&st, right);STPush(&st, keyi + 1);}//左边还有元素,按顺序插入keyi-1和leftif (left < keyi - 1){STPush(&st, keyi - 1);STPush(&st, left);}}STDestroy(&st);
}

五、快速排序优化

1、三数取中选key值

前面三种快速排序的方法起初都要随机选取一个值作为key,我们之前是直接默认为数组首元素的,这样不够随机,容易出现最坏的情况,使得它的时间复杂度接近O(N2),所以我们可以写一个函数来选取这个key,使得它比较随机,而不是直接为首元素。

三数取中:

在一个数组最前面、最后面,中间这三个位置的数中选出大小处于中间的数

// 三数取中
int GetMidi(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] > a[right]){if (a[right] > a[mid]){return right;}else if(a[mid]>a[right]&&a[mid]<a[left]){return mid;}else{return left;}}else{if (a[left] > a[mid]){return left;}else if (a[mid] > a[left] && a[mid] < a[right]){return mid;}else{return right;}}
}

在快排时用三数取中法选取key值再将它换到数组开头,可以有效避免出现最坏的情况,大大提升算法效率

2、小区间优化

当递归到数据较小时可以使用插入排序,使得小区间不再递归分割,降低递归次数

六、代码测试

//打印数组
void PrintArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void TestQuickSort1()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };QuickSort1(a, 0, sizeof(a) / sizeof(int) - 1);printf("hoare版本快速排序:\n");PrintArray(a, sizeof(a) / sizeof(int));
}
void TestQuickSort2()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };QuickSort2(a, 0, sizeof(a) / sizeof(int) - 1);printf("挖坑法快速排序:\n");PrintArray(a, sizeof(a) / sizeof(int));
}
void TestQuickSort3()
{int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };QuickSort3(a, 0, sizeof(a) / sizeof(int) - 1);printf("前后指针法快速排序:\n");PrintArray(a, sizeof(a) / sizeof(int));
}
int main()
{TestQuickSort1();TestQuickSort2();TestQuickSort3();return 0;
}

在这里插入图片描述

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

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

相关文章

蓝桥杯每日一题2023.9.27

4408. 李白打酒加强版 - AcWing题库 题目描述 题目分析 对于这题我们发现有三个变量&#xff0c;店&#xff0c;花&#xff0c;酒的数量&#xff0c;对于这种范围我们使用DP来进行分析。 dp[i][j][k]我们表示有i个店&#xff0c;j朵花&#xff0c;k单位酒的集合&#xff0c…

Databend 源码阅读:配置管理

作者&#xff1a;尚卓燃&#xff08;PsiACE&#xff09;澳门科技大学在读硕士&#xff0c;Databend 研发工程师实习生 Apache OpenDAL(Incubating) Committer https://github.com/PsiACE 对于 Databend 这样复杂的数据库服务端程序&#xff0c;往往需要支持大量的可配置选项&am…

PTA程序辅助实验平台——2023年软件设计综合实践_3(分支与循环)

第一题&#xff1a;7-1 印第安男孩 - C/C 分支与循环 朵拉编程的时候也想顺便练习英语。她编程从键盘读入一个整数n&#xff0c;如果n值为0或者1&#xff0c;向屏幕输出“0 indian boy.”或“1 indian boy.”&#xff1b;如果n大于1&#xff0c;比如9&#xff0c;则输出“9 in…

查看Linux系统信息的常用命令

文章目录 1. 机器配置查看2. 常用分析工具3. 常用指令解读3.1 lscpu 4. 定位僵尸进程5. 参考 1. 机器配置查看 # 总核数物理CPU个数x每颗物理CPU的核数 # 总逻辑CPU数物理CPU个数x每颗物理CPU的核数x超线程数 cat /proc/cpuinfo| grep "physical id"| sort| uniq| w…

GaussDB数据库SQL系列-游标管理

目录 一、前言 二、概述&#xff08;GaussDB&#xff09; 1、游标概述 2、游标的使用分类 三、GaussDB中的显式游标&#xff08;示例&#xff09; 1、显式游标的使用与操作步骤 2、显式游标示例 四、GaussDB中的隐式游标&#xff08;示例&#xff09; 1、隐式游标简介…

MySQL基础进阶

文章目录 MySQL基础进阶 约束 \color{red}{约束} 约束约束的概念和分类约束的概念约束的分类 非空约束概念语法 唯一约束概念语法 主键约束概念语法 数据库设计 \color{red}{数据库设计} 数据库设计软件的研发步骤数据库设计概念数据库设计的步骤表关系一对一一对多&#xff08…

Pytest+Allure+Excel接口自动化测试框架实战

1. Allure 简介 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不仅以 Web 的方式展示了简介的测试结果&#xff0c;而且允许参与开发过程的每个人可以从日常执行的测试中&#xff0c;最大限度地提取有用信息。 Allure 是由 Java 语言开发的…

结构型设计模式——桥接模式

摘要 桥接模式(Bridge pattern): 使用桥接模式通过将实现和抽象放在两个不同的类层次中而使它们可以独立改变。 一、桥接模式的意图 将抽象与实现分离开来&#xff0c;使它们可以独立变化。 二、桥接模式的类图 Abstraction: 定义抽象类的接口Implementor: 定义实现类接口 …

MySQL 的 C 语言接口

1. mysql_init MYSQL *mysql_init(MYSQL *mysql); mysql_init函数的作用&#xff1a;创建一个 MYSQL 对象&#xff08;该对象用于连接数据库&#xff09;。 mysql_init函数的参数&#xff1a; ① mysql&#xff1a;MYSQL 结构体指针&#xff0c;一般设置为 NULL 。 mysql_init函…

嵌入式芯片-NE555

目录 1、比较器&#xff08;运放&#xff09; 2、相反门&#xff08;非门&#xff09; 3、或非门 4、双稳态触发器 5、NE555功能框图 1、比较器&#xff08;运放&#xff09; 2、相反门&#xff08;非门&#xff09; 3、或非门 4、双稳态触发器 5、NE555功能框图

俞敏洪:董宇辉在北京有房子了!

据媒体报道&#xff0c;9月26日&#xff0c;俞敏洪在直播中透露&#xff0c;董宇辉已经在北京拥有了自己的房子&#xff0c;并且强调这是大家共同努力的结果。 这一消息引起了广泛关注和热议。在此之前&#xff0c;董宇辉曾在公开场合表示&#xff0c;俞敏洪老师为了给他凑钱买…

数据库索引失效

索引失效是指数据库查询在执行过程中无法有效利用索引&#xff0c;导致查询性能下降或索引无法被使用的情况&#xff0c;以下是常见的导致索引失效的原因&#xff1a; 模糊查询以通配符开头&#xff0c;自己都不知道具体要查什么&#xff0c;怎么使用索引呢&#xff0c;所以会导…

输送机使用的常见误区

输送机也称流水线&#xff0c;是指在自动化生产过程中起到运输货物&#xff0c;联通各个生产设备的主要机械设备。但在使用的过程中&#xff0c;很多用户对于输送机的使用存在一定的误区&#xff0c;导致设备故障频出&#xff0c;下面就针对用户已在使用输送机过程中的常见误区…

Android Studio 创建项目不自动生成BuildConfig文件

今天在AS上新建项目发现找不到BuildConfig文件&#xff0c;怎么clear都不行。通过多方面查找发现原来gradle版本不同造成的&#xff0c;Gradle 8.0默认不生成 BuildConfig 文件。 如上图&#xff0c;8.0版本是没有source文件夹 上图是低于8.0版本有source文件夹 针对这个问题&…

基于若依框架进行二次开发优化指南

背景 若依&#xff08;RuoYi&#xff09;开源框架是一个功能强大的Java开发框架&#xff0c;专注于快速构建企业级后台管理系统。它提供了一套丰富的功能和模块&#xff0c;可以帮助开发人员快速搭建稳定、高效的管理系统。本篇博客将大家了解若依框架的基本概念和使用方法&am…

云原生Kubernetes:对外服务之 Ingress

目录 一、理论 1.Ingress 2.部署 nginx-ingress-controller(第一种方式) 3.部署 nginx-ingress-controller(第二种方式) 二、实验 1.部署 nginx-ingress-controller(第一种方式) 2.部署 nginx-ingress-controller(第二种方式) 三、问题 1.启动 nginx-ingress-controll…

Selenium Web自动化测试 —— 高级控件交互方法!

一、使用场景 使用场景对应事件复制粘贴键盘事件拖动元素到某个位置鼠标事件鼠标悬停鼠标事件滚动到某个元素滚动事件使用触控笔点击触控笔事件&#xff08;了解即可&#xff09; https://www.selenium.dev/documentation/webdriver/actions_api 二、ActionChains解析 实例…

iOS自动化测试方案(一):MacOS虚拟机保姆级安装Xcode教程

文章目录 一、环境准备二、基础软件三、扩展&#xff1a;usb拓展插件 一、环境准备 1、下载VMware虚拟机的壳子&#xff0c;安装并注册软件(可以百度注册码)&#xff0c;最新版本&#xff1a;v17 2、下MacOS系统iOS镜像文件&#xff0c;用于vmware虚拟机安装&#xff0c;当前镜…

油猴(篡改猴)学习记录

第一个Hello World 注意点:默认只匹配了http网站,如果需要https网站,需要自己添加match https://*/*代码如下 这样子访问任意网站就可以输出Hello World // UserScript // name 第一个脚本 // namespace http://tampermonkey.net/ // version 0.1 // descri…

ElasticSearch(二)

1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff08;Domain Specific Language&#xff09;来定义查询。常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数据&#xff0c;…