深入理解数据结构第五弹——排序(2)——快速排序

排序(1):深入了解数据结构第四弹——排序(1)——插入排序和希尔排序-CSDN博客

前言:

在前面我们已经讲过了几种排序方式,他们的效率有快有慢,今天我们来学习一种非常高效的排序方式——快速排序

目录

一、快速排序的思想

二、快速排序的递归实现

2.1 霍尔法

2.2 挖坑法

2.3 前后指针法

三、快排的非递归实现

四、完整代码示例

五、总结


一、快速排序的思想

快速排序是一种常用的排序算法,属于比较排序的一种。它的基本思想是先选取一个基准数据,经过一趟排序,让比它小的分为一部分,比它大的分为另一部分,然后再对这两部分继续这种操作,直到他们有序

快速排序的具体步骤如下:

  1. 选择一个基准元素(通常是待排序数组的第一个元素、最后一个元素或者中间元素)。
  2. 将比基准元素小的元素放在基准元素的左边,比基准元素大的元素放在基准元素的右边,这一步称为分区操作。
  3. 对基准元素左右两部分分别递归地进行快速排序。

比如这样一组数据{ 4,7,1,9,3,6,5,8,3,2,0 }

1、首先我们先选择一个基准元素(我们以最左边的元素为基准元素为例)

2、对剩下的元素进行排序,比基准元素小的排在左边,比基准元素大的排在右边

3、对小的部分和大的部分重复上面两部操作,最后我们就可以得到一个有序的数组

这一步就可以清楚的看到其实快排的这种思想很像二叉树,所以很容易通过类似二叉树递归的那种思想来解决

二、快速排序的递归实现
 

快排的实现其实是很有意思的,在上面我们已经讲了快排的思想,其实就是不断的重复分区操作的过程,所以我们就可以设计一个递归来实现这种,同时,由于每一步都要进行分区,所以我们可以封装一个分区排序函数(PartSort函数)在前,重复这个过程

