【算法】快速排序、归并排序(非递归版)

news2025/5/11 17:08:54

目录

一、快速排序(非递归)

1.原理

2.实现

2.1 stack

2.2 partition(array,left,right)

2.3 pivot - 1 > left

二、归并排序(非递归)

1.原理

2.实现

2.1 gap

2.1.1 i += 2*gap

2.1.2 gap *= 2

2.1.3 gap < array.length

2.2 left、mid、right

2.2.1 left = i

2.2.2 mid >= array.length


前言:

 在看快速排序的非递归实现之前,可以先看看快速排序用递归实现的版本:【算法】快速排序(递归实现),里面有详细介绍下面讲到的基准排序,基准排序是快速排序实现的基础,最好先去了解下再往下看

递归实现归并排序的也献上【算法】归并排序(递归实现)

一、快速排序(非递归)

1.原理

  • 在每一个待排元素所确定在的待排序区间内把待排序元素用基准排序一个个排好等到把所有待排序区间对应的一个个待排序元素都排好后,整个数组就排好了

(在区间内进行基准排序时,默认以区间第一个元素为待排序元素)

用基准排序把每一个元素在对应已知的待排序范围排好,因为每排好一个元素,此元素排序位置确定且其排出的左小右大性质能缩小剩余的元素的待排序范围区域,所以每当一个元素排好后,其余元素对应的已知待排序范围就会发生变化,所以元素所在的待排序区域是经过动态变化来的


2.实现

    public static void quickSortNonR(int[] array) {
        Stack<Integer> stack = new Stack<>();//2.1
        int left = 0;
        int right = array.length-1;
        int piovt = partition(array,left,right);//2.2
        if(piovt - 1 > left) {//2.3
            stack.push(left);
            stack.push(piovt-1);
        }
        if(piovt + 1 < right) {
            stack.push(piovt+1);
            stack.push(right);
        }
        while (!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            piovt = partition(array,left,right);
            if(piovt - 1 > left) {
                stack.push(left);
                stack.push(piovt-1);
            }
            if(piovt + 1 < right) {
                stack.push(piovt+1);
                stack.push(right);
            }
        }
    }
    //2.2基准排序挖坑法实现:
    private static int partition(int[] array,int left,int right) {
        int key = array[left];
        while (left < right) {
            while (left < right && array[right] >= key) {
                right--;
            }
            //right下标元素一定比key小
            array[left] = array[right];
            while (left < right && array[left] <= key) {
                left++;
            }
            //left下标元素一定比key大
            array[right] = array[left];
        }
        array[left] = key;
        return left;
    }

2.1 stack

利用栈的动态进出存储删取管理这些动态区间


2.2 partition(array,left,right)

partition方法是默认拿待排序区间的第一个元素为基准完成该元素在待排序区间内排好的


2.3 pivot - 1 > left

说明剩下的左边的待排序区域至少有两个元素,还是需要去进行基准排序的


二、归并排序(非递归)

1.原理

一轮轮去有序数组gap的两两合并去合并的有序数组单位会越来越大,直到最后一次两两合并后成的是整体数组的一个有序数组,数组就整体排好序了


2.实现

    public static void mergeSortNor(int[] array) {
        int gap = 1;//2.1
        while (gap < array.length) {//2.1.3
            for (int i = 0; i < array.length; i += 2*gap) {//2.1.1
                int left = i;//2.2.1
                int mid =left+gap-1;
                int right = mid+gap;
                if(mid >= array.length) {//2.2.2
                    mid = array.length-1;
                }
                if(right >= array.length) {
                    right = array.length-1;
                }
                merge(array,left,right,mid);
            }
            gap *= 2;//2.1.2
        }
    }
    private static void merge(int[] array, int left, int right, int mid) {
        int s1 = left;
        int s2 = mid+1;
        int[] tmpArr = new int[right-left+1];
        int k = 0;
        //证明两个区间都同时有数据的
        while (s1 <= mid && s2 <= right) {
            if(array[s2] <= array[s1]) {
                tmpArr[k++] = array[s2++];
            }else {
                tmpArr[k++] = array[s1++];
            }
        }
        while (s1 <= mid) {
            tmpArr[k++] = array[s1++];
        }
        while (s2 <= right) {
            tmpArr[k++] = array[s2++];
        }
        //tmpArr里面一定是这个区间内有序的数据了
        for (int i = 0; i < tmpArr.length; i++) {
            array[i+left] = tmpArr[i];
        }
    }

