数据结构--快速排序

news2024/12/28 20:15:23

文章目录

  • 快速排序的概念
  • Hoare版本
  • 挖坑法
  • 前后指针法
  • 快速排序的优化
    • 三数取中法
    • 小区间用插入排序
  • 非递归的快速排序

快速排序的概念

快速排序是通过二叉树的思想,先设定一个值,通过比较,比它大的放在它的右边,比它小的放在它的左边;这样相当于在二叉树中,小的放在左子树,大的放在右子树,设定的值就是根;再通过递归的思想,将它们继续按这种方式进行排序,排到最后就排好了;这就是快速排序的概念。

void QuickSort(int* a, int left,int right)
{
	//终止条件
	if (left >= right)
	{
		return;
	}
	//获取key值,为递归条件做准备
		int key = PartSort(a, left, right);
		//key变为分隔的中间值了
		QuickSort(a, left, key - 1);
		QuickSort(a, key + 1, right);
	
}

在现在常见的递归函数中,有几个版本(改变PartSort);

Hoare版本

在这里插入图片描述

//HOARE
int PartSort(int* a, int left, int right)
{
	int key = left;
	while (left < right)
	{
		while (left<right && a[right] >= a[key])
		{
			right--;
		}
		while (left<right && a[left] <= a[key])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	
	Swap(&a[key], &a[left]);
	return left;
}

函数进来我们总会以最左边的left变为key,由于最后需要实现位置交换不能将key记住值,而应该是下标,这样才能进行位置交换,否则只是一个复制的值而已,无法实现位置交换。

内层循环为什么也要写left<right
对于循环来说,不像if语句一样,只判断一次,while语句会不断的进行判断,然后执行里面的语句;如果对于条件符合的话,那么left就有可能超过了left,left位置不定,可能会越界或者造成数据混乱,所以也要在内层循环中加上这个条件。

当左右指针指定的值与key判断时,至少需要一个指针指向的元素需要判断等于key指向的元素,
在这里插入图片描述
如果没有加上的话,举个例子:
6 1 2 6 4 5 7 8 9 6 10,左右指针会指向6,进行交换后还是6,将会陷入死循环;

我们在最后a[key]和a[left]进行交换的时候,没有进行交换判断,是怎么确保a[left]小于key的呢?
在内层循环中,我们是先走的是右指针,再走左指针;这样就保证了right只有走到小于a[key]或者遇到left才会停下
left初始位置至少也是a[key]怎么说都会小于等于a[key];
这样就保证在终止条件下,a[left]总会小于等于a[eky];

挖坑法

有人觉得上面的方法需要通过先右指针先走,再左指针走太麻烦了,所以有了个挖坑法;
在这里插入图片描述

//挖坑法
int PartSort2(int* a, int left, int right)
{
	
	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;
}

前后指针法

通过定义两个指针,通过一定要求,将小的值一直往数组前面丢;
在这里插入图片描述

//指针法
int PartSort3(int* a, int left, int right)
{
	
	int key = left;
	int prev = left;
	int cur = left + 1;

	while (cur <= right)
	{
	
		if (a[cur] < a[key] && ++prev!=cur)
		{
			Swap(&a[prev], &a[cur]);
			
		}
		cur++;
	}
	Swap(&a[key], &a[prev]);
	return prev;
}

cur指针是用来循环遍历的,可以一直cur++;而只有满足if语句条件时,才进行交换;

验证:

void TestQuickSort1()
{
	int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };
	QuickSort(a, 0,sizeof(a) / sizeof(a[0])-1);
	PrintfArray(a, sizeof(a) / sizeof(a[0]));
}
int main()
{

	TestQuickSort1();
	return 0;
}

在这里插入图片描述

快速排序的优化

一般来说,快速排序的时间复杂度为O(N*logN)
在这里插入图片描述

三数取中法

