通俗易懂的教你如何使用Java实现快速排序

news2025/1/10 17:20:27

文章目录

  • 快速排序
    • 🔒题目
    • 💡分析
    • 🔑题解

快速排序

🔒题目

image-20230223140519319

题目链接:785.快速排序-Acwing题库

💡分析

  • 基本思想分治
  • 主要步骤
    • Step1确定主元。从要划分的数组中选取一个元素作为主元,一般的选取方式有四种:①取最左边的元素 ②取最右边的元素 ③取中间的元素 ④随机选取(这四种选法根据个人喜好或者具体场景而定)
    • Step2划分区间。将数组中所有的元素于主元进行比较,直到使得左右区间能够恒大于等于或恒小于等于主元,如果我们想要升序排序,就可以让将所有小于等于主元的元素排在左边,所有大于主元的元素都在右边;如果我们想要降序排序,就可以将所有大于等于主元的元素都排在左边,所有小于主元的元素都在右边。当然这个等号左右两边任取其一即可,并不是非得让左边取等号
    • Step3递归进行Step1和Step2。我们通过Step1和Step2会划分出两个区间,然后再接着划分这两个区间,直到区间无法再进行划分(即区间只剩一个元素)时,此时数组就排序完毕了

🔑题解

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

  • 方式一:每次选取区间中最右边的元素为主元,双指针从左往右遍历

在这里插入图片描述

i负责标记左区间和右区间的分界点,而j负责遍历需要划分的区间,找出比主元小的元素,并将这个元素放到i+1的左侧,这样就能够实现区间的划分,最终使得i+1左侧的子区间恒小于pivoti+1右侧的区间恒大于等于主元

import java.util.Scanner;

/**
 * @author ghp
 * @title 快速排序
 * @description
 */
public class Main {
	public static void main(String[] args) {
		// 接收输入
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[] arr = new int[n];
		for (int i = 0; i < arr.length; i++) {
			arr[i] = sc.nextInt();
		}
		// 快速排序
		quickSort(arr, 0, arr.length - 1);
		// 输出结果
		for (int i = 0; i < arr.length; i++) {
			System.out.print(arr[i] + " ");
		}
	}

	/**
	 * 快速排序
	 * @param arr 待排序的数组
	 * @param left 待划分区间的最左侧索引
	 * @param right 带划分区间的最右侧索引
	 */
	private static void quickSort(int[] arr, int left, int right) {
		if (left >= right) {
			// 区间中只有一个元素时,排序完毕,递归结束
			return;
		}
		// 划分区间,并获取划分数组的主元索引,用于下一次的划分
		int pivot = partition(arr, left, right);
		quickSort(arr, left, pivot-1); // 继续划分左侧区间
		quickSort(arr, pivot+1, right); // 继续划分右侧区间
	}

	/**
	 * 划分区间(增序排序)
	 * @param arr 待排序的数组
	 * @param left 待划分区间的最左侧索引
	 * @param right 带划分区间的最右侧索引
	 * @return 返回本次用于划分区间的主元
	 */
	private static int partition(int[] arr, int left, int right) {
		// 选取主元
		int pivot = arr[right];
		int i = left-1; 
		int temp;
		// 划分区间(左侧区间<主元,右侧区间>=主元)
		for (int j = left; j < right; j++) {
			if(arr[j] < pivot) {
				// 将比主元小的元素放到(i+1)的左侧
				temp = arr[j];
				arr[j] = arr[i+1];
				arr[i+1] = temp;
				i++;
			}
		}
		// 将主元放到分界点
		temp = arr[right];
		arr[right] = arr[i+1];
		arr[i+1] = temp;
		// 返回主元索引
		return i+1;
	}
}

