Java数据结构第二十一期:解构排序算法的艺术与科学(三)

news2025/3/10 5:36:50

专栏:Java数据结构秘籍

个人主页:手握风云

目录

一、常见排序算法的实现

1.1. 归并排序

二、排序算法复杂度及稳定性分析


一、常见排序算法的实现

1.1. 归并排序

        归并排序是建⽴在归并操作上的⼀种有效的排序算法,该算法是采⽤分治法的一个⾮常典型的应⽤。将已有序的⼦序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。如下图所示。

        类似于快速排序,利用数组下标的中间值mid进行分解,当left=right时,说明左树已经分解完毕,然后再去分解右树,然后再进行排序与合并。

    public void MergeSort(int[] array) {
        MergeSortChild(array, 0, array.length - 1);
    }

    private void MergeSortChild(int[] array, int left, int right) {
        if(left >= right){
            return;
        }

        int mid =(left+right)/2;

        MergeSortChild(array,left,mid);//递归左树
        MergeSortChild(array,mid+1,right);//递归右树

        //开始合并
        Merge(array,left,mid,right);
    }

        我们接下来说一下合并有序数组的思路。首先,我们需要额外定义一个临时数组用来储存合并后的数据。我们先定义三个指针,先让三个指针同时指向三个数组的第一个下标,比较nums[s1]与nums[s2]的值。如果nums[s1]>nums[s2],将nums[s1]放入nums里面,同时s1、s向右移动;如果nums[s1]<nums[s2],将nums[s2]放入nums里面,同时s2、s向右移动。在移动期间,要保证指针不会越界。

    private void Merge(int[] array, int left, int mid, int right) {
        int[] tmpArr = new int[right + left + 1];
        int k = 0;
        int s1 = left, e1 = mid, s2 = mid + 1, e2 = right;
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] <= array[s2]) {
                tmpArr[k++] = array[s1++];
            } else {
                tmpArr[k++] = array[s2++];
            }
        }
        while (s1 <= e1) {
            tmpArr[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmpArr[k++] = array[s2++];
        }
    }

        这样临时数组中储存的就是有序数据,但原数组还不是有序的,我们将临时数组拷贝到原始数组中。

        for (int i = 0; i < k; i++) {
            array[i + left] = tmpArr[i];
        }

        完整代码:

import java.util.Random;

public class Sort {
    public void MergeSort(int[] array) {
        MergeSortChild(array, 0, array.length - 1);
    }

    private void MergeSortChild(int[] array, int left, int right) {
        if(left >= right) {
            return;
        }

        int mid = (left + right) / 2;

        MergeSortChild(array,left,mid);

        MergeSortChild(array,mid+1,right);
        //开始合并
        Merge(array,left,mid,right);
    }

    private void Merge(int[] array, int left, int mid, int right) {
        int[] tmpArr = new int[right - left + 1];
        int k = 0;
        int s1 = left, e1 = mid, s2 = mid + 1, e2 = right;
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] <= array[s2]) {
                tmpArr[k++] = array[s1++];
            } else {
                tmpArr[k++] = array[s2++];
            }
        }
        while (s1 <= e1) {
            tmpArr[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmpArr[k++] = array[s2++];
        }
        //临时数组当中存储的是有序的数据 -> 拷贝数据到原始数组当中
        for (int i = 0; i < k; i++) {
            array[i+left] = tmpArr[i];
        }
    }
    public void DsiOrder(int[] array) {
        Random ran = new Random();
        for (int i = 0; i < array.length; i++) {
            array[i] = ran.nextInt(1, 100);
        }
    }
}
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        Sort sort = new Sort();
        int[] array = new int[7];
        sort.DsiOrder(array);
        System.out.println("排序前:"+ Arrays.toString(array));
        sort.MergeSort(array);
        System.out.println("排序后:"+ Arrays.toString(array));
    }
}

        归并排序还可以进行非递归实现。要想进行非递归的实现,就要模拟出递归的过程。上面采用了分解与合并的过程,非递归的方式我们就可以省去分解的过程。我们对数组里的元素进行分组,每一个单独的元素都是有序的;然后缩小分组,对两个元素进行排序,变成每两个元素有序……直到数组里的每个元素都有序,也就是gap大于数组长度时。由于合并需要3个参数,根据上一种做法的分析,left=i,mid=left+gap-1,right=mid+gap。

    public void MergeSort(int[] array){
        int gap=1;
        while(gap < array.length){
            for (int i = 0; i < array.length; i=i+2*gap) {
                int left = i;
                int mid = left+gap-1;
                int right = mid+gap;
                Merge(array,left,mid,right);
            }
            gap = 2*gap;
        }
    }

        代码写到这里,我们需要考虑mid和right是否会越界的问题。

        完整代码实现:

import java.util.Random;

public class Sort {
    public void DisOrder(int[] array) {
        Random in = new Random();
        for (int i = 0; i < array.length; i++) {
            array[i] = in.nextInt(1, 100);
        }
    }

