快速排序(下)

news2024/12/26 11:53:30

快速排序(下)

在这里插入图片描述

前言

在上一篇文章中我们了解了快速排序算法,但那是Hoare的版本,其实还有别的版本:一种是挖坑法,它们的区别主要在于如何找基准值。霍尔的版本思路难理解但代码好理解,挖坑法则是思路好理解但代码不好理解;还有一种是lomuto的前后指针法
此外,还有不使用递归的快排方法(找基准值还是用的三种方法之一)。

本文就来讲解这几种不同的快排方法。

正文

挖坑法

实现思路

创建左右指针。首先从右向左找出比基准值小的数据,找到后立即放入左边坑中,当前位置变为新的“坑”,然后从左向右找出比基准值大的数据,找到后立即放入右边坑中,当前位置变为新的“坑”,结束循环后将最开始存储的分界值放入当前的“坑”中,返回当前“坑”的下标(即分界值下标)。

画图理解一下什么是“坑”。

这就是整个“挖坑”然后“填坑”直到left和right重叠时将基准值放到该位置(它该待的位置)的过程。

现在我们可以来写一下挖坑法的代码:

//挖坑法
int _QuickSort(int* arr, int left, int right)
{
	int hole = left;
	int key = arr[hole];

	while (left < right)//注意和霍尔版区别
	{
		//这里同样需要限制且不能是arr[right] >= key,否则可能无法“二分”,最终效率低下
		while (left < right && arr[right] > key)
		{
			--right;
		}
		arr[hole] = arr[right];
		hole = right;
		while (left < right && arr[left] < key)
		{
			++left;
		}
		arr[hole] = arr[left];
		hole = left;
	}
	arr[hole] = key;
	return hole;
}
排序效果

我们在main函数中写这样的代码:

	int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };
	int n = sizeof(a) / sizeof(int);
	printf("排序前: ");
	PrintArr(a, n);//代码不具体展示

	QuickSort(a, 0, n-1);

	printf("排序后: ");
	PrintArr(a, n);

执行结果:

可以看到我们就成功排序了。


lomuto前后指针法

实现思路

创建前后指针,从左往右找比基准值小的进行交换,使得小的都排在基准值的左边。

其实前后指针对于很多学过数据结构的人来说应该已经不陌生了,但是快排也能用到前后指针法,我们看看具体怎么做

  • 定义两个变量prev和cur,让cur指向位置的数据和key值比较
    1. 若arr[cur]<arr[key],prev向后走一步并和cur交换
    2. 若arr[cur]>=arr[key],cur继续往后
  • 退出循环后将prev与key值交换,找到基准值

同样不变的思路也是要找基准值,也就是要让基准值左侧的数据都小于基准值,让基准值右侧的数据都大于基准值。

那么两个变量的作用是什么?可以说prev是用来占坑的,而cur用来找小,找到小后就让prev加一后和cur交换。退出循环后将prev与key值交换,就找到了基准值。

代码:

//lomuto前后指针法
int _QuickSort(int* arr, int left, int right)
{
	int prev = left, cur = left + 1;
	int key = left;

	while (cur<=right)
	{
		if (arr[cur] < arr[key] && ++prev !=cur)
            //prev和cur相同就不交换
            //写为arr[cur] <= arr[key],加上等号也是殊途同归
		{
			Swap(&arr[cur], &arr[prev]);
		}
		cur++;//进不进循环cur都要往后走
	}
	Swap(&arr[key], &arr[prev]);

	return prev;
}

执行效果:

也一样成功排序完了。

可以发现这一种方法比起前面的Hoare版本和挖坑法,在取不取等号的探讨上少了很多麻烦。

  • 注意:

    在循环内的if语句中arr[cur] < arr[key]如果写为arr[cur] <= arr[key],并不能解决无法”二分“子序列的问题,所以加不加都无所谓。

    这也就是前后指针法的一个**缺陷:如果数组中数据都相等**,效率就会很低。


快排的非递归版本

所以现在已经知道了三种版本的快排,其实也就是三种找基准值的方法。

既然我们的快排使用的是递归,就难免有空间上的缺陷。其实快排还有非递归版本。但是要借助数据结构:栈

那么现在问题是左右区间再分左右区间时我们怎么找区间呢?因为找基准值时没有使用递归而是在找区间时使用的递归(所以找基准值使用前面说的三种方法的任意一种都行)。

(先将5和0入栈,然后出栈,left=0,right=5,找到基准值为3)

我们知道keyi(基准值)也就能区分左区间和右区间,这两个区间任意哪一个入栈都行。

现在,我们还是一样要现将right入栈再将left入栈(先入右区间),然后再入另一个区间。

此时栈不为空,我们出栈,先出的两个就是左区间,然后去找它的基准值。