image-20230223150828107

  • 方式二:每次选取区间最左边的元素为主元,指针双向遍历(左指针从左往右遍历,找比主元大的;右指针从右往左遍历,找比主元小的)

    在这里插入图片描述

    i从左到右遍历,找 >= pivot的元素,j从右往左遍历,找 <= pivot 的元素,然后交换ij找的的元素,最终遍历完后,就会使得,左区间的元素都 <= pivot,右区间的元素都 >= pivot

    import java.util.Scanner;
    
    /**
     * @author ghp
     * @title 快速排序
     * @description
     */
    public class Main {
    	public static void main(String[] args) {
    		// 接收输入
    		Scanner sc = new Scanner(System.in);
    		int n = sc.nextInt();
    		int[] arr = new int[n];
    		for (int i = 0; i < arr.length; i++) {
    			arr[i] = sc.nextInt();
    		}
    		// 快速排序
    		quickSort(arr, 0, arr.length - 1);
    		// 输出结果
    		for (int i = 0; i < arr.length; i++) {
    			System.out.print(arr[i] + " ");
    		}
    	}
    
    	/**
    	 * 快速排序
    	 * 
    	 * @param arr   待排序的数组
    	 * @param left  待划分区间的最左侧索引
    	 * @param right 带划分区间的最右侧索引
    	 */
    	private static void quickSort(int[] arr, int left, int right) {
    		if (left >= right) {
    			// 区间中只有一个元素时,排序完毕,递归结束
    			return;
    		}
    		// 划分区间,并得到分界点的索引
    		int index = partition(arr, left, right);
    		quickSort(arr, left, index-1); // 继续划分左侧区间
    		quickSort(arr, index, right); // 继续划分右侧区间
    	}
    
    	/**
    	 * 划分区间
    	 * @param arr 待排序的数组
    	 * @param left 待划分区间的最左侧索引
    	 * @param right 带划分区间的最右侧索引
    	 * @return 返回分界点的索引
    	 */
    	private static int partition(int[] arr, int left, int right) {
    		// 选取主元
    		int pivot = arr[right];
    		int i = left - 1;
    		int j = right + 1;
    		// 划分区间(左侧区间<=主元,右侧区间>=主元)
    		while (i < j) {
    			// 从左往右遍历,寻找>=主元的元素
    			do {
    				i++;
    			} while (arr[i] < pivot);
    			// 从右往左遍历,寻找<=主元的元素
    			do {
    				j--;
    			} while (arr[j] > pivot);
    			// 判断区间是否划分完毕
    			if (i < j) {
    				// 区间还未划分完毕,需要交换元素
    				int temp = arr[i];
    				arr[i] = arr[j];
    				arr[j] = temp;
    			}
    		}
    		// 返回分界点
    		return i;
    	}
    }
    

    方式二的效率是要高于方式一的,但是容易发生边界问题

    注意事项

    • 情况1:返回的是i

      • 如果pivot取的是arr[left],则划分的区间是【left, index 】和【index+1, right

      • 如果pivot取的是arr[right],则划分的区间是【left, index-1】和【index, right

      记忆技巧:主元在左就+1,主元在右就-1

      上面这两个必须配套使用,如果选错了就会出现递归死循环

      如果pivot取的是arr[left],但划分区间选择【left, index-1】和【index, right】,就会出现如下图所示的死循环:

      image-20230223160804191

      PS:其实也不需要死记硬背,当pivot取值 arr[left] 时,我们可以明确主元在左,而每次我们循环实用 if 寻找符合条件的元素时,此时我们的 i 索引对应的元素至少是和主元相等的,所以返回的 i 所划分的区间就是【left, i】和【i+1, right】。同理如果主元选择 arr[right] ,如果返回的是i,此时主元在右边,此时 i 索引对应的元素是可能比主元大的,因为每次遍历 if 寻找符合目标的元素,i都会自增,所以 i 划分的区间为【left, i-1】和【i,right】

    • 情况2:返回的是j

      • 如果pivot取的是arr[left],则划分的区间是【left, j 】和【j+1, right
      • 如果pivot取的是arr[right],则划分的区间是【left,j-1 】和【j, right

      推导思路和上面是一致的

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

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

相关文章

python3+pytest+requests+allure+yaml测试框架搭建

目录 设计框架的原则 1.框架整体结构 2.框架各个模块说明 3.示例 3.1 先写一个测试用例 3.2 对上面的用例进行分层封装&#xff08;可根据业务复杂度分两层或者三层&#xff0c;此处演示分三层&#xff09; 3.3生成allure测试报告并查看 设计框架的原则 封装基类方法 对…