    public void MergeSort(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            for (int i = 0; i < array.length; i = i + 2 * gap) {
                int left = i;
                int mid = left + gap - 1;
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                int right = mid + gap;
                if (right >= array.length) {
                    right = array.length - 1;
                }
                Merge(array, left, mid, right);
            }
            gap = 2 * gap;
        }
    }

    private void Merge(int[] array, int left, int mid, int right) {
        int[] tmpArr = new int[right - left + 1];
        int k = 0;
        int s1 = left, e1 = mid, s2 = mid + 1, e2 = right;
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] <= array[s2]) {
                tmpArr[k++] = array[s1++];
            } else {
                tmpArr[k++] = array[s2++];
            }
        }
        while (s1 <= e1) {
            tmpArr[k++] = array[s1++];
        }
        while (s2 <= e2) {
            tmpArr[k++] = array[s2++];
        }
        //临时数组当中存储的是有序的数据 -> 拷贝数据到原始数组当中
        for (int i = 0; i < k; i++) {
            array[i + left] = tmpArr[i];
        }
    }
}
import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        Sort sort = new Sort();
        int[] array = new int[6];
        sort.DisOrder(array);
        System.out.println("排序前:"+ Arrays.toString(array));
        sort.MergeSort(array);
        System.out.println("排序后:"+ Arrays.toString(array));
    }
}

        归并排序是稳定的。时间复杂度,每次递归都需要合并,O(n) = nlogn;空间复杂度上,申请的临时数组与原数组长度一样,O(n)=n

        归并排序而已用于海量数据的排序问题。比如在磁盘等外部存储进行的排序,内存只有1G,需要排序的数据有100G。我们把100G内存分成200份,每一份512M。每一个文件都经过内存的排序后,每个文件都单独有序了。进行2路归并,同时对 200 份有序⽂件做归并过程,最终结果就有序了。

二、排序算法复杂度及稳定性分析

排序方式最好情况最坏情况空间复杂度稳定性
冒泡排序n^{2}n^{2}1稳定
插入排序nn^{2}1稳定
选择排序n^{2}n^{2}1不稳定
希尔排序nn^{2}1不稳定
堆排序nlognnlogn1不稳定
快速排序nlognn^{2}logn\rightarrow n不稳定
归并排序nlognnlognn稳定

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

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

相关文章

go切片定义和初始化

1.简介 切片是数组的一个引用&#xff0c;因此切片是引用类型&#xff0c;在进行传递时&#xff0c;遵守引用传递的机制。切片的使用和数组类似&#xff0c;遍历切片、访问切片的元素和切片的长度都一样。。切片的长度是可以变化的&#xff0c;因此切片是一个可以动态变化的数…

【NLP 39、激活函数 ⑤ Swish激活函数】

我的孤独原本是座荒岛&#xff0c;直到你称成潮汐&#xff0c;原来爱是让个体失序的永恒运动 ——25.2.25 Swish激活函数是一种近年来在深度学习中广泛应用的激活函数&#xff0c;由Google Brain团队在2017年提出。其核心设计结合了Sigmoid门控机制和线性输入的乘积&#xff0c…

南开提出1Prompt1Story,无需训练,可通过单个连接提示实现一致的文本到图像生成。

&#xff08;1Prompt1Story&#xff09;是一种无训练的文本到图像生成方法&#xff0c;通过整合多个提示为一个长句子&#xff0c;并结合奇异值重加权&#xff08;SVR&#xff09;和身份保持交叉注意力&#xff08;IPCA&#xff09;技术&#xff0c;解决了生成图像中身份不一致…

STM32驱动OLED屏幕全解析:从原理到温度显示实战(上) | 零基础入门STM32第五十三步

主题内容教学目的/扩展视频OLED显示屏重点课程电路原理&#xff0c;手册分析&#xff0c;驱动程序。初始化&#xff0c;清屏&#xff0c;ASCII字库&#xff0c;显示分区。调用显示函数。做带有加入图形和汉字显示的RTC时钟界面。讲字库的设计原理。 师从洋桃电子&#xff0c;杜…

MySQL语法总结

本篇博客说明&#xff1a; &#xff01;&#xff01;&#xff01;.注意此系列都用的是MySQL语句&#xff0c;和SQLServer&#xff0c;PostgreSQL有些细节上的差别&#xff01;&#xff01;&#xff01; 1.每个操作都是先展示出语法格式 2.然后是具体例子 3.本篇注脚与文本顺讯息…

从预测到控制:电力RK3568边缘计算机在电网调度中的全面应用

在智能电网的快速发展中&#xff0c;电力Ubuntu工控机&#xff08;简称“电力工控机”&#xff09;作为核心设备&#xff0c;扮演着不可或缺的角色。特别是在智能电网调度场景中&#xff0c;电力工控机的高效、稳定和智能化特性&#xff0c;为电网的稳定运行和高效管理提供了强…

day-106 统计放置房子的方式数

思路 动态规划&#xff1a;因为中间有街道隔开&#xff0c;所以只需计算一边街道的排列方式&#xff0c;最后计算平方即可 解题过程 动态转换方程&#xff1a;f[i]f[i-1]f[i-2] Code class Solution {int num 1000000007;public int countHousePlacements(int n) {int arr[…

