八大排序(二)快速排序

一、快速排序的思想

快速排序是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.rhkb.cn/news/140260.html

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

相关文章

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 下载…

2023手把手教授neo4j安装及环境配置

安装包下载&#xff1a; 首先进入Neo4j官网&#xff1a;Neo4j Graph Database & Analytics | Graph Database Management System 在上方选择栏中选择“Products”&#xff0c;在其中选择“Deployment Center”&#xff0c;点击“Download Neo4j to get started” 然后往下…

【Tomcat】Tomcat 运行原理

Tomcat 运行原理 一. Servlet 运行原理1. 接收请求2. 根据请求计算响应3. 返回响应 二. Tomcat 的伪代码1. Tomcat 初始化流程2. Tomcat 处理请求流程3. Servlet 的 service 方法的实现 一. Servlet 运行原理 在 Servlet 的代码中我们并没有写 main 方法, 那么对应的 doGet 代…

ARM Linux DIY(十三)Qt5 移植

前言 板子带有屏幕&#xff0c;那当然要设计一下 GUI&#xff0c;对 Qt5 比较熟悉&#xff0c;那就移植它吧。 移植 Qt5 buildroot 使能 Qt5&#xff0c;这里我们只开启核心功能 gui module --> widgets module 编译 $ make ODIY_V3S/ qt5base编译报错&#xff1a;找不…

Flink TaskManger 内存计算实战

Flink TaskManager内存计算图 计算实例 案例一、假设Task Process内存4GB。 taskmanager.memory.process.size4096m 先排减JVM内存。 JVM Metaspace 固定内存 256mJVM Overhead 固定比例 process * 0.1 4096 * 0.1 410m 得到 Total Flink Memory 4096-256-410 3430m 计…

【线性代数】为什么 AA* = |A|E

A A ∗ 矩阵相乘&#xff0c;刚好是行列式展开的定义 AA*矩阵相乘&#xff0c;刚好是行列式展开的定义 AA∗矩阵相乘&#xff0c;刚好是行列式展开的定义 矩阵提取一个因子 ∣ A ∣ &#xff0c;所有元素需要除 ∣ A ∣ 矩阵提取一个因子 |A|&#xff0c;所有元素需要除 |A| 矩…

【C/C++笔试练习】——printf在使用%的注意事项、for循环语句的三个条件、运算符优先级、删除公共字符

文章目录 C/C笔试练习1.%符号在printf用作格式说明符的注意事项&#xff08;1&#xff09;输出%5.3s&#xff08;2&#xff09;判断%中小数点含义 2.for循环语句的三个条件&#xff08;3&#xff09;判断循环次数&#xff08;4&#xff09;判断循环次数 3.运算符优先级&#xf…

【ACDC数据集】:预处理ACDC心脏3D MRI影像数据集到VOC数据集格式,nii转为jpg,label转为png

【Segment Anything Model】做分割的专栏链接&#xff0c;欢迎来学习。 【博主微信】cvxiaoyixiao 本专栏为公开数据集的预处理&#xff0c;持续更新中。 文章目录 1️⃣ ACDC数据集介绍2️⃣ ACDC数据集样例 3️⃣ 预处理ACDC目标 4️⃣ 处理结果样图 5️⃣ 代码 6️⃣ 划分测…

文件高效批量重命名,轻松重命名不同类型的文件名并隐藏编号

你是否曾经因为文件名混乱而感到困扰&#xff1f;你是否希望有一种方法可以快速、简单地管理你的文件名&#xff1f;如果你的答案是肯定的&#xff0c;那么我们的产品——文件重命名工具&#xff0c;将是你的完美解决方案&#xff01; 首先我们要进入文件批量改名高手主页面&a…

Sqilte3初步教程

文章目录 安装创建数据库创建和删除表插入行数据 安装 Windows下安装&#xff0c;首先到下载页面&#xff0c;下载Windows安装软件&#xff0c;一般是 sqlite-dll-win32-*.zip sqlite-tools-win32-*.zip下载之后将其内容解压到同一个文件夹下&#xff0c;我把它们都放在了D:\…