void QuickSort(int* a, int begin,int end)
{if (begin >= end){return;}int keyi = PartSort3(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

其中参数a是数组指针,begin是传入数组的首元素位置,end是传入元素尾元素位置,过程图如下:

快排函数的主体就是上面那几步,接下来,我们重点讲解一下快排分区排序函数(PartSort函数)该如何实现,这一步也是非常有趣的,目前我们有三种方法来实现这个函数的功能:

      1、霍尔排序

      2、挖坑法

      3、前后指针法

2.1 霍尔法

霍尔法是霍尔大佬(就是快排的发明者)自己刚开始用的排序方法,但是由于这种分部排序方法需要注意到的点太多,所以后来才又有了后面两种排序方法,现在我们先来学习一下霍尔大佬的这种方法

霍尔排序其实就是严格按照我们上面讲的快排的那种思想进行的,就是先选一个基准数,然后对后面数进行大致的判断,让比基准数小的位于基准数左侧,比基准数大的位于基准数右侧

霍尔实现这个过程的方法就是先选取最左边的元素作为基准元素,然后记录剩下元素左右位置,然后让左边向右移动,当遇到一个比基准元素大的数就停下来,右边向左移动,遇到一个小于基准元素的数停下来,然后让左右这两个数交换

然后再讲左右两部分分开再进行类似的操作

由图可见这是一种类似二叉树的操作,所以非常适合用递归来解决,具体代码如下:

int GetMid(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] > a[mid]){if (a[right] > a[left])return left;else if (a[right] < a[mid])return mid;elsereturn right;}else{if (a[right] < a[left])return left;else if (a[right] > a[mid])return mid;elsereturn right;}
}
//1、hero 霍尔排序
//[left,right]
int PartSort(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int keyi = left;while (left < right){while (left < right && a[left] <= a[keyi]){left++;}while (left < right && a[right] >= a[keyi]){right--;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);return left;
}

但前面我们也已经提到过了,霍尔排序有一些细节是一定要处理到位的,就比如

1、如果取左边数为基准元素,右边就要先开始移动(right - -),反之就左边先开始移动,否则就容易可能会出现溢出的现象

2、要注意当左右移动到的那个元素等于基准元素时是要跳过的,同时最后left==right时要将这个元素与基准元素交换

3、如果对于一个较为有序的函数,比如{1,2,4,5,7,3,9,8},快排的效率其实是偏低的,因为我们刚开始选的基准元素基本没啥作用,所以我们选择的基准元素要尽可能贴近中间值,所以就有了上述代码中的GetMid函数
 

2.2 挖坑法

鉴于霍尔法注意事项太多,且霍尔法较难理解,后面又有大佬总结出挖坑法这一思路相同,但更形象更容易理解的方法

挖坑法的思路如下:

先以左边元素为基准元素,然后将这个元素挖出,将这个位置理想化成一个坑,然后再从右边向左边移动,找到一个小于基准数的数后将它放入坑中,将这个位置作为新的坑,再从左边往右边去,找到一个大于基准数字的数,填入坑中,将这个位置作为新坑,直到最后将基准数字放入最后的坑中

挖坑法的思路要比霍尔法,简单很多,实现如下:

//2、挖坑法
int PartSort2(int* a,int left,int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int key = a[left];int hole = left;while (left < right){while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;while (left<right && a[left]<=key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return left;
}

2.3 前后指针法

前后指针法是一个更容易理解的很不错的方法,体现了后人的智慧

前后指针法的思路如下:

首先先将最左边元素作为基准元素,然后定义一个prev表示后指针,定义一个cur表示前指针,cur=prev+1,然后让前指针先走,当遇到一个小于基准元素的数时停下来,然后让后指针走一步,然后交换这两个数据,直到前指针把所有数据走完

实现上述过程的代码:

//3、前后指针法
int PartSort3(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int prev = left;int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi]){prev++;Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[prev], &a[keyi]);return prev;
}

三、快排的非递归实现

这个由于篇幅问题,留在下一章进行讲解(其实是本人累了.......坐在这写一下午了,呜呜呜呜呜........),这个明天写,嘿嘿

四、完整代码示例

上面我们就已经把快排的几种分部处理的方法和思想讲的很清楚了,接下来,我们就通过一个完整的代码实例来感受快排的魅力所在

对数组{ 4,7,1,9,3,6,5,8,3,2,0 }进行快排

Seqlish.h

//快速排序
void QuickSort(int* a, int begin, int end);

Seqlish.c

//快速排序
void PrintArray(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}
void Swap(int* e1, int* e2)
{int tmp = *e1;*e1 = *e2;*e2 = tmp;
}
int GetMid(int* a, int left, int right)
{int mid = (left + right) / 2;if (a[left] > a[mid]){if (a[right] > a[left])return left;else if (a[right] < a[mid])return mid;elsereturn right;}else{if (a[right] < a[left])return left;else if (a[right] > a[mid])return mid;elsereturn right;}
}
//1、hero 霍尔排序
//[left,right]
int PartSort(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int keyi = left;while (left < right){while (left < right && a[left] <= a[keyi]){left++;}while (left < right && a[right] >= a[keyi]){right--;}Swap(&a[left], &a[right]);}Swap(&a[left], &a[keyi]);return left;
}
//2、挖坑法
int PartSort2(int* a,int left,int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int key = a[left];int hole = left;while (left < right){while (left < right && a[right] >= key){right--;}a[hole] = a[right];hole = right;while (left<right && a[left]<=key){left++;}a[hole] = a[left];hole = left;}a[hole] = key;return left;
}
//3、前后指针法
int PartSort3(int* a, int left, int right)
{int mid = GetMid(a, left, right);Swap(&a[mid], &a[left]);int prev = left;int cur = prev + 1;int keyi = left;while (cur <= right){if (a[cur] < a[keyi]){prev++;Swap(&a[prev], &a[cur]);}cur++;}Swap(&a[prev], &a[keyi]);return prev;
}
//递归的快速排序
void QuickSort(int* a, int begin,int end)
{if (begin >= end){return;}int keyi = PartSort3(a, begin, end);QuickSort(a, begin, keyi - 1);QuickSort(a, keyi + 1, end);
}

test.c

//测试快速排序
void TestQuick()
{int a[] = { 4,7,1,9,3,6,5,8,3,2,0 };PrintArray(a, sizeof(a) / sizeof(a[0]));//QuickSort(a, 0, sizeof(a) / sizeof(a[0]) - 1);      //递归快排QuickSortNonR(a, 0, sizeof(a) / sizeof(a[0]) - 1);    //非递归快排PrintArray(a, sizeof(a) / sizeof(a[0]));
}int main()
{TestQuick();return 0;
}

运行结果如下:

五、总结

总之,快排的思路是有点类似二叉树的,所以适合用递归来解决,当然,用非递归同样能处理,这里我们留下了一个尾巴,等下次解决,上面提到的这些内容,如果有不理解的地方欢迎私信与我交流或者在评论区中指出

谢谢各位大佬观看,创作不易,还请各位大佬点赞支持一下!!!

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

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

相关文章

笔记本电脑上的聊天机器人: 在英特尔 Meteor Lake 上运行 Phi-2

对应于其强大的能力&#xff0c;大语言模型 (LLM) 需要强大的算力支撑&#xff0c;而个人计算机上很难满足这一需求。因此&#xff0c;我们别无选择&#xff0c;只能将它们部署至由本地或云端托管的性能强大的定制 AI 服务器上。 为何需要将 LLM 推理本地化 如果我们可以在典配…

windows下使用nginx设置静态资源路由

1、下载nginx http://nginx.org/en/download.html 下载稳定版本&#xff0c;以nginx/Windows-1.24.0为例&#xff0c;下载后解压&#xff0c;解压后如下&#xff1a; 2、启动nginx 打开cmd命令窗口&#xff0c;切换到nginx解压目录下&#xff1a; start nginx # 启动nginx服务…

OpenWrt 多拨负载均衡不起作用

检查 负载均衡->规则->Https->粘滞模式 是否启动&#xff0c;设置为 否 如果设置为是&#xff0c;那么根据官方描述&#xff1a; 来自相同源 IP 的流量&#xff0c;如果已经匹配过此规则并且在粘滞超时时间内&#xff0c;将会使用相同的 WAN 接口 意思就是如果你同一个…

实验六 智能手机互联网程序设计(微信程序方向)实验报告

实验目的和要求 请完成创建图片库应用&#xff0c;显示一系列预设的图片。 提供按钮来切换显示不同类别的图片。 二、实验步骤与结果&#xff08;给出对应的代码或运行结果截图&#xff09; 1.WXML <view> <button bindtap"showAll">所有图片</but…

小程序视频怎么保存到mp4

小程序上的视频如何下载成mp4&#xff0c;本文就将教大家如何将小程序视频保存到mp4&#xff0c;这里要用到一个工具:下载高手 下载高手的文件我已经打包好了 下载高手链接&#xff1a;https://pan.baidu.com/s/1qJ81sNBzzzU0w6DWf-9Nxw?pwdl09r 提取码&#xff1a;l09r -…

PHP反序列化命令执行+PHP反序列化POP大链 +PHP反序列化基础

[题目信息]&#xff1a; 题目名称题目难度PHP反序列化命令执行1 [题目考点]&#xff1a; 反序列化命令执行&#xff0c;获取题目flag。[Flag格式]: SangFor{t5euvZ_OB8Jd_h2-}[环境部署]&#xff1a; docker-compose.yml文件或者docker tar原始文件。 docker-compose up …

DataGrip2024安装包(亲测可用)

目录 一、软件简介 二、软件下载 一、软件简介 DataGrip是由JetBrains公司开发的一款强大的关系数据库集成开发环境&#xff08;IDE&#xff09;&#xff0c;专为数据库开发人员和数据库管理员设计。它提供了一个统一的界面&#xff0c;用于管理和开发各种关系型数据库&#x…

用于半监督的图扩散网络 笔记

1 Title Graph Neural Diffusion Networks for Semi-supervised Learning&#xff08;Wei Ye, Zexi Huang, Yunqi Hong, and Ambuj Singh&#xff09;【2022】 2 Conclusion This paper proposes a new graph neural network called GND-Nets (for Graph Neural Diffu…

计算股价波动率python

上述图片上传gemini&#xff0c;提问&#xff1a;转换为python代码 好的&#xff0c;以下是您发送的图像中公式的 Python 代码&#xff1a; python def stock_volatility(prices, opening_prices, N): """ 计算股票价格的波动率。 参数&#xff1a; p…

vscode如何方便地添加todo和管理todo

如果想在vscode中更加方便的添加和管理TODO标签&#xff0c;比如添加高亮提醒和查看哪里有TODO标签等&#xff0c;就可以通过安装插件快速实现。 安装插件 VSCode关于TODO使用人数最多的插件是TODO Height和Todo Tree 按住 CtrlShiftX按键进入应用扩展商店&#xff0c;输入to…

潮玩宇宙小程序定制大逃杀游戏APP开发H5游戏

游戏名称&#xff1a;潮玩宇宙大逃杀 游戏类型&#xff1a;休闲竞技类小游戏 游戏目标&#xff1a;玩家通过选择房间躲避杀手&#xff0c;生存下来并瓜分被杀房间的元宝。 核心功能 房间选择&#xff1a;玩家进入游戏后&#xff0c;可以选择一间房间躲避杀手。杀手行动&…

机器学习——自动驾驶

本章我们主要学习以下内容: 阅读自动驾驶论文采集数据根据论文搭建自动驾驶神经网络训练模型在仿真环境中进行自动驾驶 论文介绍 本文参考自2016年英伟达发表的论文《End to End Learning for Self-Driving Cars》 📎end2end.pdf

c语言中的数组

数组 数组是一种构造类型&#xff0c;是由基本类型构造而成。当我们想用一群变量来描述同一类相同的东西时候&#xff0c;比如100个年龄变量&#xff0c;我们可以这样int age1&#xff1b;int age2&#xff1b;int age3&#xff1b;……int age 100;这样即不方便书写&#xff…

通过腾讯云搭建跨境电商demo的详细操作过程(建站系统 保姆级指导,巨详细)

引言&#xff1a; 有许多做跨境电商的朋友&#xff0c;或者为跨境电商服务的小企业&#xff0c;都会面临搭建电商平台V1.0的问题 因此&#xff0c;花了点时间&#xff0c;找了一个开源的项目&#xff0c;让大家可以跑起来&#xff0c;一方面了解平台都有哪些模块&#xff0c;另…

揭秘ebay、亚马逊测评系统:从稳定环境搭建到防关联技术

在亚马逊、ebay平台上进行自养号测评、L ka等活动&#xff0c;首要问题是确保环境的安全性和稳定性。一个稳定的环境是进行测评的基础&#xff0c;如果无法解决安全性问题&#xff0c;那么从事这些项目就不值得。我们在环境技术研发领域已经有8年的经验&#xff0c;在早期测试了…

创建k8s deploy yaml文件的imagePullSecrets语句

镜像仓库是harbor kubectl create secret docker-registry key --docker-server192.168.0.190 --docker-usernameadmin --docker-passwordHarbor12345

Lagent AgentLego 智能体介绍

本文主要介绍智能体相关基础知识&#xff0c;主流的智能体开源项目&#xff0c;重点介绍Lagent智能体和AgentLego框架。 一. 为什么要有智能体 目前的大预言模型有一些局限性&#xff0c;包括有时候会生成虚假信息&#xff0c;也就是我们说的“大模型胡言乱语”&#xff0c;还…

LeetCode 每日一题 Day 123-136

1379. 找出克隆二叉树中的相同节点 给你两棵二叉树&#xff0c;原始树 original 和克隆树 cloned&#xff0c;以及一个位于原始树 original 中的目标节点 target。 其中&#xff0c;克隆树 cloned 是原始树 original 的一个 副本 。 请找出在树 cloned 中&#xff0c;与 tar…

虚拟机下如何使用Docker(完整版)

Docker详细介绍&#xff1a; Docker 是一款开源的应用容器引擎&#xff0c;由Docker公司最初开发并在2013年发布。Docker的核心理念源自于操作系统级别的虚拟化技术&#xff0c;尤其是Linux上的容器技术&#xff08;如LXC&#xff09;&#xff0c;它为开发人员和系统管理员提供…

字体反爬积累知识

目录 一、什么是字体反扒 二、Unicode编码 三、利用font包获取映射关系 一、什么是字体反扒 字体反爬是一种常见的反爬虫技术&#xff0c;它通过将网页中的文本内容转换为特殊的字体格式来防止爬虫程序直接获取和解析文本信息。字体反爬的原理是将常规的字符映射到特殊的字…