PostgreSQL安装和mcp PostgreSQL

文章目录 一. 安装之后修改权限并登录1. 确保当前用户具有sudo权限2. 修改/etc/postgresql/<版本号>/main/pg_hba.conf配置文件为trust&#xff0c;可以免密登录3. 进行免密登录4. 添加root用户和修改postgres用户密码1. postgres用户密码2. 添加root用户3. 为root用户设…

LPZero: Language Model Zero-cost Proxy Search from Zero(未更新完预览版本)

LPZero代码 摘要 神经架构搜索 (NAS) 有助于自动执行有效的神经网络搜索&#xff0c;同时需要大量的计算资源&#xff0c;尤其是对于语言模型。零样本 NAS 利用零成本 (ZC) 代理来估计模型性能&#xff0c;从而显着降低计算需求。然而&#xff0c;现有的 ZC 代理严重依赖于深…

字典树运用

字典树运用 字典树LC208 创建字典树0-1字典树 字典树 字典树又叫 前缀树&#xff0c; 是一种树形数据结构&#xff0c;用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景&#xff0c;例如自动补全和拼写检查。 LC208 创建字典树 这是一个字符串字典树…

RReadWriteLock读写锁应用场景

背景 操作涉及一批数据&#xff0c;如订单&#xff0c;可能存在多个场景下操作&#xff0c;先使用读锁&#xff0c;从redis缓存中获取操作中数据 比如 关闭账单&#xff0c; 发起调账&#xff0c; 线下结算&#xff0c; 合并支付 先判断当前操作的数据&#xff0c;是否在…

0087.springboot325基于Java的企业OA管理系统的设计与实现+论文

一、系统说明 基于springbootvue的企业OA管理系统,系统功能齐全, 代码简洁易懂&#xff0c;适合小白学编程。 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数…

Spring Boot 3 整合 MinIO 实现分布式文件存储

引言 文件存储已成为一个做任何应用都不可回避的需求。传统的单机文件存储方案在面对大规模数据和高并发访问时往往力不从心&#xff0c;而分布式文件存储系统则提供了更好的解决方案。本篇文章我将基于Spring Boot 3 为大家讲解如何基于MinIO来实现分布式文件存储。 分布式存…

Redis|集群 Cluster

文章目录 是什么能干嘛集群算法-分片-槽位slotredis集群的槽位slotredis集群的分片分片槽位的优势slot槽位映射——业界的3种解决方案小厂&#xff1a;哈希取余分区中厂&#xff1a;一致性哈希算法分区大厂&#xff1a;哈希槽分区 面试题&#xff1a;为什么 Redis 集群的最大槽…

【定制开发】碰一碰发视频系统定制开发,支持OEM

在短视频营销爆发的2025年&#xff0c;"碰一碰发视频"技术已成为实体商家引流标配。某连锁餐饮品牌通过定制化开发&#xff0c;单月视频发布量突破10万条&#xff0c;获客成本降低80%&#xff01;本文将深入解析该系统的技术架构与开发要点&#xff0c;助你快速搭建高…

Linux上位机开发(开篇)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 传统的上位机开发&#xff0c;一般都是默认pc软件开发。既然是pc软件&#xff0c;一般来说都是基于windows平台开发。开放的框架&#xff0c;无非是…

算法005——有效三角形个数

力扣——有效三角形个数点击链接跳转 判断三条边是否能组成三角形&#xff0c;大家第一时间想到的就是两边之和大于第三边 但是运用这个方法&#xff0c;我们需要判断三次&#xff0c;有一个更简单的方法&#xff0c;只需要判断一次 因为 C 已经是三边之中最大的了&#xff…

【STM32】江科大STM32学习笔记汇总(已完结)

00. 目录 文章目录 00. 目录01. STM32学习笔记汇总02. 相关资料下载03. 打赏04. 附录 01. STM32学习笔记汇总 【STM32】STM32学习笔记-课程简介(01) 【STM32】STM32学习笔记-STM32简介(02) 【STM32】STM32学习笔记-软件安装(03) 【STM32】STM32学习笔记-新建工程(04) 【ST…

【Python编程】高性能Python Web服务部署架构解析

一、FastAPI 与 Uvicorn/Gunicorn 的协同 1. 开发环境&#xff1a;Uvicorn 直接驱动 作用&#xff1a;Uvicorn 作为 ASGI 服务器&#xff0c;原生支持 FastAPI 的异步特性&#xff0c;提供热重载&#xff08;--reload&#xff09;和高效异步请求处理。 启动命令&#xff1a; u…

OSPF的各种LSA类型,多区域及特殊区域

一、OSPF的LSA类型 OSPF&#xff08;开放最短路径优先&#xff09;协议使用多种LSA&#xff08;链路状态通告&#xff09;类型来交换网络拓扑信息。以下是主要LSA类型的详细分类及其作用&#xff1a; 1. Type 1 LSA&#xff08;路由器LSA&#xff09; 生成者&#xff1a;每个…