然后这样找下去直到right小于left或等于left时,也就是没有数据或者只有一个数据时,构不成有效区间,就不入栈

左区间所有基准值找完后,就像二叉树一样,开始走右区间。

开始走右区间时也就是刚好栈里剩的两个数据为右区间时。

所以可以看出,我们取栈顶元素就是在模拟递归。

代码参考

现在我们通过代码来看看怎么使用栈进行快排。

//非递归版快排
//借助数据结构——栈
void QuickSortNonR(int* arr, int left, int right)
{
	ST st;
	STInit(&st);
	StackPush(&st, right);
	StackPush(&st, left);

	while (!StackEmpty(&st))
	{
		//取两次栈顶
		int begin = StackTop(&st);
		StackPop(&st);

		int end = StackTop(&st);
		StackPop(&st);

		//找基准值——用双指针法
		int prev = begin;
		int cur = begin + 1;
		int keyi = begin;

		while (cur <= end)
		{
			if (arr[cur] <= arr[keyi] && ++prev!=cur)
			{
				Swap(&arr[cur], &arr[prev]); 
			}
			cur++;
		}
		Swap(&arr[keyi], &arr[prev]);

		keyi = prev;
		//根据基准值划分左右区间
		//左区间:[begin,keyi-1]
		//右区间:[keyi+1,end]

		if (keyi + 1 < end)//控制区间有效
		{
			StackPush(&st, end);
			StackPush(&st, keyi + 1);
		}	
		if (keyi-1>begin)//控制区间有效
		{
			StackPush(&st, keyi - 1);
			StackPush(&st, begin);
		}
	}

	STDestroy(&st);
}

那么到此本文就结束了,祝学习愉快=_=

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

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

相关文章

Java新特性(二) Stream与Optional详解

Java8新特性&#xff08;二&#xff09; Stream与Optional详解 一. Stream流 1. Stream概述 1.1 基本概念 Stream&#xff08;java.util.stream&#xff09; 是Java 8中新增的一种抽象流式接口&#xff0c;主要用于配合Lambda表达式提高批量数据的计算和处理效率。Stream不是…

【前端】中后台框架 添加其他布局的探索

文章目录 前言需求整理第一步&#xff1a;实现可切换布局第二步&#xff1a;配置页面顶部的路由&#xff08;一级路由&#xff09;第三部&#xff1a;配置左侧二级和二级以上的路由第四部&#xff1a;给侧边栏加一个动画第五部&#xff1a;刷新页面之后顶部路由、左侧路由的回显…

5款免费写作生成软件,自动生成原创文章很简单

在人工智能时代的今天&#xff0c;创作者面对写作不再是一件令人望而生畏的事情。随着AI技术的不断发展&#xff0c;涌现出了许多优秀的免费写作生成软件&#xff0c;让自动生成原创文章变得轻松简单。以下为大家详细介绍5款备受赞誉的免费写作生成软件&#xff0c;下面跟随小编…

硬盘数据丢失不再怕,四大恢复工具帮你轻松逆转局面!

硬盘故障、误删文件、病毒攻击等原因导致数据丢失的情况时有发生。面对这种情况&#xff0c;如何高效、快速地进行硬盘数据恢复呢&#xff1f;接下来几款好用的数据恢复软件推荐给大家。 一、福昕数据恢复&#xff1a;全方位恢复&#xff0c;让数据无遗漏 链接&#xff1a;ww…

手把手教你OpenCV实现实时人脸检测(C++)

目录 1&#xff0c;原理介绍 2&#xff0c;代码讲解 3&#xff0c;全部代码 4&#xff0c;结果展示 1&#xff0c;原理介绍 haarcascade_frontalface_default.xml 是一个 XML 文件&#xff0c;它包含了使用 Haar 特征分类器训练得到的人脸检测模型。这个模型是 OpenCV 库自…

【小知识】站在前人的肩膀上写程序——STL库初阶算法函数的使用

【小知识】站在前人的肩膀上写程序——STL库初阶算法函数的使用 1.墨水瓶算法和swap函数2.打擂台算法和max&#xff0c;min函数3.排序——sort函数 1.墨水瓶算法和swap函数 如果想交换两个墨水瓶的墨水该怎么办呢&#xff1f;我们可以准备第三个墨水瓶。将第一个墨水瓶的墨水倒…

linux安装配置jdk

①下载jdk安装包&#xff0c;放在/opt/app/software/java下 cd /opt/app/software/java②进行解压操作 tar -zxvf jdk-8u251-linux-x64.tar.gz③解压完成之后&#xff0c;进行环境变量的配置&#xff0c;shell下执行 vi ~/.bash_profile根据jdk的安装目录&#xff0c;加入 …