搭建ELK+Filebead+zookeeper+kafka实验

部署 Zookeeper 集群 准备 3 台服务器做 Zookeeper 集群 192.168.10.17 192.168.10.21 192.168.10.22 1.安装前准备 关闭防火墙 systemctl stop firewalld systemctl disable firewalld setenforce 0 安装 JDK yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-…

DolphinDB x 龙蜥社区,打造多样化的数据底座

近日&#xff0c;浙江智臾科技有限公司&#xff08;以下简称“DolphinDB”&#xff09;正式签署 CLA 贡献者许可协议&#xff0c;加入龙蜥社区&#xff08;OpenAnolis&#xff09;。 DolphinDB 主创团队从 2012 年开始投入研发产品。作为一款基于高性能时序数据库&#xff0c;D…

【pytest】 allure 生成报告

1. 下载地址 官方文档; Allure Framework 参考文档&#xff1a; 最全的PytestAllure使用教程&#xff0c;建议收藏 - 知乎 https://github.com/allure-framework 1.2安装Python依赖 windows&#xff1a;pip install allure-pytest 2. 脚本 用例 import pytest class …

代码随想录算法训练营 动态规划part12

一、最佳买卖股票时机含冷冻期 309. 买卖股票的最佳时机含冷冻期 - 力扣&#xff08;LeetCode&#xff09; public class Solution {public int maxProfit(int[] prices) {int len prices.length;if (len < 2) {return 0;}int[] dp new int[3];dp[0] 0;dp[1] -price…

leetcode:2446. 判断两个事件是否存在冲突(python3解法)

难度&#xff1a;简单 给你两个字符串数组 event1 和 event2 &#xff0c;表示发生在同一天的两个闭区间时间段事件&#xff0c;其中&#xff1a; event1 [startTime1, endTime1] 且event2 [startTime2, endTime2] 事件的时间为有效的 24 小时制且按 HH:MM 格式给出。 当两个…

terraform简单的开始-vpc cvm创建

从网络开始 从创建VPC开始 复用前面的main.tf的代码&#xff1a; terraform {required_providers {tencentcloud {source "tencentcloudstack/tencentcloud"version "1.81.25"}} } variable "region" {description "腾讯云地域"…

Spring Boot的新篇章:探索2.0版的创新功能

文章目录 引言1. Spring Boot 2.0的响应式编程2. 自动配置的改进3. Spring Boot 2.0的嵌入式Web服务器4. Spring Boot 2.0的Actuator端点5. Spring Boot 2.0的Spring Data改进6. Spring Boot 2.0的安全性增强7. Spring Boot 2.0的监控和追踪8. Spring Boot 2.0的测试改进结论 &…

Learn Prompt-提供示例

目前我们与 ChatGPT 交流的主要形式是文字。提示除了指令问题的形式外&#xff0c;还可以包含例子。特别是当我们需要具体的输出时&#xff0c;提供例子可以省去我们对具体任务的解释&#xff0c;帮助ChatGPT更好地理解我们的确切需求&#xff0c;从而提供更准确&#xff0c;更…

http协议与tomcat

目录 引言 抓包 fiddler的基本使用及设置 HTTP请求 请求首行请求头空行正文 请求的首行方法URL版本号 ​编辑 响应首行响应头空行正文 响应的首行版本号状态码 URL(网址) url基本格式 urlencode 常见方法 get和post区别 认识请求"报头"(header) Host Content-Len…

数据结构-----堆(完全二叉树)

目录 前言 一.堆 1.堆的概念 2.堆的存储方式 二.堆的操作方法 1.堆的结构体表示 2.数字交换接口函数 3.向上调整&#xff08;难点&#xff09; 4.向下调整&#xff08;难点&#xff09; 5.创建堆 6.堆的插入 7.判断空 8.堆的删除 9.获取堆的根(顶)元素 10.堆的遍历…