第十八章 使用LNMP架构部署动态网站环境

文章目录 第十八章 使用LNMP架构部署动态网站环境一、源码包程序1、源码包的优势2、基本步骤&#xff08;1&#xff09;、下载及解压源码包文件&#xff08;2&#xff09;、编译源码包代码&#xff08;3&#xff09;、生成二进制安装程序&#xff08;4&#xff09;、运行二进制…

VS2022调试Win-flex bison生成的C语言程序

Win-flex bison是flex和bison在Windows平台的一个移植版本&#xff0c;它支持flex&#xff08;快速词法分析器&#xff09;和bison&#xff08;GNU解析器生成器&#xff09;。 Win-flex bison的下载及安装可参看“Windows中使用Lex&#xff08;Win flex-bison&#xff09;”&a…

CIBF2023深圳电池展圆满结束!昂视期待与您下次相会

5月18日&#xff0c;CIBF2023深圳电池展圆满结束&#xff0c;展会为期三天&#xff0c;各位参展商展示了最新技术与产品&#xff0c;并在展位上开展花式互动&#xff0c;现场气氛火热。 作为电池行业的权威展会&#xff0c;CIBF2023深圳电池展为国内外用户、采购商、经销商提供…

cuda编程学习——第二个cuda程序(官方案例分析)!干货向(二)

前言&#xff1a; 最近在做三维重建&#xff0c;尤其是Nerf方面多视角合成工作的时候&#xff0c;意识到了cuda的编程计算可以大大提高其中渲染的计算&#xff0c;最明显的例子是Instant-ngp&#xff0c;Plenoxels等文章&#xff0c;因此后面会学Cuda一段时间&#xff0c;同时…

Python代码最好的加密.pyd——easycython(Windows系统)

1 安装easycython 1.1 建议选用python 3.6及其以下的版本&#xff01;&#xff01; 1.2 CMD命令行 pip install easycython2 安装Visual Studio 2.1 下载 点击链接 https://visualstudio.microsoft.com/zh-hans/free-developer-offers/ 2.2 安装注意事项 记得勾选红色下图的…

渗透测试--3.1.社会工程学攻击

目录 社会工程学攻击 SET介绍 一、建立克隆钓鱼网站收集目标凭证 二、set工具集之木马欺骗实战反弹链接 三、后渗透阶段 1.查看主机系统信息 2.到处用户密码的hash值 3.获得shell控制台 日志清除 四、钓鱼邮件 1、测试邮箱的连通性 2、参数说明 3、Kali 内置了s…

位运算实现加减乘除(自用水文)

目录 位运算实现加法 位运算实现减法 位运算实现乘法 位运算实现除法 代码示例 PS&#xff1a;用位运算实现的加减乘除&#xff0c;其数据都是整型的(int、char、size_t等&#xff09; 位运算实现加法 LeetCode_2.两数相加_小白麋鹿的博客-CSDN博客https://yt030917.blo…

【Jmeter第一章】Jmeter实操详细教程(快速入门)

文章目录 1、前言2、Jmeter介绍3、Jmeter下载安装4、Jmeter快速入门4.1、切换为中文显示4.2、基本使用 总结 1、前言 本篇内容为Jmeter的简单使用介绍&#xff0c;是基础的使用技巧&#xff0c;希望能帮到各位&#xff0c;不足之处还望多多包涵&#xff0c;最后感谢您的阅览。…

ChatGPT工作提效之初探路径独孤九剑遇强则强

ChatGPT工作提效之遇强则强 前言一、如何使用ChatGPT二、ChatGPT实战应用三、ChatGPT会叫的小孩有奶吃工具类的交互问答类的交互开发类的交互 前言 读《笑傲江湖》西湖比剑时&#xff0c;对于独孤九剑1的解读印象颇为深刻。令狐冲被任我行这个高手激发出许多精妙的剑招。这独孤…

【原创】企业级别的Kafka配置--按照市场分区