LeeCode Practice Journal | Day31_GA05

56. 合并区间 题目&#xff1a;56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 题解&#xff1a;代码随想录 (programmercarl.com) 思路很清晰&#xff0c;对数组的操作稀烂&#xff0c;细节上也出现很多问题 solution public class Solution {public int[][] Merge(in…

2024全新Thinkphp聊天室H5实时聊天室群聊聊天室自动分配账户完群组/私聊/禁言等功能/全开源运营版本

全开源运营版本聊天室H5实时聊天室群聊聊天室自动分配账户完群组/私聊/禁言等功能 运营版本的聊天室&#xff0c;可以添加好友&#xff0c;建立群组&#xff0c;私聊&#xff0c;禁言功能 H5TP5.0mysqlPHP 源码开源不加密

【python】Python二手房住房数据抓取可视化(源码+数据集+论文)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

python爬取豆瓣电影top250-python实战项目,手把手教学,附源码

目录 1、分析网页2、请求服务器 2.1导入包2.2设置浏览器代理2.3请求服务器格式2.4请求服务器代码汇总 3.xpath提取信息 3.1获取xpath节点的方法3.2xpath提取内容3.2.1提取文本3.2.2提取链接3.2.3提取标签元素 4.正则表达式 4.1提取固定位置的信息4.2匹配出数字 5、提取一页中的…

Java 并发编程:Java 线程池的介绍与使用

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 024 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

细分 Insight 合作伙伴 2024 年企业技术状况报告

Insight Partners 的团队刚刚发布了 2024 年企业技术状况报告。在 60 幻灯片中有很多东西可以消耗&#xff0c;但我们挑选了应该让我们的观众感兴趣的东西 - 坦率地说&#xff0c;有很多有趣的东西。我将把调查方法的东西留给你使用&#xff0c;但足以说样本量很大&#xff0c;…

dami支付漏洞

使用burpsuite等抓包工具&#xff0c;抓取数据包后&#xff0c;修改数据包中的参数从而达到支付篡改的目的&#xff1b;篡 改的参数&#xff1a;商品ID&#xff0c;购买价格&#xff0c;购买数量&#xff0c;手机号码&#xff0c;订单D&#xff0c;支付状态 常见漏洞利用手段…

国家网络身份个人认证方法

申领网络身份认证后&#xff0c;用户会得到一张虚拟的“网络身份证”&#xff0c;它可以向需要实名认证的互联网平台进行认证&#xff0c;不再需要输入姓名和身份证号等信息。 申请方式&#xff1a;各手机应用平台搜索国家网络身份认证即可&#xff08;必须支持NFC才能申请&am…

AI产品经理必备:什么是LLM,有什么优劣势

LLM&#xff08;Large Language Model大型语言模型&#xff09;是一种人工智能技术&#xff0c;能够理解和生成自然语言文本。LLM可以应用于多种场景&#xff0c;包括自然语言理解、文本生成、机器翻译、对话系统、问答系统、文本摘要、情感分析等。可以帮助人们快速生成文章、…

c# 构造器的声明与调用

在C#中&#xff0c;构造器&#xff08;Constructor&#xff09;是一种特殊类型的函数&#xff0c;用于初始化类的新实例。构造器的名字必须与类名完全相同&#xff0c;并且没有返回类型&#xff0c;甚至连void也不行。 当创建类的一个新实例时&#xff0c;构造器会自动被调用。…

全球轻型电动轮椅市场规划预测:未来六年CAGR为7.3%

随着全球人口老龄化的加剧和消费者对便捷、高效出行工具的需求增加&#xff0c;轻型电动轮椅作为提升行动不便人士生活质量的重要工具&#xff0c;正逐渐受到市场的广泛关注。本文旨在通过深度分析轻型电动轮椅行业的各个维度&#xff0c;揭示行业发展趋势和潜在机会。 【市场…

StudyStudyStudy第十六天(2024.8.2)

1.代理模式 代理模式分为静态代理和动态代理 代理模式&#xff0c;就是在想要执行的代码之前或之后添加代码块&#xff0c;并不会破坏原有的代码结构。可以理解成加上了一个访问层 1.静态代理 创建一个接口Shopping public interface Shopping {void shopping(); }创建一个…

笔记:唐老师讲电赛之唐老师讲电子器件(1)电阻 参数与选型

电阻 a . 精度 电阻----运放中的电阻要选精度高的&#xff0c;一般0.1% 若在设计电路中电路参数由某个电阻决定&#xff0c;则需要选取高精度电阻。例如&#xff0c;反向放大器等对于反馈系数、增益等参数完全由电阻决定的&#xff0c;则需要选取精度较高的电阻&#xff0c;…