【排序】——2.快速排序法(含优化)

在这里插入图片描述


快速排序法

在这里插入图片描述


pic_bf427633.png

递归法

霍尔版本(左右指针法)

1.思路

1、选出一个key,一般是最左边或是最右边的。
2、定义一个begin和一个end,begin从左向右走,end从右向左走。(需要注意的是:若选择最左边的数据作为key,则需要end先走;若选择最右边的数据作为key,则需要bengin先走)。
3、在走的过程中,若end遇到小于key的数,则停下begin开始走,直到begin遇到一个大于key的数时将begin和right的内容交换,end再次开始走,如此进行下去,直到begin和end最终相遇,此时将相遇点的内容与key交换即可。(选取最左边的值作为key)
4.此时key的左边都是小于key的数,key的右边都是大于key的数
5.将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作,此时此部分已有序

📝tip1
在这里插入图片描述

  • 常选左为key,那右边先走。

📝优化一:选key的常见三种方法

  • 1.三数取中(比较推荐)

    • 三数取中 left mid right
    • 大小居中的值,也就是不是最大也不是最小的
  • 2.随机取一个

  • 3.取中间位置

//关于选kevi——三数取中,作为kevi(比较好)
int Getmid(int* arr, int left, int right)
{//方法1:三数取中//left	mid	  right//找到三者——中间值的下标,返回int mid = (left + right) / 2;if (arr[left] < arr[mid]){if (arr[mid] < arr[right]){return mid;}else if(arr[right]<arr[mid]){return right;}else{return left;}}else    //此时已经arr[left]>arr[mid]{if (arr[right] < arr[mid]){return mid;}else if (arr[left] < arr[right]){return left;}else{return right;}}
}
//方法2:随机取一个(还行)
int GetRand(int* arr, int left, int right)
{srand((size_t)time(NULL));return rand() % (right-left+1)+left;
}
//方法3:取中间位置
int GetMid(int* arr, int left, int right)
{return (left + right) / 2;
}

📝优化二:小区间优化

优化小数组的交换,就是为了解决大才小用问题

原因:对于很小和部分有序的数组,快排不如插排好。当待排序序列的长度分割到一定大小后,继续分割的效率比插入排序要差,此时可以使用插排而不是快排

截止范围:待排序序列长度N = 10,虽然在5~20之间任一截止范围都有可能产生类似的结果,这种做法也避免了一些有害的退化情形

if (right - left + 1 < 10)
{InsertSort(a + left, right - left + 1);		//使用插入排序return;
}
2.图解

单趟动图
在这里插入图片描述

3.代码

代码如下

//hoare(霍尔)版本(左右指针法)
#include<stdio.h>
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
void QuickSort(int* arr,int left,int right)
{//递归终止条件if (left >= right)return;//记录一下,因为后面要变化int begin=left;int end=right;//选左边为keyint mid = Getmid(arr, left, right);		//三数取中Swap(&arr[mid],&arr[left]);				//把找到的,移到最左边int kevi = left;while (left < right){//右先走// 注意:left<right//right,找小while (left < right &&arr[right] >= arr[kevi]){right--;}//left找大while (left < right && arr[left]<= arr[kevi]){left++;}//交换(大和小的)Swap(&arr[left], &arr[right]);}//交换key和相遇点Swap(&arr[kevi], &arr[right]);kevi = right;		//更新kevi//递归// 右边大于kevi  左边小于kevi//此时[begin,kevi) kevi [kevi+1,end]QuickSort(arr, begin,kevi-1);QuickSort(arr, kevi+1,end);
}

运行结果:
在这里插入图片描述

挖坑法

1.思路
  • 选key,左边为key(坑)
  • 右边先走,找小(小于key),找到把值放key(坑里),此时right变新坑
  • 左边后走,找大(大于key),找到把值放key(坑里),此时left变新坑
  • 一直相遇,把一开始key的值放相遇点
  • 递归下去
2.图解

在这里插入图片描述

