快速排序算法的递归和非递归

news2025/2/26 14:51:00

基本思路

选择一个基准值,将数组划分三个区域,小于基准值的区域位于左侧,等于基准值的区域位于中间,大于基准值的区域位于右侧。将大于和小于区域继续进行分区,周而复始,不断进行分区和交换,直到排序完成

递归

思路:

步骤1:

在当前分区范围[l,r]中随机选中一个数作为基准值,交换到分区范围的最右侧,即r位置

步骤2:

以r位置基准值进行分区

步骤3:

对所以小于区域和大于区域继续进行步骤1操作,直到范围为1结束

单次分区过程:

less 代表小于基准值分区范围,more代表大于分区值范围,index代表当前待比较位置,r为当前分区范围最右位置

比较当前index位置和基准位置

如果 arr[index] == arr[r] ,则index向右移动

如果大于,则 more向左移动,并将index位置的数与more位置的数进行交换

如果小于,则将 less右侧位置的数与index数交换;即less范围扩大 less++,交换less和index位置数,index右移

code:
    //递归
    public static void quickSortRecursive(int [] arr){
        if(arr == null || arr.length < 2)return;
        progress(arr,0,arr.length-1);
    }
    //让arr在[l,r]上有序
    public static void progress(int [] arr,int l,int r){
        if(l >= r){
            return;
        }
        //swap(arr, L + (int) (Math.random() * (R - L + 1)), R);
        //指定一个[l,r]范围随机位置放到最右侧作为基准值
        // math.random: [0,1)
        // math.random * x : [0,x)
        // Math.random() * (r -l + 1) : l到r长度范围内的一个随机数, + l则定位到数组的索引上
        swap(arr,l + (int)(Math.random() * (r -l + 1)),r);
        int [] equalArea = partition(arr,l,r);
        progress(arr,l,equalArea[0] -1);
        progress(arr,equalArea[1] + 1,r);
    }
