排序进阶----快速排序

        当我们写了插入和希尔排序后,我们就应该搞更难的了吧。大家看名字就知道我们这篇博客的内容了吧。而且从名字上来看。快速排序就很快吧。那么为什么这个排序怎么能叫快速排序啊。我们希尔排序不是很快嘛。那么我们的快速排序肯定是有特殊之处嘞。不然这就太自负了。而且对于快速排序是用上了我们以前学的知识,二叉树。那我们为什么这么说嘞。我们下面就来讲讲嘛。

快速排序的思路

       其实对于看快速排序来说,我们理解的话还算是比较简单的。为什么这么说了,因为大家都已经大概了解了二叉数的用法了吧。我们这个快速排序设置一个值,但是这个值就是数组里面的,然后以他为主来左右行走。就是数组的头和尾,然后向中间寻找遇见比他大或者比他小的数,然后就进行交换,直到他们相遇。他们如果相遇了,那么可以表明的一件事就是他们相遇的地点一定会比我们设定的值大或者小。那么之所以会这样子呢,我们后面在实现的时候再谈论。所以总之上面我们说的就是把这个数组从左右两边开始移动后边的如果比我们设定值小,就先暂时停留,然后左边向中间移动,如果遇见比我们设定的值大的话就暂时停留,然后他们两个交换。接着继续往前走,如果他们两个相遇了的话,我们也能确定他们相遇的值是否比设定的值大或者小。后当他们相遇了之后,这就能用上我们二叉树的内容了。然后我们分左边和右边递归来实现这个。

快速排序的实现 

       当然实现快速排序的话,我们也还是像以前一样。先来写一个单趟。然后再实现整整体的过程。那么我们上面也说过了,需要先设定一个基准值。那我就先普遍设为头节点。当我们设置头节点后,我们还要向左右移动。那么我们这个还要再设置两个。那我当我们设置完这些之后,我们就可以开始向中间移动了。但是移动我们也有一个先后吧,到底是左先动还是右先动?如果我们以右先动的话,那么就是先找比我们设定值小的值就停下来然后就是左边移动。那么就会遇见比设定值小的值停下来。当达到这个目标之后我们就进行交换。知道他们两个相遇之后就结束。然后与这个值与我们的设定值进行交换。大家可能会想你怎么知道他们相遇的值会我设定的值小?这个大家需要先想一下。我们是让右边先走的。如果他们相遇,那肯定是右边走到了比他小的子才会停下来。然后左边才走。那么这是不是就确定了他们相遇的值比设定的值小。那么我们已经退出循环了,所以我们需要在手动的调整一下。这样,我们这单趟就结束了。

void xixi(int* a, int zuo, int you)
{
	//key确定基准值
	int key = zuo;
	int left = zuo;
	int right = you;
	while (left < right)
	{
		//right先行动,找到比key小的值
		while (left < right && a[right] >= a[key])
		{
			right--;
		}
		//left行动,找到比key大的值
		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		//到达指定位置,进行交换
		swap(&a[left], &a[right]);
	}
	//走完上面的步骤后,两个下标会相聚在一个位置
	//然后对这两个位置的值进行交换
	swap(&a[right], &a[key]);
}

        上面就是快速排序的单趟排序了,看起来是不是还是比较简单啊。就是比较确定下标然后交换数组的值。那么我们刚开始说过 快速排序是会用到二叉树的内容。我们既然单趟了那么下一趟怎么走呢?我不知道日常树就是递归值不一样而已。那么我们只需要将传进去的递归值改变从他们两个相遇的地方划分成为两部分,然后分为左部分和右部分。然后再分别理赔,周而复始,这样就排序好了。大家想想是不是这样的?所以在单趟排序的时候,我们还有一个事情需要处理一下,就是将key的值改变我们在最后的交换把key的改变写上,然后递归实现。