企业级别的Kafka配置--按照市场分区 背景--Kafka广播按照市场分区生产者和消费者设计方案Kafka Broker设计消费消息时增加过滤条件消费者端利用多线程/多协程机制提高吞吐量 背景–Kafka广播 对于同一个Topic来说&#xff0c;每个消费者组都可以拿到这个Topic中的全部数据。消…

论文阅读《Gradient-based Camera Exposure Control for Outdoor Mobile Platforms》

摘要 本文介绍了一种用于移动机器人平台上图像处理和计算机视觉应用的自动调节相机曝光的新方法。由于大多数图像处理算法严重依赖于主要基于局部梯度信息的低级图像特征&#xff0c;因此我们认为梯度量可以确定适当的曝光水平&#xff0c;从而使相机能够以对照明条件具有鲁棒…

LaTeX详细安装教程|LaTeX 基础知识|LaTeX 常用语法|LaTeX 快速入门

latex安装教程 一、LaTeX 基础知识1.1 LaTeX 的特点1.2 LaTeX 的基本组成部分 二、TeXLive安装包下载三、安装步骤四、TeXstudio安装及简单使用五、快速入门&#xff08;LaTeX 常用语法&#xff09;5.1 文本格式5.2 数学公式5.3 LaTeX 支持有序列表和无序列表5.4 图片和表格 La…

vim自动文件头

注意&#xff1a;以下方法是安装了ycm后的方法&#xff0c;没安装是否好使不知道&#xff0c;建议还是安装ycm&#xff0c;原版的vim真不好用。 在用vim编辑代码的时候自动添加文件头还是比较有用的。 比如像下面这样&#xff0c;只要输入vim test.py文件头就自动添加上了。 …

【Spring框架】--03.AOP

文章目录 5.面向切面&#xff1a;AOP5.1场景模拟5.1.1声明接口5.1.2创建实现类5.1.3创建带日志功能的实现类5.1.4提出问题 5.2代理模式5.2.1概念5.2.2静态代理5.2.3动态代理5.2.4测试 5.3AOP概念及相关术语5.3.1概述5.3.2相关术语①横切关注点②通知&#xff08;增强&#xff0…

微服务架构 云原生应用从这一步开始

什么是云原生应用和微服务架构 云原生应用是一种设计和构建方式&#xff0c;旨在充分利用云计算的弹性、可扩展性和高可用性特性。云原生应用将应用程序的开发、交付和运行环境与云平台密切结合&#xff0c;以实现高度灵活、可靠和可扩展的部署。 云原生应用的核心原则包括以…

Java | 一分钟掌握定时任务 | 6 - Quartz定时任务

作者&#xff1a;Mars酱 声明&#xff1a;本文章由Mars酱原创&#xff0c;部分内容来源于网络&#xff0c;如有疑问请联系本人。 转载&#xff1a;欢迎转载&#xff0c;转载前先请联系我&#xff01; 前言 前几篇介绍了单体架构的定时任务解决方式&#xff0c;但是现代软件架构…

Mysql【基础篇】—— mysql安装和环境配置

Mysql【基础篇】—— mysql安装和环境配置&#x1f60e; Mysql 的概述Mysql下载安装和环境配置下载流程&#xff1a;Mysql启动&#xff1a;客户端连接方式一&#xff1a;使用MySQL提供的客户端命令行工具方式二&#xff1a;使用系统自带的命令行工具执行指令 总结撒花&#x1f…

tb-gateway配置OPC UA

1、安装模拟软件KEPServerEX 6 省略 2、配置OPC UA 安装好KEPServerEX 6之后,默认再电脑的最小化窗口会显示一个图标 右键点击图标,会显示一个OPC UA配置,然后点击配置,进入下面页面 点击添加按钮,弹出下面的弹窗 然后进行选择和配置,见下图,然后保存即可。 3、启动K…

【Linux】Linux编译器--vim的使用

&#x1f601;作者&#xff1a;日出等日落 &#x1f514;专栏&#xff1a;Linux 当你还不能对自己说今天学到了什么东西时&#xff0c;你就不要去睡觉。 ——利希顿堡 目录 vim是什么 vim安装 vim的基本概念 vim的基本操作 vim正常模式命令集 vim末行模…