//让arr以r位置数为基准,<arr[r]位置的数放左边,>arr[r]位置的数放右边 ==arr[r]位置的数位于中间
    //返回==arr[r]位置的数最左和最右位置
    public static int[] partition(int [] arr,int l,int r){
        //如果l > r,则数组不合规,返回一个无效值索引
        if(l > r) return new int[] { -1, -1 };
        //如果l == r,则说明只有一个值,那么等于分区也就是当前一个位置的索引
        if(l == r) return new int[] {l,r};

        //l < r

        //基准位置 r
        //less代表 <分区 的最右一个位置索引
        int less = l -1;
        //more代表 >分区 的最左一个位置的索引
        int more = r;
        //index代表待分区数最左位置索引
        int index = l;

        //进行分区,越界条件是待分区索引来到以分区区域[大于分区]
        while (index < more){
            //System.out.println("less,index,more:"+less+","+index + ","+more);
            //如果待分区数 == 基准值,那么说明该数不是大于分区的数,index向右移动
            if(arr[index] == arr[r]){
                index++;

            }
            //如果待分区数 < 基准值,那么说明该位置数是属于小于分区的数,则将该数交换到小于分区去
            if(arr[index] < arr[r]){

                //小于分区范围扩大
                less++;
                //将当前位置交换到小于分区
                //此时当前位置是等于或者小于分区的数
                swap(arr,index,less);
                //索引index需要向右移动
                index++;
                //等价于
                //swap(arr,index++,++less);
            }
            //如果待分区数 > 基准值,那么说明该位置数是属于大于分区的数,则将该数交换到大于分区去
            //此时index不移动,因为将位置的数交换到index位置上了
            if(arr[index] > arr[r]){

                //大于范围向左扩张
                more--;
                //当前数交换到大于区域中,交换来的数是一个未知大小的数,所以index不移动
                swap(arr,index,more);

                //等价于
                //swap(arr,index,--more);
            }
        }
        //遍历完成后,此时r位置为等于区域的数,需要交换到等于分区中
        swap(arr,more,r);
        //交换完成后,less为小于分区最右索引,more为等于分区最右索引
        //more原本为大于分区最左位置索引,但是将r交换到该位置后,大于分区向右缩减了一个位置,此时more位置为等于分区最右索引
        return new int[]{less+1,more};
    }
    public static void swap(int [] arr,int l,int r){
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

非递归

思路与递归一致,仅是将系统栈替换为自己控制的栈

使用栈记录每次分区得到的左右位置

然后出栈顶元素,继续分区,将新的分区如栈,直到栈为空

使用一个辅助对象用于入栈时保存分区位置 op

code:
    //非递归版
    //使用栈记录每次分区得到的左右位置
    //然后出栈顶元素,继续分区,将新的分区如栈,直到栈为空
    //使用一个辅助对象用于入栈时保存分区位置 op
    public static void quickSortUnRecursive(int [] arr){
        if(arr == null || arr.length < 2) return;
        int N = arr.length;
        Stack<Op> stack = new Stack<>();

        //随机得到基准值,放到最右位置
        swap(arr, (int)(Math.random() * N),N-1);
        //分区
        int [] equalArea = partition(arr,0,N-1);
        //将分区后的范围入栈
        stack.push(new Op(0,equalArea[0] -1));
        stack.push(new Op(equalArea[1]+1,N-1));

        //临时变量,保存出栈的范围值
        Op temp = new Op(0,0);

        while (!stack.isEmpty()){
            //出栈
            temp = stack.pop();
            //继续对当前范围进行分区

            //如果分区得到的范围错误,说明该区域已经排好序了
            if(temp.l >= temp.r)continue;

            //随机基准值
            swap(arr,temp.l + (int) (Math.random() * (temp.r - temp.l + 1)), temp.r);
            //分区
            equalArea = partition(arr,temp.l, temp.r);
            //入栈
            stack.push(new Op(temp.l, equalArea[0] -1));
            stack.push(new Op(equalArea[1]+ 1, temp.r));

        }
    }


    public static class Op{
        public int l;
        public int r;
        public Op(int l,int r){
            this.l = l;this.r = r;
        }
    }


    //让arr以r位置数为基准,<arr[r]位置的数放左边,>arr[r]位置的数放右边 ==arr[r]位置的数位于中间
    //返回==arr[r]位置的数最左和最右位置
    public static int[] partition(int [] arr,int l,int r){
        //如果l > r,则数组不合规,返回一个无效值索引
        if(l > r) return new int[] { -1, -1 };
        //如果l == r,则说明只有一个值,那么等于分区也就是当前一个位置的索引
        if(l == r) return new int[] {l,r};

        //l < r

        //基准位置 r
        //less代表 <分区 的最右一个位置索引
        int less = l -1;
        //more代表 >分区 的最左一个位置的索引
        int more = r;
        //index代表待分区数最左位置索引
        int index = l;

        //进行分区,越界条件是待分区索引来到以分区区域[大于分区]
        while (index < more){
            //System.out.println("less,index,more:"+less+","+index + ","+more);
            //如果待分区数 == 基准值,那么说明该数不是大于分区的数,index向右移动
            if(arr[index] == arr[r]){
                index++;

            }
            //如果待分区数 < 基准值,那么说明该位置数是属于小于分区的数,则将该数交换到小于分区去
            if(arr[index] < arr[r]){

                //小于分区范围扩大
                less++;
                //将当前位置交换到小于分区
                //此时当前位置是等于或者小于分区的数
                swap(arr,index,less);
                //索引index需要向右移动
                index++;
                //等价于
                //swap(arr,index++,++less);
            }
            //如果待分区数 > 基准值,那么说明该位置数是属于大于分区的数,则将该数交换到大于分区去
            //此时index不移动,因为将位置的数交换到index位置上了
            if(arr[index] > arr[r]){

                //大于范围向左扩张
                more--;
                //当前数交换到大于区域中,交换来的数是一个未知大小的数,所以index不移动
                swap(arr,index,more);

                //等价于
                //swap(arr,index,--more);
            }
        }
        //遍历完成后,此时r位置为等于区域的数,需要交换到等于分区中
        swap(arr,more,r);
        //交换完成后,less为小于分区最右索引,more为等于分区最右索引
        //more原本为大于分区最左位置索引,但是将r交换到该位置后,大于分区向右缩减了一个位置,此时more位置为等于分区最右索引
        return new int[]{less+1,more};
    }
    public static void swap(int [] arr,int l,int r){
        int temp = arr[l];
        arr[l] = arr[r];
        arr[r] = temp;
    }

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

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

相关文章

PHP 如何设计一个高安全的电商平台:淘宝/京东商品类API封装接口

电商API接口有哪些安全问题&#xff1f; 如何保证API接口安全 接口的安全性主要围绕Token、Timestamp和Sign三个机制展开设计&#xff0c;保证接口的数据不会被篡改和重复调用&#xff0c;下面具体来看&#xff1a; Token授权机制&#xff1a;用户使用用户名密码登录后服务器给…

HTML5Plus

之前写过在 vue 中使用 mui 框架的方法&#xff0c;因为用 vue 开发后打包 5App 会有一些问题&#xff0c;所以当时用到了&#xff0c;最近又一次开发移动端&#xff0c;不同的是这次使用的是 vue3 开发的&#xff0c;导致之前使用的 vue-awesome-mui 依赖不能使用了&#xff0…

Redis原理:IntSet

&#xff08;笔记总结自b站黑马程序员课程&#xff09; 一、结构 IntSet是Redis中set集合的一种实现方式&#xff0c;基于整数数组来实现&#xff0c;并且具备长度可变、有序等特征。 结构如下&#xff1a; typedef struct intset {uint32_t encoding; //编码方式uint32_t l…

计算机视觉领域经典模型汇总(2023.09.08

一、RCNN系列 1、RCNN RCNN是用于目标检测的经典方法&#xff0c;其核心思想是将目标检测任务分解为两个主要步骤&#xff1a;候选区域生成和目标分类。 候选区域生成&#xff1a;RCNN的第一步是生成可能包含目标的候选区域&#xff0c;RCNN使用传统的计算机视觉技术&#x…

Matlab 如何选择采样频率和信号长度

Matlab 如何选择采样频率和信号长度 1、概述 在实际信号分析中经常会遇到要分辨出频率间隔为 的两个分量&#xff0c;在这种情形中如何选择采样频率和信号的长度呢&#xff1f; 2、案例分析 设有一个信号由三个正弦信号组成&#xff0c;其频率分别为 &#xff0c;即&#xf…

流程图 and/or/xor 讲解

and表示后续2个活动同时触发&#xff0c; or表示后续2个活动可触发其中的1个或2个&#xff0c;无排他性&#xff0c;也就是每个活动的触发不影响其他活动&#xff1b; xor表示后续2个活动只触发一个&#xff0c;有排他性&#xff0c;也就是只能触发其中一个。 示例演示“OR”…

网络安全架构:建立安全架构方法的指导框架

01 关键发现 ■ 架构框架使用集体见解来创建最佳实践&#xff0c;指导用户考虑组织风险和业务环境。这些方法的改编和定制&#xff0c;将帮助组织从中获得最佳价值。 ■ 方法论提供了一种系统工程方法&#xff0c;使用业务输入和期望&#xff0c;来创建可重复、可跟踪&#xf…

React16、18 使用 Redux

Redux 核心 Redux 介绍 Redux 是javaScript 状态容器&#xff0c;提供可预测化的状态管理 Redux 工作流程 Actions&#xff1a;对象&#xff0c;描述对状态进行怎样的操作 Reducer&#xff1a;函数&#xff0c;操作状态并返回新的状态 Store&#xff1a;存储状态的容器&am…

【面试】Redis的热key问题如何发现和解决?

文章目录 背景一、怎么发现热key1.1 方法一:凭借业务经验&#xff0c;进行预估哪些是热key1.2 方法二:在客户端进行收集1.3 方法三:在Proxy层做收集1.4 方法四:用redis自带命令1.5 方法五:自己抓包评估 二、如何解决2.1. 利用二级缓存2.2. 备份热key2.3 永不过期2.4 分布式锁 三…

分享5个和安全相关的 VSCode 插件

开发高质量的软件应用程序可能是艰巨的&#xff0c;因为许多组成部分必须协同工作才能创建出一个可运行的解决方案。这就是为什么开发人员需要尽可能获得所有帮助和便利&#xff0c;特别是在保护他们的应用程序时。 Visual Studio Code&#xff08;VSCode&#xff09;是最受欢迎…

redis高可用——主从复制、哨兵模式、cluster集群

1、redis群集有三种模式 分别是主从同步/复制、哨兵模式、Cluster&#xff0c;下面会讲解一下三种模式的工作方式&#xff0c;以及如何搭建cIustr群集 主从复制:主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的…

jenkins创建用户

一.背景 之前用了很多次&#xff0c;现在转到甲方爸爸的岗位&#xff0c;要培养大学毕业生&#xff0c;才发现好记性不如烂笔头。给年轻人写出来。 二.创建用户的过程 1.用户管理界面入口 Dashboard>Manage Jenkins>Jenkins own user database 2.点击右边的按钮“Cre…

Arm 架构 Ubuntu 使用 Docker 安装 Gitlab 并使用

官方 gitlab 文档 我的系统是 arm 架构的 ubuntu 官网没有提供 arm 架构的 docker 的 gitlab 的安装方式&#xff0c;直接安装的也是后来加的&#xff0c;文档也是随笔带过&#xff0c;&#xff0c;&#xff0c;我用到了&#xff0c;记录一下 默认已经安装了 docker 在 docker …

【STM32】常用存储器

常用存储器 RAM 存储器 RAM 是“Random Access Memory”的缩写&#xff0c;被译为随机存储器。所谓“随机存取”&#xff0c;指的是当存储器中的消息被读取或写入时&#xff0c;所需要的时间与这段信息所在的位置无关。而RAM可随读取其内部任意地址的数据&#xff0c;时间都是…

Android 12.0 禁用系统app首次启动动画SplashScreen功能分析

1.前言 在12.0的系统开发中,由于系统增加了新特性,在app首次启动的时候,添加了启动引导动画SplashScreen功能,所以会默认显示 app图标作为一张动画来过度,解决首次启动卡顿问题,接下来分析下看是怎么样添加的,然后禁用就可以了 如图: 2.禁用系统app首次启动动画SplashSc…

关于mybatisplus报错:Property ‘sqlSessionFactory‘ or ‘sqlSessionTemplat的问题

可能是mybatisplus版本不兼容的问题&#xff0c;我之前用的3.4.0&#xff0c;springboot版本是3.1.3&#xff0c;maven版本是3.8.8&#xff0c;运行的时候报了这个错。现在修改了mybatisplus的版本&#xff0c;如下图&#xff1a; 这样就不报错了。 大家可以在这里找合适的my…

DGA行为转变引发了对网络安全的担忧

Akamai的研究人员发现&#xff0c;在域名系统(DNS)流量数据中&#xff0c;动态种子域生成算法(DGA)家族的行为发生了令人担忧的变化。这一发现揭示了恶意行为者如何调整他们的策略来延长他们的指挥与控制(C2)通信通道的寿命&#xff0c;以保护他们的僵尸网络。 从技术角度来看…

Excel文件生成与下载(SpringBoot项目)(easypoi)

说明 通过接口&#xff0c;导出表格。 使用SpringBoot框架和easypoi表格解析框架&#xff0c;生成Excel表格&#xff0c;并通过接口下载。 表格示例 依赖 版本 <easypoi.version>4.4.0</easypoi.version>依赖 <!-- easypoi --> <dependency><…

Matlab图像处理之Lee滤波器

目录 一、前言:二、LEE滤波器2.1 LEE滤波器原理2.2 LEE滤波器实现步骤三、MATLAB代码示例一、前言: LEE滤波器是一种常用于合成孔径雷达(SAR)图像去噪的滤波器。它能增强图像的局部对比度。今天我们将通过MATLAB来实现这种滤波器。 二、LEE滤波器 2.1 LEE滤波器原理 LEE滤…

Linux Debian12将本地项目上传到码云(gitee)远程仓库

一、注册码云gitee账号 这个可以参考其他教程&#xff0c;本文不做介绍。 gitee官网&#xff1a;https://gitee.com/ 二、Linux Debian12安装git 如果Linux系统没有安装git&#xff0c;可以使用下面命令安装git sudo apt install git 三、gitee新建仓库 我这只做测试&…