void QuickSort(int* a, int begin, int end)
{
	//结束条件
	if (begin >= end)
	{
		return;
	}
	//一趟的实现
	int left = begin;
	int right = end;
	int keyi = left;
	while (left < right)
	{
		//右边开始行动   一定要加上等于,因为快速排序找的是一定比它小的值
		while (left < right && a[keyi] <= a[right])
		{
			right--;
		}
		//左边开始行动
		while (left < right &&   a[left] <= a[keyi])
		{
			left++;
		}
		swap(&a[left], &a[right]);

	}
	swap(&(a[keyi]), &(a[right]));
	keyi = right;
	//[begin,keyi-1] keyi [keyi+1,end]
	QuickSort(a, begin, keyi - 1);//递归传递值
	QuickSort(a, keyi + 1, end);
}

         上面就是快速排序的基本方法了。当然都说只是基本方法了,那么他肯定还可以再进行优化。那他有哪些地方可以优化呢?我们接下来就来讲讲这些。

快速排序的优化 

1:      首先我们对于快速排序的优化就是我们最重要的key的值。我们快速排序,为什么key的值就始终是头节点了,我们不能换一个更加好一点儿的吗?所以我们的快速优化的第一个就是更改key的值。那么我们需要递归很多事,所以key的值需要更换很多次。那么我们就要写一个子函数来专门确定key的值。但是如何确定啊?这里就要用前辈的思想了。叫做三数取中。我们传递数组,和开头和结尾,然后我们计算开头和结尾中间的值比较,我们取中间的值,这样我们的快速排序就更加快速了。实现很简单:

//三数取中
int GetMid(int *a, int begin, int end)
{
	int mid = (begin + end) / 2;//平均的下标值
	if (a[begin] > a[end])//假设头大于尾的话
	{
		if (a[end] > a[mid])//尾大于平均那么尾就是中间的
			return end;
		else if (a[mid] > a[begin])//如过平均大于头的话头是中间值
			return begin;
		else//就只剩平均值了
			return mid;
	}
	else//(a[begin] < a[end])假设头大小于尾的话
	{
		if (a[end] < a[mid])//平均大于尾。则为是中间
			return end;
		else if (a[begin] < a[mid])//平均大于头的话,平均就是中间
			return mid;
		else//就只剩头了
			return begin;
	}
}

2:          另外一个优化方法就简单粗暴了许多了。因为我们都说快速排序嘛,要比我们前面学了几个排序都要好一些,而且我们也知道我们前面写的几个排序中插入排序对。小数组计算更快。我们就判断如果数组大于十的话,我们就快拍,如果小于十的话,我们就用插入排序。这就是大的用大的方法,小的用小的方法,这样我们是不是要方便许多? 但是我们要注意1.插入排序之后两个参数,一个是数据集合的起点地址,第二个是数据量。2.使用插入排序时,我们要传入待排序数据集合的其实地址,即a+begin,如果传入的是a,那排序的永远都是数组a的前n个区间。3.插入排序传入的是数据个数,所以我们要将end-begin加上1之后才传入。快速排序中end、begin都是闭区间(即数组下标)。如果大家对于插入排序还是不了解的话,可以看一下我上一篇博客。

void QuickSort_Pointer(int* a, int begin, int end)
{
	if (begin >= end)
	{
		return;
	}
	//数据区间大与10,进行快速排序
	if (end - begin > 10)
	{
		int prev = begin;
		int cur = begin + 1;
		int keyi = begin;
		//三数取中后对keyi位置的值进行交换
		int mid = GetMid(a, begin, end);
		swap(&a[mid], &a[keyi]);
		while (cur <= end)
		{
			if (a[cur] < a[keyi] && ((++prev) != cur))
			{
				swap(&a[cur], &a[prev]);
			}
			cur++;
		}
		swap(&a[prev], &a[keyi]);
		keyi = prev;
		//开始进行递归
		QuickSort_Pointer(a, begin, keyi - 1);
		QuickSort_Pointer(a, keyi + 1, end);
	}
	else
	{
		//左闭右闭
		InsertSort(a, end - begin + 1);
		InsertSort(a + begin, end - begin + 1);
	}
 
}
总结

      那么说明的就是我们快速拍摄的大概所有内容了。当然还有一种优化其实也是可以的,叫做挖坑法。这个如果大家感兴趣的话可以自己去了解一下。当然我的内容是不是很完全的。所以大家可以在评论区里面也说一下,自己感兴趣的话,我下一篇博客会补充的。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【简单讲解下Fine-tuning BERT,什么是Fine-tuning BERT?】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

