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

在这里插入图片描述


快速排序法

在这里插入图片描述


递归法

霍尔版本(左右指针法)

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;
	//选左边为key
	int 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和prev
	Swap(&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相对位置发生变化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Arduino配置ESP32环境

Arduino配置ESP32环境 引言一、IDE下载教程操作取巧方法 二、社区安装包三、官方手动安装 引言 最近入手了一款ESP32-C3的开发板&#xff0c;想继续沿用现有Arduino IDE&#xff0c;网上看了很多方法&#xff0c;大致分了三类&#xff1a;IDE下载、社区安装包、github手动配置…

基于SpringBoot+Vue+uniapp的诗词学习系统的详细设计和实现

详细视频演示 请联系我获取更详细的演示视频 项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不…

ROS理论与实践学习笔记——5 ROS机器人系统仿真之URDF(Unified Robot Description Format)语法详解

URDF 文件是一个标准的 XML 文件格式&#xff0c;用于在 ROS 中描述机器人模型的结构。URDF 通过预定义的一系列标签&#xff0c;简洁地表达机器人的组成和运动关系。虽然机器人模型可能非常复杂&#xff0c;但在 URDF 中可以主要简化为两个核心部分&#xff1a; 连杆&#xff…

6.2 遍历重定位表

本节我们将编写一个遍历重定位表的示例程序&#xff0c;打印重定位表。 本节必须掌握的知识点&#xff1a; 遍历重定位表 6.2.1 遍历重定位表 实验四十三&#xff1a;遍历重定位表 以下代码实现打印"c:\\notepad64.exe"进程重定位表的所有信息。 /*--------------…

【详尽-实战篇】使用Springboot生成自带logo或者图片的二维码-扫描二维码可以跳转到指定的页面-Zing-core

先上效果图 项目源码&#xff1a;https://download.csdn.net/download/qq_43055855/89891285 源码地址 手机扫描二维码跳转到指定网页 概述 这个项目是一个基于 Java 的二维码生成与解析工具&#xff0c;主要由 QRCodeUtil 和 QRCodeController 两个类组成。它利用了 Google…

python 爬虫 入门 一、基础工具

目录 一&#xff0c;网页开发者工具的使用 二、通过python发送请求 &#xff08;一&#xff09;、get &#xff08;二&#xff09;、带参数的get &#xff08;三&#xff09;、post 后续&#xff1a;数据解析 一&#xff0c;网页开发者工具的使用 我们可以用 requests 库…

人脸识别-特征算法

文章目录 一、LBPH算法1.基本原理2.实现步骤3.代码实现 二、Eigenfaces算法1.特点2.代码实习 三、FisherFaces算法1.算法原理2.算法特点3.代码实现 四、总结 人脸识别特征识别器是数字信息发展中的一种生物特征识别技术&#xff0c;其核心在于通过特定的算法和技术手段&#xf…

leader必备技能——编写高质量测试计划

前言 作为一个想成为leader(不论是整个测试部门还是小项目组的leader&#xff09;的人&#xff0c;测试计划编写是必备技能。 接下来我们先了解一下测试计划的一些基础知识再进一步了解。 什么是测试计划&#xff1f; 测试计划是对测试过程的整体设计&#xff0c;测试计划确…

Spring Boot知识管理:智能搜索与分析

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

c#中多态的实例应用说明

在C#中&#xff0c;多态性是通过继承和实现接口来实现的&#xff0c;允许编写可以使用基类型的代码&#xff0c;然后使用派生类型的特定行为。 一.实例界面显示 二.源码界面显示 //定义的基类abstract class Shape{public abstract int Area();//基类中的抽象方法}//定义矩形的…

【前端】如何制作一个自己的网页(6)

接上文 网络中的图片 我们也可以在百度等网站搜索自己喜欢的图片。 此时对图片点击右键&#xff0c;选择【复制图片地址】&#xff0c;即可获得该图片的网络地址。 其实在HTML中&#xff0c;除了图片以外&#xff0c;我们还可以利用地址找到另一个网页。 如右图所示&#…

第一次排查 Java 内存泄漏,别人觉得惊险为什么我觉得脸红害羞呢

今天前端一直在群里说&#xff0c;服务是不是又挂了&#xff1f;一直返回 503。我一听这不对劲&#xff0c;赶紧看了一眼 K8S 的 pod 状态&#xff0c;居然重启了4次。测试环境只有一个副本&#xff0c;所以赶紧把副本数给上调到了3个。 堵住前端的嘴&#xff0c;免得破坏我在…

【C语言】一维数组应用Fibonacci数列

Fibonacci数&#xff08;斐波那契数列&#xff09; 前两项为1&#xff0c;从第三项开始&#xff0c;每一项为前两项的和。可以知道连续三项的关系&#xff1a;f[i]f[i-1]f[i-2] 使用数组进行存储&#xff0c;十分方便。可以知道前n项的fibonacci数。 #include <stdio.h>…

数据治理(2)-数据标准

前言 在建模前规划制定数据标准&#xff0c;或在建模使用过程中根据业务情况沉淀企业业务的数据标准。通过规范约束标准代码、度量单位、字段标准、命名词典&#xff0c;来保障数据处理的一致性&#xff0c;从源头上保障数据的标准化生产&#xff0c;节约后续数据应用和处理的…

什么是 C/2023 A3(紫金山-阿特拉斯)彗星?让我们用 Python 来绘制它的路径

彗星的基本概念 彗星&#xff08;Comet&#xff09;&#xff0c;是指进入太阳系内亮度和形状会随日距变化而变化的绕日运动的天体&#xff0c;呈云雾状的独特外貌&#xff0c;也是中国神话传说的扫帚星&#xff08;星官名&#xff09;。彗星分为彗核、彗发、彗尾三部分。彗核由…

一起体验AI动手实验,OceanBase 2024 年度发布会精彩预告

2024年OceanBase年度发布会将于10月23日在北京望京凯悦酒店举行。此次大会围绕“不止于记录”的主题&#xff0c;共同探讨当前数据库领域的前沿话题&#xff0c;包含主论坛、分论坛、AI 动手实训营、开源技术交流会等多个环节&#xff0c;诚邀全国各地的企业和开发者共同参与&a…

一个月学会Java 第18天 容器与泛型(有容器的原码解读)

Day18 容器与泛型 我们来简单讲讲容器是什么&#xff0c;顾名思义&#xff0c;是存东西的器皿&#xff0c;就叫做容器&#xff0c;那在我们计算机中需要存的是什么呢&#xff0c;是不是就是数据啊&#xff0c;所以我们的java是有提供一系列数据容器的&#xff0c;容器我们也叫做…

Redis:分布式 - 集群

Redis&#xff1a;分布式 - 集群 集群数据分片哈希求余一致性哈希算法哈希槽分区算法 Docker搭建集群集群操作重定向故障转移集群扩容 集群 在主从复制与哨兵模式中&#xff0c;数据库的数据对于每一台主机来说&#xff0c;都是全量保存的。这就会导致&#xff0c;就算引入再多…

Unity网络开发基础 —— 实践小项目

概述 接Unity网络开发基础 导入基础知识中的代码 需求分析 手动写Handler类 手动书写消息池 using GamePlayer; using System; using System.Collections; using System.Collections.Generic; using UnityEngine;/// <summary> /// 消息池中 主要是用于 注册 ID和消息类…

ps提示不能使用移动工具,因为目标通道被隐藏的解决办法

解决&#xff1a;按F7&#xff0c;或者从窗口把图层打开 按图示找到快速蒙版图层。它可能被隐藏或以特殊图标显示。右键删除或者拖到右下角垃圾桶里