我们对于key的取值,都是取最左边的数(left),如果一开始数组就是有序的话,我们的快速排序就是O(N^2);
在这里插入图片描述
所以就有了三数取中法这种优化方法:通过left、mid中间值、right这三个位置的值进行比较、取它们三个数排中间大的数;这样就会可避免上面这种极端情况;

int GetMidi(int* a, int left, int right)
{
	int midi = (left + right) / 2;
	if (a[midi] > a[left])
	{
		if (a[midi] < a[right])
		{
			return midi;
		}
		//上面条件不成立,也就默认a[mid]是最大的了
		else if (a[right] > a[left])//midi最大
		{
			return right;
		}
		else
		{
			return left;
		}
	}
	else
	{
		if (a[midi] > a[right])
		{
			return midi;
		}
		//上面条件不成立,也就默认a[mid]是最小的了
		else if (a[left] > a[right]) //midi最小
		{
			return right;
		}
		else
		{
			return left;
		}
	}
}

先判断中间数和左数的大小,再判断中间数和右数的大小

在这里插入图片描述

小区间用插入排序

对于小区间来说,如果使用递归的方式来实现排序,会使用比较多的时间
在这里插入图片描述
如果使用插入排序,当区间小于10时,对于有预排序的数组来说,插入排序会快很多,而快速排序通过不断的递归,就相当于实现预排序;我们可以验证一下。

void QuickSort(int* a, int left,int right)
{
	//终止条件
	if (left >= right)
	{
		return;
	}
	//对于递归的分割,当分割区间小于10,用直接插入排序
	if ((right - left + 1) > 10)
	{
		int key = PartSort3(a, left, right);
		//key变为分隔的中间值了
		QuickSort(a, left, key - 1);
		QuickSort(a, key + 1, right);
	}
	else
	{
		InsertSort(a + left, right - left + 1);
	}
}

void TestQuickSort1()
{
	isrand(time(NULL));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	//赋值
	for (int i = 0; i < N; i++)
	{
		a1[i] = rand();

	}
	int begin5 = clock();
	QuickSort(a1, 0,N-1);
	int end5 = clock();
	printf("QuickSort:%d\n", end5 - begin5);
}

在这里插入图片描述

非递归的快速排序

我们可以利用栈来模拟实现快速排序的递归方式,但这与用递归实现的排序在本质是不同的,递归需要不断的开辟栈区的空间,而我们将使用动态栈,占用的是堆区的空间,堆区的空间比栈区的大得多,在一定程度上能避免空间溢出;