paddleocr快速入门:基于python脚本及命令行两种方式实现图片OCR识别

本篇将再讲讲paddleocr在图像OCR识别方面的应用。 一、paddlecor参数说明 字段说明默认值use_gpu是否使用GPUTRUEgpu_mem初始化占用的GPU内存大小8000Mimage_dir通过命令行调用时执行预测的图片或文件夹路径page_num当输入类型为pdf文件时有效&#xff0c;指定预测前面page_nu…

R语言ggplot2包绘制世界地图

数据和代码获取&#xff1a;请查看主页个人信息&#xff01;&#xff01;&#xff01; 1. 数据读取与处理 首先&#xff0c;从CSV文件中读取数据&#xff0c;并计算各国每日收入的平均签证成本。 library(tidyverse) ​ df <- read_csv("df.csv") %>% group_…

MAC帧

基本问题 数据链路层的协议有很多&#xff0c;但是都有三个基本问题&#xff1a;封装成帧&#xff0c;透明传输和差错检测。 封装成帧 封装成帧&#xff08;Framing&#xff09;就是在一段数据的前后分别添加首部和尾部&#xff0c;这样就构成了一个帧。帧是数据链路层的传送…

css 中clip 属性和替代方案 clip-path属性使用

clip clip 属性概述 作用&#xff1a;clip 属性用于定义一个裁剪区域&#xff0c;该区域外的元素内容将不可见。适用元素&#xff1a;clip 属性只对绝对定位&#xff08;position: absolute&#xff09;或固定定位&#xff08;position: fixed&#xff09;的元素有效&#xf…

掘金AI 商战宝典-高阶班:如何用AI制作视频(11节视频课)

课程目录&#xff1a; 1-第一讲用AI自动做视频&#xff08;上&#xff09;_1.mp4 2-第二讲用AI自动做视频&#xff08;中&#xff09;_1.mp4 3-第四讲A1做视频实战&#xff1a;店铺宣传_1.mp4 4-第五讲Al做视频实战&#xff1a;商品带贷1.mp4 5-第六讲Al做视频实战&#x…

码随想录算法训练营第二十四天| 77. 组合

77. 组合 - 力扣&#xff08;LeetCode&#xff09; class Solution {ArrayList<Integer> path new ArrayList<>();ArrayList<List<Integer>> result new ArrayList<>();public List<List<Integer>> combine(int n, int k) {if(n &…

SAP揭秘者- SAP PP模块日常常见运维问题之工单入库失败原因分析及快速处理

文章摘要&#xff1a; 无论您是负责SAP实施项目还是负责SAP运维项目&#xff0c;当用户发现有SAP PP模块的各种异常问题的时都需要作为SAP PP顾问的您快速地理解用户提交的问题&#xff0c;并快速地解决这些问题&#xff0c; 上篇文章跟大家聊了基本单位维护错了怎么修改的解决…

qt按钮的autoRepeat属性和default属性

autoRepeat属性&#xff1a;按住按钮不松&#xff0c;表示一直在点击按钮 default属性&#xff1a;点击Enter键表示在点击按钮

02Docker中的镜像和容器命令

镜像和容器 Docker中有镜像和容器的概念 镜像(Image): Docker将应用程序及其运行所需要的依赖、函数库、环境、配置等文件打包在一起称为镜像即硬盘中的文件容器(Container): 镜像中的应用程序运行起来并加载到内存中后形成的进程就是容器,一个镜像可以运行多个容器将来形成集…

计算机毕业设计hadoop++hive微博舆情预测 微博舆情分析 微博推荐系统 微博预警系统 微博数据分析可视化大屏 微博情感分析 微博爬虫 知识图谱

