八大排序(二)快速排序

一、快速排序的思想

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

二、快速排序的三种实现方法

2.1、Hoare

思想:取最左边key为基准值,用right指针找比key值小的元素,用left指针找比key位置大的元素,

将两位置值进行交换,最后,将key值放在二者相遇位置上,就可保证key左边都是比key小的值,

右边都是比key大的值,然后进行递归即可实现,从相遇点分割成两部分,在分别对左右两部分重

复上述排序。

代码实现 :           

void Swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}
//Hoare
int partSort1(int* a, int left, int right)
{int key = left;while (right > left){//从右往左找小while (right > left && a[right] >= a[key]){right--;}//从左往右找大while (right > left && a[left] <= a[key]){left++;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[key]);return left;
}void QuickSort(int* arr, int begin,int end)
{if (begin >= end){return;}int keyi = partSort1(arr, begin, end);QuickSort(arr, begin, keyi - 1);QuickSort(arr, keyi + 1, end);
}

2.2、挖坑法                                                                                             

思想:取最左边或最右边值做key,右边形成一个坑,定义两个指针left、right指向头和尾。右边找

小值放到左边坑中右边形成新坑,左边找大值放到右边左边形成新坑,将key放到相遇位置。这时

key左边值均小于key,右边值均大于key。       

代码实现:  

void Swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}//挖坑法
int partSort2(int* a, int left, int right)
{int hole = left;int key = a[left];while (right > left){//从右往左找小while (right > left && a[right] >= key){right--;}a[hole] = a[right];hole = right;//从左往右找大while (right > left && a[left] <= key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return hole;
}void QuickSort(int* arr, int begin,int end)
{if (begin >= end){return;}int keyi = partSort2(arr, begin, end);QuickSort(arr, begin, keyi - 1);QuickSort(arr, keyi + 1, end);
}

   2.3、双指针法 

思想:     

1.选择数组中的第一个元素arr[startIndex]作为轴(pivot)

2.左指针为left,从最左边开始寻找第一个比pivot大的数

3.右指针为right,从最右面的一个元素开始向左寻找第一个小于等于pivot的数值

4.经过2,3两个步骤后,将会出现以下两种情况

​ (1):left和right没有相遇,此时进行交换,swap(arr,left,right);

​ (2):left和right相遇,做swap(arr,startIndex,left),然后返回left

5.partition中返回pivot用于分割数组,下一次用于排序的数组被分割为(startIndex,pivot-1),(pivot+1,endIndex)两段,进行递归操作

代码实现:

void Swap(int* p1, int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}int partSort3(int* a, int left, int right)
{int prev = left;int cur = prev + 1;int key = left;while (cur <= right){if (a[cur] < a[key] && ++prev != cur){Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[prev], &a[key]);return prev;
}void QuickSort(int* arr, int begin,int end)
{if (begin >= end){return;int keyi = partSort3(arr, begin, end);QuickSort(arr, begin, keyi - 1);QuickSort(arr, keyi + 1, end);
}

三、快速排序的优化

3.1、三数取中

当要排序的数组有序或者相对有序,比如我们要把一个逆序的数组按顺序排列,这时我们如果还选

择left为key的话,效率就会非常的低。我们要排除这种低效的可能就要让Key的值相对靠中间一

点,对此我们可以在实现一个函数,选择处left ,right ,和mid三个数中数值中间的那个数。用这个

数作为key就会避免我们遇到的这类问题。


                        

代码实现:

//三数取中
int Getmid(int* a, int left, int right)
{int mid = (left + right) / 2;// left mid rightif (a[left] < a[mid]){if (a[mid] < a[right]){return mid;}else if (a[left] > a[right])  // mid是最大值{return left;}else{return right;}}else // a[left] > a[mid]{if (a[mid] > a[right]){return mid;}else if (a[left] < a[right]) // mid是最小{return left;}else{return right;}}
}

对此我们就可以改进上面的三种方法,都可以在三种方法的开头添加这段代码,使之让key为靠中间的数,避免数组为有序的排序时间效率低的问题。

	int midi = Getmid(a, left, right);Swap(&a[left], &a[midi]);

3.2、小区间优化 

我们递归的深度越高效率越高,但是我们刚开始递归时深度很低,所以效率低下,所以我们可以采用高深度的时候用快速排序,在低深度的时候用直接插入排序,会对运行效率有所提高。

void QuickSort(int* a, int begain, int end)
{if (begain >= end)return;//小区间优化法 当数据量比较大的时候可以通过调整参数(20),来减小递归次数,提高性能if ((end - begain) > 20){int meeti = HoareSort(a, begain, end);QuickSort(a, begain, meeti - 1);QuickSort(a, meeti + 1, end);}else{//数量比较少的时候用直接插入来排序InsertSort(a + begain, end - begain + 1);}}

四、非递归实现快速排序

递归需要在栈上为函数开辟空间,32位下,栈可使用的内存大小不超过2G,如果递归较深,依然可能会发生栈溢出,这个时候递归排序就不大适用,所以需要非递归出场。

利用栈来存储区间下标,代码如下:要注意先数组头,后入数组尾。出栈时栈顶的数据为数组尾,在出才为头位置下标。

代码如下:

//交换函数
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}//三数取中
int GetMinIndex(int* arr, int left, int right)
{int mid = (left + right) >> 1;if (arr[left] < arr[mid]){if (arr[mid] < arr[right]){return mid;}if (arr[left] < arr[right] && arr[right] < arr[mid]){return right;}return left;}else//arr[left] >= arr[mid]{if (arr[left] < arr[right]){return left;}if (arr[mid] < arr[right] && arr[right] < arr[left]){return right;}return mid;}
}//快排非递归
void QuickSort(int* arr, int n)
{ST st;StackInit(&st);//把左右区间压栈,先压右边StackPush(&st, n - 1);//后压左边StackPush(&st, 0);//只要栈不为空,就继续分割排序while (!StackEmpty(&st)){//从栈里面取出左右区间int left = StackTop(&st);StackPop(&st);int right = StackTop(&st);StackPop(&st);int index = GetMinIndex(arr, left, right);//因为我们下面的逻辑都是把第一个数作为key,//为了避免改动代码,这里我们直接交换就可以Swap(&arr[left], &arr[index]);//开始单趟排序int begin = left;int end = right;int pivot = begin;int key = arr[begin];while (begin < end){//end开始找小while (begin < end && arr[end] >= key){end--;}arr[pivot] = arr[end];pivot = end;//begin开始找大while (begin < end && arr[begin] <= key){begin++;}arr[pivot] = arr[begin];pivot = begin;}pivot = begin;arr[pivot] = key;//区间分为[left,pivot-1]pivot[pivot+1,right]//利用循环继续分割区间//先入右子区间if (pivot + 1 < right){//说明右子区间不止一个数//先入右边边界StackPush(&st, right);//再入左边边界StackPush(&st, pivot+1);}//再入左子区间if (left < pivot-1){//说明左子区间不止一个数//先入右边边界StackPush(&st, pivot-1);//再入左边边界StackPush(&st, left);}	}StackDestory(&st);
}

五、时间复杂度 

快速排序的时间复杂度:

最坏情况下,时间复杂度是O(n^2); (逆序)

最优情况下,时间复杂度是O(nlogn);

平均时间复杂度是O(nlogn);

快速排序是时间复杂度:O(logn)

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

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

相关文章

【新版】系统架构设计师 - 案例分析 - 软件工程

个人总结&#xff0c;仅供参考&#xff0c;欢迎加好友一起讨论 文章目录 结构化分析SA数据流图DFD数据流图平衡原则答题技巧例题1例题2 面向对象的分析OOA用例图用例模型细化用例描述用例关系【包含、扩展、泛化】分析模型定义概念类确定类之间的关系类图与对象图实体类 - 存储…

【音视频】MP4封装格式

基本概念 使用MP4box.js查看MP4内部组成结构 整体结构 数据索引&#xff08;moov&#xff09;数据流包&#xff08;mdat&#xff09; 各个包的位置&#xff0c;大小&#xff0c;信息&#xff0c;时间戳&#xff0c;编码方式等全在数据索引 数据流包只有纯二进制码流数据 数据…

C++ - 红黑树 介绍 和 实现

前言 前面 学习了 AVL树&#xff0c;AVL树虽然在 查找方面始终拥有 O(log N &#xff09;的极高效率&#xff0c;但是&#xff0c;AVL 树在插入 ,删除等等 修改的操作当中非常的麻烦&#xff0c;尤其是 删除操作&#xff0c;在实现当中细节非常多&#xff0c;在实现上非常难掌控…

第52节:cesium 3DTiles模型特效+选中高亮(含源码+视频)

结果示例: 完整源码: <template><div class="viewer"><vc-viewer @ready="ready" :logo="false"><vc-navigation

云上亚运:所使用的高新技术,你知道吗?

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 公众号&#xff1a;网络豆云计算学堂 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a; 网络豆的主页​​​​​ 目录 前言 一.什么是云上亚运会 二.为什么要使用云…

【Newman+Jenkins】实施接口自动化测试

一、是什么Newman Newman就是纽曼手机这个经典牌子&#xff0c;哈哈&#xff0c;开玩笑啦。。。别当真&#xff0c;简单地说Newman就是命令行版的Postman&#xff0c;查看官网地址。 Newman可以使用Postman导出的collection文件直接在命令行运行&#xff0c;把Postman界面化运…

负载均衡原理及应用

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

AI AIgents时代 - (三.) AutoGPT和AgentGPT

前两篇讲解了Agent的原理和组件&#xff0c;这节我将给大家介绍两个agent项目&#xff0c;给出它们的工作原理和区别&#xff0c;并教大家亲手尝试使用 Agents&#x1f389; &#x1f7e2; AutoGPT&#x1f916;️ 我们的老朋友&#xff0c;之前文章也专门写过。AutoGPT 是一…

【C++杂货铺】一颗具有搜索功能的二叉树

文章目录 一、二叉搜索树概念二、二叉搜索树的操作2.1 二叉搜索树的查找2.2 二叉搜索树的插入2.3 二叉搜索树的删除 三、二叉搜索树的实现3.1 BinarySearchTreeNode&#xff08;结点类&#xff09;3.2 BinarySearchTree&#xff08;二叉搜索树类&#xff09;3.2.1 框架3.2.2 in…

108. 将有序数组转换为二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输…

【Spark】win10配置IDEA、saprk、hadoop和scala

终于&#xff0c;要对并行计算下手了哈哈哈。 一直讲大数据大数据&#xff0c;我单次数据处理量大概在1t上下&#xff0c;是过亿级的轨迹数据。 用python调用multiprogress编写的代码&#xff0c;用多线程也要一个多月跑完。 我对这个效率不太满意&#xff0c;希望能快一点再快…

Python_ithheima_第二阶段

第一章 01-初识对像 02 类的成员方法 03 类和对象 04 构造方法 05 魔术方法 06 封装 07 封装的课后练习题讲解 08 继承的基础语法 pass关键字的功能是“语法补全” 同名成员或方法&#xff0c;谁先来谁优先级高 09 复写父类成员和调用父类成员 10 变量的类型注解 11 函数和方法…

Qt---day4---9.20

qt完成时钟&#xff1a; 头文件&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QPaintEvent> #include <QtDebug> #include <QPainter> #include <QTimerEvent> #include <QTime>QT_BEGIN_NAMESPACE names…

ROS2 的行为树 — 第 1 部分:解锁高级机器人决策和控制

一、说明 在复杂而迷人的机器人世界中&#xff0c;行为树&#xff08;BT&#xff09;已成为决策过程中不可或缺的一部分。它们提供了一种结构化、模块化和高效的方法来对机器人的行为进行编程。BT起源于视频游戏行业&#xff0c;用于控制非玩家角色&#xff0c;他们在机器人领域…

Kafka 常见问题

文章目录 kafka 如何确保消息的可靠性传输Kafka 高性能的体现利用Partition实现并行处理利用PageCache 如何提高 Kafka 性能调整内核参数来优化IO性能减少网络开销批处理数据压缩降低网络负载高效的序列化方式 kafka 如何确保消息的可靠性传输 消费端弄丢了数据 唯一可能导致…

MinGW相关错误

1、go编译c报错 cc1.exe: sorry, unimplemented: 64-bit mode not compiled in 参考&#xff1a;BeifangCc go编译c报错 cc1.exe: sorry, unimplemented: 64-bit mode not compiled in 说明当前gcc是32位&#xff0c;无法在当前64位机器上正常工作&#xff0c;需要更新gcc 下载…

指针和数组笔试题的透析

指针---进阶篇&#xff08;三&#xff09; 一、前言二、一维数组例题透析&#xff1a;三、指针笔试题1.例一&#xff1a;2.例二&#xff1a;3.例三&#xff1a;4.例四&#xff1a;5.例五&#xff1a;6.例六&#xff1a; 一、前言 那么好了好了&#xff0c;宝子们&#xff0c;从…

R语言绘制PCA双标图、碎石图、变量载荷图和变量贡献图

1、原论文数据双标图 代码&#xff1a; setwd("D:/Desktop/0000/R") #更改路径#导入数据 df <- read.table("Input data.csv", header T, sep ",")# ----------------------------------- #所需的包: packages <- c("ggplot2&quo…

Innodb 原理和日志

一、MySQL结构 客户端 server层 查询缓存&#xff08;5.7&#xff09; 连接器 分析器 优化器 执行器 引擎层 二、一条update操作mysql的流程 三、MySQL的日志 &#xff08;1&#xff09;redo log 保证MySQL 持久性的关键&#xff0c;如果MySQL宕机&#xff0c;buffer pool…

Java编程的精髓:深入理解JVM和性能优化

文章目录 Java虚拟机&#xff08;JVM&#xff09;的核心概念1. 类加载器&#xff08;Class Loader&#xff09;2. 内存区域3. 垃圾回收&#xff08;Garbage Collection&#xff09;4. 类型转换和多态 JVM性能调优1. JVM参数调整2. 内存管理3. 多线程优化4. 使用性能分析工具5. …