数据结构-8.Java. 七大排序算法(下篇)

news2025/4/21 4:18:27

在这里插入图片描述

本篇博客给大家带来的是排序的知识点, 由于时间有限, 分两天来写, 下篇主要实现最后一种排序算法: 归并排序。同时把中篇剩下的快排非递归实现补上.
文章专栏: Java-数据结构
若有问题 评论区见
欢迎大家点赞 评论 收藏 分享
如果你不知道分享给谁,那就分享给薯条.
你们的支持是我不断创作的动力 .

王子,公主请阅!!!

  • 1. 快速排序的非递归实现
    • 1.1 具体实现
      • 1.1.1 区间入栈细节
    • 1.2 代码如下:
    • 1.3 快速排序的特性总结:
  • 2. 归并排序
    • 2.1 归并排序的基本思想
    • 2.2 具体步骤
      • 2.2.1 分解操作
      • 2.2.1 合并排序操作
    • 2.3 归并排序的非递归实现
    • 2.4 归并排序的特性总结:

1. 快速排序的非递归实现

拿到第一个基准值pivot之后, 如何进行左右子序列的快速排序呢?
可以利用栈保存左区间 和 右区间.

1.1 具体实现

1. 利用上篇文章的 partition算法拿到第一个基准值,用栈存储左区间 和 右区间,
2. 重复第一步,直到栈为空.

1.1.1 区间入栈细节

如下分析图:
在这里插入图片描述

1.2 代码如下:

//挖坑法,求基准值.
private static int partition2(int[] array,int left,int right) {
        int key = array[left];
        while(left < right) {
            while(left < right && array[right] >= key) {
                right--;
            }
            //从右边开始第一个比key小的元素覆盖[left]
            array[left] = array[right];
            while(left < right && array[left] <= key) {
                left++;
            }
            //从左边开始第一个比key大的元素覆盖空位.
            array[right] = array[left];
        }
        //key填补最终的空位,此时left=right
        array[left] = key;
        return left;
    }
//快速排序法的非递归实现.
    public static void quickSortNor(int[] array) {
        Stack<Integer> stack = new Stack<>();
        int left = 0;
        int right = array.length-1;
        //拿到第一个基准
        int pivot = partition2(array,left,right);
        //左序列区间端点入栈.
        if(pivot-1 > left) {
            stack.push(left);
            stack.push(pivot-1);
        }
        //右序列区间端点入栈.
        if (pivot+1 < right) {
            stack.push(pivot+1);
            stack.push(right);
        }
        while(!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            pivot = partition2(array,left,right);
            if(pivot-1 > left) {
                stack.push(left);
                stack.push(pivot-1);
            }
            if (pivot+1 < right) {
                stack.push(pivot+1);
                stack.push(right);
            }
        }
    }

1.3 快速排序的特性总结:

1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)

在这里插入图片描述
3. 空间复杂度:O(logN)
4. 稳定性:不稳定

2. 归并排序

2.1 归并排序的基本思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and
Conquer)的一个非常典型的应用。原序列分解成若干个子序列, 子序列排序, 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

2.2 具体步骤

2.2.1 分解操作

定义left = 0, right = array.length-1; mid = (left + right)/2; 递归分解即可.[left,mid],[mid+1,right]; 注意递归的终止条件 left >= right.

/**
     * 归并排序
     * 时间复杂度: O(N*logN) 和快速排序类似,
     *
     * 空间复杂度:O(N) - 临时数组和原数组长度一样.
     *
     * 稳定性: 稳定.
     *
     * @param array
     */
public static void mergeSort(int[] array) {
        mergeSortFunc(array,0,array.length-1);
    }
    //先分解,再归并排序.
    private static void mergeSortFunc(int[] array,int left,int right) {
        if(left >= right) return;

        int mid = (left+right)/2;
        //分解左右
        mergeSortFunc(array,left,mid);
        mergeSortFunc(array,mid+1,right);
        //将小数组排序合并
        merge(array,left,mid,right);
    }

在这里插入图片描述