使用数据结构的栈,利用它的后进先出的道理,可以实现递归的效果;
![在这里插入图片描述](https://img-blog.csdnimg.cn/960b19d52a6c40719d3ce57bf381e87f.png

//利用入栈的方法思想递归方式
void QuickSortNonR(int* a, int left, int right)
{
	//创栈
	ST stack;
	STInit(&stack);
	//先将第一个插入
	STPush(&stack, right);
	STPush(&stack, left);
	while (!STEmpty(&stack))
	{
		
		int key = PartSort2(a, left, right);
		if (key + 1 < right)
		{
			STPush(&stack, right);
			STPush(&stack, key + 1);
		}
		if (left < key - 1)
		{
			STPush(&stack, key - 1);
			STPush(&stack, left);
		}
		//更新左右指针
		left = STTop(&stack);
		STPop(&stack);
		right = STTop(&stack);
		STPop(&stack);

	}
	STDestory(&stack);
}

在循环里面,还需要判断左右指针的位置,因为需要判断最后进入栈的序列下标,循环还需要完成排序取出下标的数组;

验证:

void TestQuickSort2()
{
	int a[] = { 9,1,2,5,7,4,8,6,3,5,1,2,3,5,1,8,3 };
	QuickSortNonR(a, 0, sizeof(a) / sizeof(a[0]) - 1);
	PrintfArray(a, sizeof(a) / sizeof(a[0]));
}

在这里插入图片描述

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

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

相关文章

Python中的数据常见问题

数据可视化在Python中是一个非常重要的主题&#xff0c;它可以帮助我们更好地理解和分析数据。无论是探索数据的特征&#xff0c;还是向其他人展示数据的结果&#xff0c;数据可视化都起到了关键作用。然而&#xff0c;在进行数据可视化时可能会遇到一些常见问题。本文将为您分…

基于微信小程序的同城家政服务预约系统(源码+lw+部署文档+讲解等)

文章目录 前言系统主要功能&#xff1a;具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计…

DC电源模块关于宽电压输入和输出的范围

BOSHIDA DC电源模块关于宽电压输入和输出的范围 DC电源模块是一种电子设备&#xff0c;能够将输入的直流电源转换成所需的输出电源&#xff0c;用于供电各种电子设备。其中&#xff0c;关于宽电压输入和输出的范围&#xff0c;是DC电源模块常见的设计要求之一。本文将详细介绍…

嵌入式Linux应用开发-基础知识及GCC 编译器的使用

嵌入式Linux应用开发-基础知识及GCC 编译器的使用 第一章 HelloWorld 背后没那么简单1.1 交叉编译 hello.c1.2 请回答这几个问题1.3 演示 (...) 第二章 GCC 编译器的使用2.1 配套视频内容大纲2.1.1 GCC 编译过程(精简版)2.1.2 常用编译选项2.1.3 怎么编译多个文件2.1.4 制作、使…

深度学习:模型训练过程中Trying to backward through the graph a second time解决方案

1 问题描述 在训练lstm网络过程中出现如下错误&#xff1a; Traceback (most recent call last):File "D:\code\lstm_emotion_analyse\text_analyse.py", line 82, in <module>loss.backward()File "C:\Users\lishu\anaconda3\envs\pt2\lib\site-packag…

【Unity】LODGroup 计算公式

Unity 在配置 LodGroup 时&#xff0c;其分级切换的计算方法是按照物体在相机视野中占据的比例计算的。在运行时&#xff0c;如果相机视野范围&#xff08;Field of View&#xff09;没有改变&#xff0c;那么这个值可以直接换算成物体距离相机的距离。这里就讨论下如何计算得到…

ubuntu下用pycharm专业版连接AI服务器及其docker环境

一&#xff1a;用pycharm专业版连接AI服务器 1、首先在自己电脑上新建一个文件夹&#xff0c;后续用于映射服务器上自己所要用的项目文件 2、用pycharm专业版打开该文件夹&#xff0c;作为一个项目打开 3、然后在工具->部署->配置 4、配置中形式如下&#xff1a; 点击左…

Chatbot UI集成LocalAI实现自托管的ChatGPT

本文比惯例提前了一天发&#xff0c;因为明天一早&#xff0c;老苏就踏上回乡的路了&#xff0c;三年没回老家&#xff0c;这次专门请了 2 天的假 难得回家&#xff0c;打算多陪陪家人&#xff0c;和多年不见的朋友聚聚&#xff0c;当然如果有网络条件&#xff0c;还是会正常发…

英语单词记忆学习打卡系统 微信小程序

本单词记忆系统使用了计算机语言Java和存放数据的仓库MySQL&#xff0c;采用了微信小程序模式来实现。本系统使用了框架SSM和Uni-weixin实现了单词记忆系统应有的功能&#xff0c;系统主要角色包括管理员和用户。 关键词&#xff1a;Java&#xff1b;MySQL&#xff1b;SSM  在…

Unity实现设计模式——命令模式

Unity实现设计模式——命令模式 推荐一个Unity学习设计模式很好的GitHub地址&#xff1a;https://github.com/QianMo/Unity-Design-Pattern 有非常多的Star 一、介绍 命令模式使得请求的发送者与请求的执行者之间消除耦合&#xff0c;让对象之间的调用关系更加灵活。在命令模…

聊聊零拷贝技术原理和应用

文章目录 0. 引言1. 什么是零拷贝技术 1. 零拷贝技术在不同领域的应用2.传统拷贝技术的缺点3. 零拷贝技术的原理与实现1. sendfile系统调用2. 内核缓冲区与用户缓冲区3. DMA&#xff08;Direct Memory Access&#xff09;技术4. 文件描述符传递与共享5. Direct I/O&#xff08;…

Apache shiro RegExPatternMatcher 权限绕过漏洞 (CVE-2022-32532)

漏洞描述 2022年6月29日&#xff0c;Apache 官方披露 Apache Shiro &#xff08;CVE-2022-32532&#xff09;权限绕过漏洞。 当Apache Shiro中使用RegexRequestMatcher进行权限配置&#xff0c;且正则表达式中携带"."时&#xff0c;未经授权的远程攻击者可通过构造恶…

基于Springboot实现毕业生信息招聘平台管理系统演示【项目源码+论文说明】分享

基于Springboot实现毕业生信息招聘平台管理系统演示 摘要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 毕业生信息招聘平台&#xff0c;主要的模块包括查看管理员&#xff1b;首页、个人中心、企…

Nginx 可视化管理工具与 cpolar 配置:实现远程访问本地服务的优化

文章目录 前言1. docker 一键安装2. 本地访问3. Linux 安装cpolar4. 配置公网访问地址5. 公网远程访问6. 固定公网地址 前言 Nginx Proxy Manager 是一个开源的反向代理工具&#xff0c;不需要了解太多 Nginx 或 Letsencrypt 的相关知识&#xff0c;即可快速将你的服务暴露到外…

服务断路器_服务雪崩解决方案之服务降级

什么是服务降级 两种场景: 当下游的服务因为某种原因响应过慢&#xff0c;下游服务主动停掉一些不太重要的业务&#xff0c;释放出服务器资源&#xff0c;增加响应速度&#xff01;当下游的服务因为某种原因不可用&#xff0c;上游主动调用本地的一些降级逻辑&#xff0c;避免…

SPA移动端解决方案参考

企业在实现SAP移动化时遇到的一些挑战&#xff0c;如果我们利用自己开发团队来进行应用程序的开发&#xff0c;可能会陷入规划&#xff0c;开发&#xff0c;调试&#xff0c;测试的循环中&#xff0c;最后仍一无所获。那如果企业寻找第三方咨询公司进行开发的话&#xff0c;又担…

【高阶数据结构】哈希的应用 {位图;std::bitset;位图的应用;布隆过滤器;布隆过滤器的应用}

一、位图 1.1 位图概念 面试题 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中。【腾讯】 遍历查找&#xff1a;内存中无法存放40亿个整数&#xff08;约占内存15-16G&#xff09;&#xff1b;时间复杂…

项目经理工具箱

新项目经理误区 要解决的关键点 事&#xff1a;范围&#xff0c;进度&#xff0c;成本&#xff0c;质量 人&#xff1a;项目干系人&#xff0c;团队&#xff0c;外包成员&#xff1b; 干系人管理计划&#xff0c;沟通管理计划 技术和管理区别和联系 非暴力沟通 结构思考力 重…

正点原子lwIP学习笔记——NTP实时时间实验

1. NTP简介 NTP&#xff08;Network Time Protocol&#xff09;网络时间协议基于UDP&#xff0c;用于网络时间同步的协议&#xff0c;使网 络中的计算机时钟同步到UTC&#xff0c;再配合各个时区的偏移调整就能实现精准同步对时功能。 NTP 服务器&#xff08;Network Time Pr…

ERROR in docs.42140ac.js from UglifyJs webpack打包报错

ERROR in docs.42140ac.js from UglifyJs 原因是UglifyJs 针对js压缩 不支持es6语法&#xff08;或者引入的第三方插件存在es6语法&#xff09; ERROR in docs.42140ac.js from UglifyJs 使用的 uglifyjs-webpack-plugin 解决方法 降低uglifyjs-webpack-plugin的版本 “ugl…