2.1 gap

每轮去两两合并的单有序数组的元素个数,最开始的单位有序数组长度是1


2.1.1 i += 2*gap

一轮中往后继续去合并下一对的两gap有序数组


2.1.2 gap *= 2

一轮全部两两合并完后gap单位有序数组长度已翻倍


2.1.3 gap < array.length

gap >= array.length时,继续去有序数组合并也都只有一个靠前的左数组(整体数组),去合并也是没有变化了,且其实在gap >= array.length之前就一定有最后一次的两两合并成整体有序数组的了


2.2 left、mid、right

  • [left,mid] —> 两合并数组中靠前gap长度有序数组
  • (mid,right] —> 两合并数组中靠后gap长度有序数组

2.2.1 left = i

i < array.length,再进来的left是一定是left < array.length的


2.2.2 mid >= array.length
  • 如果mid < array.length 但 right >= array.length

左右两数组靠前的正常满足gap长度,靠后的不足gap长度,merge合并时也是正常合并

  • 如果mid >= array.length - 1

只有一个靠前的左数组,merge合并后还是它没变的左数组

三、排序总结

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

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

相关文章

【实战中提升自己】内网安全部署之dot1x部署 本地与集成AD域的主流方式(附带MAC认证)

1 dot1x部署【用户名密码认证&#xff0c;也可以解决私接无线AP等功能】 说明&#xff1a;如果一个网络需要通过用户名认证才能访问内网&#xff0c;而认证失败只能访问外网与服务器&#xff0c;可以部署dot1x功能。它能实现的效果是&#xff0c;当内部用户输入正常的…

[matlab]南海地形眩晕图代码