2.2.1 合并排序操作

在这里插入图片描述

1. 定义一个临时数组,用来存放有序的子序列,长度为 right - left + 1, k 指向数组起始位置; 令s1指向[left],s2指向[mid+1].
2. [s1] 与 [s2] 比较, 若[s1] > [s2], 则 [s2] 放到数组中, s2++, [s2] < [s1]…类推
3. 重复第二步 直到 s1 > mid && s2 > right.
4. s1 与 s2 中两边的元素总有一边先放完, 把剩下的那边元素放入临时数组即可.

在这里插入图片描述
5. 最后一步将有序的临时数组放回原数组.

//归并排序的核心
    private static void merge(int[] array,int left,int mid,int right) {
        int s1 = left;
        int s2 = mid+1;
        int k = 0;
        int[] tmpArr = new int[right-left+1];
        while(s1 <= mid && s2 <= right) {
            if(array[s2] <= array[s1]) { //等号不取便稳定.
                tmpArr[k++] = array[s2++];
            }else {
                tmpArr[k++] = array[s1++];
            }
        }
        //s1中所有元素都大于s2,s2放完了,s1还没放完.
        while(s1 <= mid) {
            tmpArr[k++] = array[s1++];
        }
        //s2中所有元素都大于s1,s1放完了,s2还没放完.
        while(s2 <= right) {
            tmpArr[k++] = array[s2++];
        }
        //tmpArr为有序数组.
        for (int i = 0; i < tmpArr.length; i++) {
            array[i+left] = tmpArr[i];//left不一定为0.
        }
    }

2.3 归并排序的非递归实现

在归并排序核心算法的基础上, 定义变量gap = 1, 循环for (int i = 0; i < array.length; i += gap*2)中, left = i; mid = left+gap-1; right = mid+gap; 这么定义可以将原数组分为若干个长度为2的小数组.
由于要不断分解, 故gap应当写一个循环.

在这里插入图片描述
处理细节:
当gap > array.length/2时, mid和right可能会越界, 所以 需要加上条件,防止越界.

代码如下:

 //归并排序的核心
    private static void merge(int[] array,int left,int mid,int right) {
        int s1 = left;
        int s2 = mid+1;
        int k = 0;
        int[] tmpArr = new int[right-left+1];
        while(s1 <= mid && s2 <= right) {
            if(array[s2] <= array[s1]) { //等号不取便稳定.
                tmpArr[k++] = array[s2++];
            }else {
                tmpArr[k++] = array[s1++];
            }
        }
        //s1中所有元素都大于s2,s2放完了,s1还没放完.
        while(s1 <= mid) {
            tmpArr[k++] = array[s1++];
        }
        //s2中所有元素都大于s1,s1放完了,s2还没放完.
        while(s2 <= right) {
            tmpArr[k++] = array[s2++];
        }
        //tmpArr为有序数组.
        for (int i = 0; i < tmpArr.length; i++) {
            array[i+left] = tmpArr[i];//left不一定为0.
        }
    }
 //归并排序的非递归实现.
    public static void mergeSortNor(int[] array) {
        int gap = 1;
        while(gap < array.length) {
            for (int i = 0; i < array.length; i += gap*2) {
                int left = i;
                int mid = left+gap-1;
                int right = mid+gap;
                //mid和right可能会越界
                if(mid >= array.length) {
                    mid = array.length-1;
                }
                if(right >= array.length) {
                    right = array.length-1;
                }
                merge(array,left,mid,right);
            }
            gap *= 2;
        }
    }

2.4 归并排序的特性总结:

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN) -> 算法与快排类似.
3. 空间复杂度:O(N) -> 临时数组长度与原数组相同.
4. 稳定性:稳定

总的来说, 快排和归并排序还是需要花时间去想清楚每一步是怎么回事, 代码为什么要这么写??? 去解答心里有十万个为什么, 解答出自己问自己的所有的问题, 就说明掌握了这个知识点.
本篇博客到这里就结束啦, 感谢观看, 期待与你的下一次相遇!!!

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

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