摘 要 随着社交媒体的普及和互联网技术的快速发展&#xff0c;热点舆情事件频发&#xff0c;对于政府、企业和公众来说&#xff0c;及时了解和分析热点舆情&#xff0c;把握舆论走向&#xff0c;已经成为一项重要的任务。然而&#xff0c;传统的数据处理和分析方法在面对海量…

华为 2024 届实习校园招聘-硬件通⽤/单板开发——第五套

华为 2024 届实习校园招聘-硬件通⽤/单板开发——第五套 部分题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09;&#xff08;共十套&#xff0c;每套四十题选择题&#xff09;获取&#xff08;WX:…

Java18新版本特性!

Java 18引入了多项新特性&#xff0c;主要包括默认UTF-8字符集、简单的Web服务器、栈步进API等。Java 18是Oracle在2022年发布的版本&#xff0c;其旨在通过一系列创新特性来提升开发效率与性能。下面将逐一探讨Java 18的主要新特性以及它们对开发者的具体影响&#xff1a; 默认…

【C语言】10.C语言指针(4)

文章目录 1.回调函数是什么&#xff1f;2.qsort 使⽤举例2.1 使⽤qsort函数排序整型数据2.2 使⽤qsort排序结构数据 3.qsort函数的模拟实现 1.回调函数是什么&#xff1f; 回调函数就是一个通过函数指针调用的函数。 如果你把函数的指针&#xff08;地址&#xff09;作为参数…

Prime1 - 信息收集和分析能力的试炼

主机发现 nmap扫描与分析 端口22、80 详细扫描&#xff1b;linux、ubuntu、 udp扫描 端口都是关闭的 脚本扫描 web渗透 打开只有一张图片&#xff1b;源码有图片和一个alt&#xff1a;hnp security不知道有啥用&#xff0c;先记录下来吧 继续web渗透思路走吧&#xff0c;目录…

线性代数|机器学习-P3乘法和因式分解矩阵

文章目录 1. 矩阵分解2. S Q Λ Q T SQ\Lambda Q^T SQΛQT3. A U Σ V T AU\Sigma V^T AUΣVT4. A LU 分解5. 矩阵的四个子空间 1. 矩阵分解 目前我们有很多重要的矩阵分解&#xff0c;每个分解对应于多个前提条件&#xff0c;分解方法&#xff0c;分解后的形状会中如下&…

如何跨渠道分析销售数据 - 6年制造业销售经验小结

如何跨渠道分析销售数据 - 6年制造业销售经验小结&#xff08;1&#xff09; 【前言】 在我过去6年销售工作生涯中&#xff0c;从第一年成为公司销冠后&#xff0c;我当时的确自满的一段时间&#xff0c;认为自己很了不起。但是第一年的销售业绩并没有拿到提成&#xff0c;最…

“一键”掌控数据库特权,DpEasy 新版本即将启航

去年11月&#xff0c;我们在 BinTools 社区推出了一款新产品——DpEasy。在我们最初设计这款产品的时候&#xff0c;我们给出的定位是「数据库安全风险扫描工具」&#xff0c;目标是提供一种简单、安全且高效的方式来管理数据库账号密码以及分析数据库账号的使用情况&#xff0…

Python开发与应用实验1 | 开发环境安装配置

*本文来自博主对专业课 Python开发与应用 实验部分的整理与解析。 *一些题目可能会增加了拓展部分&#xff08;⭐&#xff09;。拓展部分不是实验报告中原有的内容&#xff0c;而是博主本人的补充&#xff0c;以便各位学习参考。 *实验环境为&#xff1a;Python 3.10 &#xf…

[AFCTF 2018]JPython

小祥为了保护自己的代码&#xff0c;修改了部分Python Bytecode指令集&#xff0c;并把这个指令集称之为JPython&#xff0c; JPython只能在他私人定制的环境上才能运行&#xff0c;其他人无法得到这个环境。 现在&#xff0c;小明为了获取小祥代码中的秘密&#xff0c;收集到了…