3.代码
//挖坑法
void QuickSort_keng(int* arr,int begin,int end)
{//递归结束if (begin >= end)return;//int left = begin;int right = end;int mid = Getmid(arr, left, right);			//三数取中Swap(&arr[mid], &arr[left]);				//换过来int kevi = arr[left];						//设置开始的坑位,保存key的值int keng = left;							//记录坑位的位置while (left < right){//right 找小while (left<right&&arr[right] >= kevi){right--;}Swap(&arr[right], &arr[keng]);			//交换,right位置变成新的坑位keng = right;//left找大while (left < right && arr[left] <= kevi){left++;}Swap(&arr[left], &arr[keng]);			//交换,left位置变成新的坑位keng = left;							}//最终把kevi放keng位置(相遇点)arr[keng] = kevi;//递归QuickSort_keng(arr,begin,keng-1);QuickSort_keng(arr, keng+1, end);}

在这里插入图片描述

前后指针法

1.思路
  • 1、选出一个key,一般是最左边或是最右边的。
  • 2、起始时,prev指针指向序列开头,cur指针指向prev+1(前后指针)
  • 3、若cur指向的内容小于key,则prev先向后移动一位,然后交换prev和cur指针指向的内容,然后cur指针++;
  • 4、若cur指向的内容大于key,则cur指针直接++。如此进行下去,直到cur到达end位置,此时将key和++prev指针指向的内容交换即可。

经过一次单趟排序,最终也能使得key左边的数据全部都小于key,key右边的数据全部都大于key

然后也还是将key的左序列和右序列再次进行这种单趟排序,如此反复操作下去,直到左右序列只有一个数据,或是左右序列不存在时,便停止操作

2.图解

在这里插入图片描述

3.代码
void Swap(int* p1, int* p2)
{int tmp = *p1;*p1 = *p2;*p2 = tmp;
}
//前后指针版本
void QuickSort_prev(int* arr, int begin, int end)
{//递归结束if (begin >= end) return;记录区间范围int left =begin;int right = end;int kevi = left;		//记录key的下标//前后指针int prev =left;int cur = left + 1;while (cur <= right){if (arr[cur] < arr[kevi] && ++prev != cur){//把小的交换到前面Swap(&arr[cur],&arr[prev]);}cur++;}//cur越界了,交换kevi和prevSwap(&arr[kevi], &arr[prev]);kevi = prev;//递归QuickSort_prev(arr, left, kevi - 1);QuickSort_prev(arr, kevi + 1, right);}

非递归法

1.思路

  • 使用stack来存储待处理的区间,先入栈的是右端点,然后是左端点

  • 在while循环中,只要栈不为空,就继续处理。

  • 每次从栈中弹出两个元素,分别代表当前处理区间的左右端点。

  • 执行一趟快速排序的划分操作,使用kevi作为基准元素的索引,prev用于记录小于基准元素的最后一个元素的位置。

  • 在while循环中,cur遍历当前区间,如果发现比基准元素小的元素,则将其与prev位置的元素交换。

  • 划分完成后,将基准元素与prev位置的元素交换,确保基准元素位于中间位置

  • 检查新的区间是否需要继续排序,如果需要,则将新的区间端点压入栈中

  • 重复步骤2-7,直到栈为空,这意味着所有元素都已经被排序。

2.图解

在这里插入图片描述

3.代码

这里得单趟,我套用了前面 前后指针的方法

//非递归
void QuickSort_None(int* arr, int begin, int end)
{stack<int> st;		//栈//先入右,后入左st.push(end);st.push(begin);//不为空while (!st.empty()){//出栈区间,开始一趟int left = st.top();st.pop();int right = st.top();st.pop();//走单趟int kevi = left;int prev = left;int cur = left+1;while (cur <= right){//把小的换到前面if (arr[cur] < arr[kevi] && ++prev != cur){Swap(&arr[prev], &arr[cur]);}++cur;}//把kevi换到相遇点Swap(&arr[kevi],&arr[prev]);kevi = prev;//入栈新区间-先右后左// [begin,kevi-1] kevi [kevi+1,end]if (kevi + 1 < right){st.push(end);		//先右后左st.push(kevi+1);}if (left<kevi-1){st.push(kevi-1);	// 先右后左st.push(left);}}
}

算法性能

在这里插入图片描述

1.时间复杂度

在这里插入图片描述

2.空间复杂度

在这里插入图片描述

3.稳定性

不稳定
下面2 2 1 的例子,两个2相对位置发生变化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

第十六周:机器学习笔记

第十六周周报 摘要Abstratc一、机器学习1. Pointer Network&#xff08;指针网络&#xff09;2. 生成式对抗网络&#xff08;Generative Adversarial Networks | GAN&#xff09;——&#xff08;上&#xff09;2.1 Generator&#xff08;生成器&#xff09;2.2 Discriminator&…

React 子组件调用父组件的方法,以及互相传递数据

<script type"text/babel" data-type"module"> import React, { StrictMode, useState } from react; import { createRoot } from react-dom/client;const ParentComponent () > {const [message, setMessage] useState("")//父组件…

C语言函数实现:深入理解strcpy

文章目录 一、strcpy函数的基本用法二、strcpy函数的实现原理三、strcpy函数的应用场景四、strcpy函数的安全性问题五、结论 C语言函数实现&#xff1a;深入理解strcpy 在C语言编程中&#xff0c;字符串处理是一项基础且重要的任务。 strcpy函数作为C标准库中的一个基本函数&a…

计算生物学与生物信息学漫谈-2-测序深度/读长质量和Fasta处理

上一篇文章中我们介绍了测序技术的由来与发展&#xff0c;那么在介绍第三代测序的时候&#xff0c;我们提到了关于测序深度和读长的问题&#xff0c;那么本篇文章就详解介绍一下。 计算生物学与生物信息学漫谈-1-测序一路走来-CSDN博客 目录 1.测序深度SEQUENCING DEPTH &…

现代物流管理:SpringBoot技术突破

3系统分析 3.1可行性分析 通过对本智能物流管理系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本智能物流管理系统采用SSM框架&#xff0c;JAVA作为开发语…

【SQL|大数据|数据清洗|过滤】where条件中 “ != “ 和 “ NOT IN() ” 对NULL的处理

对数据进行清洗过滤的时候&#xff0c;NULL往往是一个很特殊的存在&#xff0c;对NULL值的存在通常有以下三种方式 1、保留NULL 2、过滤掉NULL 3、将NULL替换为其他符合业务需求的默认常量 下面是一些常用处理NULL的方式&#xff1a; 如下图所示数据源&#xff1a; car_vin&…

嵌入式入门学习——6Protues点亮数码管,认识位码和段码,分辨共阴还是共阳(数字时钟第一步)

0 系列文章入口 嵌入式入门学习——0快速入门&#xff0c;Let‘s Do It&#xff01; 首先新建基于Arduino UNO的protues工程&#xff0c;见本系列第3篇文章 1 点“P”按钮找器件 2 输入“seg”或“digit”查找数码管器件 3 找到我们想要的6位7段数码管 4如图A、B…DP都是段码…

ESP32-C3 入门笔记04:gpio_key 按键 (ESP-IDF + VSCode)

1.GPIO简介 ESP32-C3是QFN32封装&#xff0c;GPIO引脚一共有22个&#xff0c;从GPIO0到GPIO21。 理论上&#xff0c;所有的IO都可以复用为任何外设功能&#xff0c;但有些引脚用作连接芯片内部FLASH或者外部FLASH功能时&#xff0c;官方不建议用作其它用途。 通过开发板的原…

自由学习记录(11)

Surface Effector 2D Platform Effector 2D 向上跳跃穿过天花板的功能 平台效应器不用变Trigger&#xff0c;因为本来就是要有碰撞的 use one way grouping是让所有相关联的碰撞器都可以单面跳墙 默认不勾选&#xff0c;左右两边没有摩擦力和弹力&#xff0c;要自己先设置sid…

三菱PLC伺服-停止位置不正确故障排查

停止位置不正确时&#xff0c;请确认以下项目。 1)请确认伺服放大器(驱动单元)的电子齿轮的设定是否正确。 2&#xff09;请确认原点位置是否偏移。 1、设计近点信号(DOG)时&#xff0c;请考虑有足够为0N的时间能充分减速到爬行速度。该指令在DOG的前端开始减速到爬行速度&…

计算机毕业设计 基于Web的景区管理系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

市场上几个跨平台开发框架?

跨平台桌面应用开发框架是一种工具或框架&#xff0c;它允许开发者使用一种统一的代码库或语言来创建能够在多个操作系统上运行的桌面应用程序。传统上&#xff0c;开发者需要为每个操作系统编写不同的代码&#xff0c;使用不同的开发工具和语言。而跨平台桌面应用开发框架通过…

【网络】IP协议的地址管理

【网络】IP协议的地址管理 一. IP协议格式二. 地址管理1.动态分配IP地址2.NAT机制2.1 NAT机制下网络的请求/响应 3. 网段划分3.1 特殊的IP地址 4.路由选择5.DNS域名解析系统 一. IP协议格式 4位版本号(version): 指定IP协议的版本&#xff08;IPv4/IPv6&#xff09;, 对于IPv4来…

一起搭WPF架构之livechart的MVVM使用介绍

一起搭WPF架构之livechart使用介绍 前言ModelViewModelView界面设计界面后端 效果总结 前言 简单的架构搭建已经快接近尾声了&#xff0c;考虑设计使用图表的形式将SQLite数据库中的数据展示出来。前期已经介绍了livechart的安装&#xff0c;今天就详细介绍一下livechart的使用…

第6篇:无线与移动网络

目录 引言 6.1 无线网络的基础概念 6.2 无线局域网&#xff08;WLAN&#xff09;与IEEE 802.11 6.3 蓝牙与无线个域网&#xff08;WPAN&#xff09; 6.4 无线城域网&#xff08;WMAN&#xff09;与WiMax 6.5 ZigBee与智能家居 6.6 移动蜂窝网络&#xff08;3G/4G/5G&…

SpringCloudStream使用StreamBridge实现延时队列

利用RabbitMQ实现消息的延迟队列 一、安装RabbitMQ 1、安装rabbitmq 安装可以看https://blog.csdn.net/qq_38618691/article/details/118223851,进行安装。 2、安装插件 安装完毕后,exchange是不支持延迟类型的,需要手动安装插件,需要和安装的rabbitmq版本一致 https:…

深入探讨C++多线程性能优化

深入探讨C多线程性能优化 在现代软件开发中&#xff0c;多线程编程已成为提升应用程序性能和响应速度的关键技术之一。尤其在C领域&#xff0c;多线程编程不仅能充分利用多核处理器的优势&#xff0c;还能显著提高计算密集型任务的效率。然而&#xff0c;多线程编程也带来了诸…

62天框架安全(学习)

发现学了之后没有去复习&#xff0c;每天都要问自己学了什么&#xff0c;复习了吗&#xff0c;下次还能记住吗 一下内容来自【小迪安全2023】第62天:服务攻防-框架安全&CVE复现&Spring&Struts&Laravel&ThinkPHP_小迪安全文档2023-CSDN博客 一个网站的源码…

排序算法 —— 直接插入排序

目录 1.直接插入排序的思想 2.直接插入排序的实现 实现分析 实现代码 3.直接插入排序的分析 时间复杂度分析 空间复杂度分析 稳定性 1.直接插入排序的思想 直接插入排序的思想就是把待排序的元素按其关键码值的大小依次插入到一个已经排好序的有序序列中&#xff0c…

一种基于OCR图像识别技术的发票采集管理系统及方法

一种基于OCR图像识别技术的发票采集管理系统及方法 摘要 本发明涉及了一种基于OCR图像识别技术的发票采集管理系统及方法&#xff0c;该系统的发票信息采集单元采集发票图片信息数据&#xff0c;OCR图像识别单元基于OCR图像识别技术并结合人工智能深度学习算法对发票图片信息数…