相关文章

DeepSeek私有化部署+JAVA通过API调用离线大模型问答

在当今快速发展的数字化时代&#xff0c;企业对于高效、灵活的技术解决方案需求日益增长。DeepSeek作为一款领先的智能搜索与分析平台&#xff0c;凭借其强大的数据处理能力和精准的搜索结果&#xff0c;已经成为众多企业提升运营效率的得力助手。为了更好地满足企业对数据安全…

【吾爱出品】针对红警之类老游戏适用WIN10和11的补丁cnc-ddraw7.1汉化版

针对红警之类老游戏适用WIN10和11的补丁cnc-ddraw7.1汉化版 链接&#xff1a;https://pan.xunlei.com/s/VOJ8PZd4avMubnDzHQAeZDxWA1?pwdnjwm# 直接复制到游戏安装目录&#xff0c;保持与游戏主程序同目录下。

内容中台驱动企业数字化内容管理高效协同架构

内容概要 在数字化转型加速的背景下&#xff0c;企业对内容管理的需求从单一存储向全链路协同演进。内容中台作为核心支撑架构&#xff0c;通过统一的内容资源池与智能化管理工具&#xff0c;重塑了内容生产、存储、分发及迭代的流程。其核心价值在于打破部门壁垒&#xff0c;…

【第14章:神经符号集成与可解释AI—14.4 神经符号集成与可解释AI的未来发展趋势与挑战】