[matlab]南海地形眩晕图代码 请ChatGPT帮写个南海地形眩晕图代码 图片 图片 代码 .rtcContent { padding: 30px; } .lineNode {font-size: 12pt; font-family: "Times New Roman", Menlo, Monaco, Consolas, "Courier New", monospace; font-style: n…

Web安全和渗透测试--day6--sql注入--part 1

场景&#xff1a; win11家庭版&#xff0c;edge浏览器 &#xff0c; sqlin靶场 定义&#xff1a; SQL 注入&#xff08;SQL Injection&#xff09;是一种常见的网络安全攻击方式&#xff0c;攻击者通过在 Web 应用程序中输入恶意的 SQL 代码&#xff0c;绕过应用程序的安全机…

[SpringBoot]快速入门搭建springboot

默认有spring基础&#xff0c;不会一行代码一行代码那么细致地讲。 SpringBoot的作用 Spring Boot是为了简化Spring应用的创建、运行、调试、部署等而出现的。就像我们整个SSM框架时&#xff0c;就常常会碰到版本导致包名对不上、Bean非法参数类型的一系列问题&#xff08;原出…

理解.NET Core中的配置Configuration

什么是配置 .NET中的配置&#xff0c;本质上就是key-value键值对&#xff0c;并且key和value都是字符串类型。 在.NET中提供了多种配置提供程序来对不同的配置进行读取、写入、重载等操作&#xff0c;这里我们以为.NET 的源码项目为例&#xff0c;来看下.NET中的配置主要是有…

MYSQL “Too Many Connections“ 错误解决

1.查询当前连接数 show status like "Threads_connected"; 2.查询数据库最大连接数 show variables like "max_connections" 3.查询所有活动连接 show processlist; 4.根据查询结果观察是否有长时间未被释放的连接 参数解释 : 字段说明id连接的唯一…

【外研在线-注册/登录安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

【NLP 63、大模型应用 —— Agent】

人与人最大的差距就是勇气和执行力&#xff0c;也是唯一的差距 —— 25.4.16 一、Agent 相关工作 二、Agent 特点 核心特征&#xff1a; 1.专有场景&#xff08;针对某个垂直领域&#xff09; 2.保留记忆&#xff08;以一个特定顺序做一些特定任务&#xff0c;记忆当前任务的前…

React 打包

路由懒加载 原本的加载方式 #使用lazy()函数声明的路由页面 使用Suspense组件进行加载 使用CDN优化

2025.4.14-2025.4.20学习周报

目录 摘要Abstract1. 文献阅读1.1 模型架构1.2 实验分析1.3 代码实践 总结 摘要 在本周阅读的论文中&#xff0c;作者提出了一种名为MGSFformer的空气质量预测模型。模型通过残差去冗余模块可以有效解耦多粒度数据间的信息重叠&#xff1b;时空注意力模块采用并行建模策略&…

【1】云原生,kubernetes 与 Docker 的关系

Kubernetes&#xff1f;K8s&#xff1f; Kubernetes经常被写作K8s。其中的数字8替代了K和s中的8个字母——这一点倒是方便了发推&#xff0c;也方便了像我这样懒惰的人。 什么是云原生&#xff1f; 云原生&#xff1a; 它是一种构建和运行应用程序的方法&#xff0c;它包含&am…

Kubernetes控制平面组件:APIServer 限流机制详解

云原生学习路线导航页&#xff08;持续更新中&#xff09; kubernetes学习系列快捷链接 Kubernetes架构原则和对象设计&#xff08;一&#xff09;Kubernetes架构原则和对象设计&#xff08;二&#xff09;Kubernetes架构原则和对象设计&#xff08;三&#xff09;Kubernetes控…

springboot全局异常捕获处理

一、需求 实际项目中&#xff0c;经常抛出各种异常&#xff0c;不能直接抛出异常给前端&#xff0c;这样用户体验相当不好&#xff0c;用户看不懂你的Exception,对于一些sql异常&#xff0c;直接抛到页面上也不安全。所以有没有好的办法解决这些问题呢&#xff0c;当然有了&am…

【文献阅读】EndoNet A Deep Architecture for Recognition Tasks on Laparoscopic Videos

关于数据集的整理 Cholec80 胆囊切除手术视频数据集介绍 https://zhuanlan.zhihu.com/p/700024359 数据集信息 Cholec80 数据集 是一个针对内窥镜引导 下的胆囊切除手术视频流程识别数据集。数据集提供了每段视频中总共7种手术动作及总共7种手术工具的标注&#xff0c;标…

基于springboot的个人财务管理系统的设计与实现

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

Linux系统编程---孤儿进程与僵尸进程

1、前言 在上一篇博客文章已经对Linux系统编程内容进行了较为详细的梳理&#xff0c;本文将在上一篇的基础上&#xff0c;继续梳理Linux系统编程中关于孤儿进程和僵尸进程的知识脉络。如有疑问的博客朋友可以通过下面的博文链接进行参考学习。 Linux系统编程---多进程-CSDN博客…

简单使用MCP

简单使用MCP 1 简介 模型上下文协议&#xff08;Model Context Protocol&#xff0c;MCP&#xff09;是由Anthropic&#xff08;产品是Claude&#xff09;推出的开放协议&#xff0c;它规范了应用程序如何向LLM提供上下文。MCP可帮助你在LLM之上构建代理和复杂的工作流。 从…

MySQL:9.表的内连和外连

9.表的内连和外连 表的连接分为内连和外连 9.1 内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;之前查询都是内连 接&#xff0c;也是在开发过程中使用的最多的连接查询。 语法&#xff1a; select 字段 from 表1 inner join 表2 on 连接…

在阿里云和树莓派上编写一个守护进程程序

目录 一、阿里云邮件守护进程 1. 安装必要库 2. 创建邮件发送脚本 mail_daemon.py 3. 设置后台运行 二、树莓派串口守护进程 1. 启用树莓派串口 2. 安装依赖库 3. 创建串口输出脚本 serial_daemon.py 4. 设置开机自启 5. 使用串口助手接收 一、阿里云邮件守护进程 1.…

基于前端技术的QR码API开发实战:从原理到部署

前言 QR码&#xff08;Quick Response Code&#xff09;是一种二维码&#xff0c;于1994年开发。它能快速存储和识别数据&#xff0c;包含黑白方块图案&#xff0c;常用于扫描获取信息。QR码具有高容错性和快速读取的优点&#xff0c;广泛应用于广告、支付、物流等领域。通过扫…