想象一下,如果AI既能像人类一样直觉感知(比如一眼认出街角的咖啡店),又能像数学家一样逻辑推理(比如计算最优路线避开拥堵),这个世界会变成什么样?这种“双脑协同”正是神经符号集成技术的终极目标。 但现实是,当前99%的AI系统要么只会“死记硬背”数据(如深度学习模…

[Spring Boot] Expense API 实现

[Spring Boot] Expense API 实现 项目地址&#xff1a;expense-api 项目简介 最近跟着视频做的一个 spring boot 的项目&#xff0c;包含了比较简单的记账功能的实现&#xff08;只限 API 部分&#xff09;&#xff0c;具体实现的功能有&#xff1a; 记账&#xff08;expen…

设置默认构建变体 Build Variant

Android Studio在打开项目时有时会把我设置好的build Variant改为默认的变体&#xff0c;没注意的话可能打完包才发现打错了&#xff0c;浪费时间。因此&#xff0c;有必要通过代码设置一个我想要的默认变体。 代码其实很简单&#xff0c;只要在变体下面加上isDefault true即可…

【大模型】DeepSeek使用与原理解析:从V3到R1

文章目录 一、引言二、使用与测评1.7大R1使用技巧2.官网实测 发展历程三、Deepseek MoE&#xff1a;专家负载均衡 &#xff08;2024年1月&#xff09;四、GRPO&#xff1a;群体相对策略优化&#xff08;DeepSeek-Math&#xff0c;2024年4月&#xff09;五、三代注意力&#xff…

DAY04 Object、Date类、DateFormat类、Calendar类、Math类、System类

学习目标 能够说出Object类的特点是所有类的祖宗类,任意的一个类都直接或者间接的继承了Object类,都可以使用Object类中的方法Animal extends Object:直接继承Cat extends Animal:间接继承 能够重写Object类的toString方法altinsert,选择toString 能够重写Object类的equals方法…

图像生成GAN和风格迁移

文章目录 摘要abstract1.生成对抗网络 GAN1.1 算法步骤 2.风格迁移2.1 损失函数2.2 论文阅读2.2.1 简介2.2.2 方法2.2.3 实验2.2.4 结论 3.总结 摘要 本周学习了生成对抗网络&#xff08;GAN&#xff09;与风格迁移技术在图像生成中的应用。首先介绍了GAN模型中生成器与判别器…

golangAPI调用deepseek

目录 1.deepseek官方API调用文档1.访问格式2.curl组装 2.go代码1. config 配置2.模型相关3.错误处理4.deepseekAPI接口实现5. 调用使用 3.响应实例 1.deepseek官方API调用文档 1.访问格式 现在我们来解析这个curl 2.curl组装 // 这是请求头要加的参数-H "Content-Type:…

【第15章:量子深度学习与未来趋势—15.3 量子深度学习在图像处理、自然语言处理等领域的应用潜力分析】

一、开篇:为什么我们需要关注这场"量子+AI"的世纪联姻? 各位技术爱好者们,今天我们要聊的这个话题,可能是未来十年最值得押注的技术革命——量子深度学习。这不是简单的"1+1=2"的物理叠加,而是一场可能彻底改写AI发展轨迹的范式转移。 想象这样一个…

JAVA安全—Shiro反序列化DNS利用链CC利用链AES动态调试

前言 讲了FastJson反序列化的原理和利用链&#xff0c;今天讲一下Shiro的反序列化利用&#xff0c;这个也是目前比较热门的。 原生态反序列化 我们先来复习一下原生态的反序列化&#xff0c;之前也是讲过的&#xff0c;打开我们写过的serialization_demo。代码也很简单&…

LangChain大模型应用开发:提示词工程应用与实践

介绍 大家好&#xff0c;博主又来给大家分享知识了。今天给大家分享的内容是LangChain提示词工程应用与实践。 在如今火热的大语言模型应用领域里&#xff0c;LangChain可是一个相当强大且实用的工具。而其中的提示词(Prompt)&#xff0c;更是我们与语言模型进行有效沟通的关…

2025 N1CTF crypto 复现

近一个月都没有学习了&#xff0c;一些比赛也没有打&#xff0c;很惭愧自己还是处在刚放假时的水平啊&#xff0c;马上开学了&#xff0c;抓紧做一些训练来康复。 CheckIn import os from Crypto.Util.number import * from secret import FLAGp, q getPrime(512), getPrime…

Windows Defender Control--禁用Windows安全中心

Windows Defender Control--禁用Windows安全中心 链接&#xff1a;https://pan.xunlei.com/s/VOJDuy2ZEqswU4sEgf12JthZA1?pwdtre6#

mount 出现 2038 问题

在 linux 上挂载 ext4 文件系统时出现了 2038 年问题&#xff0c;如下&#xff1a; [ 236.388500] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null) [ 236.388560] ext4 filesystem being mounted at /root/tmp supports timestamps until 2…

【第12章:深度学习与伦理、隐私—12.4 深度学习与伦理、隐私领域的未来挑战与应对策略】

凌晨三点的自动驾驶测试场,AI系统突然在暴雨中做出惊人决策——它选择撞向隔离带而不是紧急变道,因为算法推演发现隔离带后的应急车道站着五个工程师。这个惊悚的伦理困境,揭开了深度学习伦理危机最尖锐的冰山一角。 一、潘多拉魔盒已开:深度学习伦理的四大原罪 1.1 数据原…

RL--2

强化学习当中最难的两个点是&#xff1a; 1.reward delay&#xff1b; 2.agent的行为会影响到之后看到的东西&#xff0c;所以agent要学会探索世界&#xff1b; 关于强化学习的不同类型&#xff0c;可以分为以下三种&#xff1a; 一种是policy based&#xff1a;可以理解为它是…

SpringMVC新版本踩坑[已解决]

问题&#xff1a; 在使用最新版本springMVC做项目部署时&#xff0c;浏览器反复500&#xff0c;如下图&#xff1a; 异常描述&#xff1a; 类型异常报告 消息Request processing failed: java.lang.IllegalArgumentException: Name for argument of type [int] not specifie…

2025 pwn_A_childs_dream

文章目录 fc/sfc mesen下载和使用推荐 fc/sfc https://www.mesen.ca/docs/ mesen2安装&#xff0c;vscode安装zg 任天堂yyds w d 左右移动 u结束游戏 i崩溃或者卡死了 L暂停 D658地方有个flag 发现DEEE会使用他。且只有这个地方&#xff0c;maybe会输出flag&#xff0c;应…