排序算法之——归并排序

归并排序

  • 1. 基本思想
  • 2. 数据的分解
  • 3. 数据的合并
  • 4.归并排序的实现
    • 4.1 递归实现
      • 4.1.1 一个易错点
      • 4.1.2 运行结果
    • 4.2 非递归实现
      • 4.2.1 图示思路
      • 4.2.2 代码实现
      • 4.2.3 一个易错点
      • 4.2.4 修改后的代码
      • 4.2.5 运行结果
  • 6. 时间复杂度
  • 7. 空间复杂度
  • 8. 稳定性
  • 9. 动图演示

1. 基本思想

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

2. 数据的分解

数据的分解采用递归分解,形式上和一棵完全二叉树相似。
在这里插入图片描述

3. 数据的合并

数据的合并过程不仅仅是将短数据合成长的,否则这仅仅是原来分解数据的逆过程。数据的合并过程中牵扯到对两组数据的排序再合并。为了实现这一目的,采用了双指针法。下面以图1.1中的在这里插入图片描述
两组数据为例进行说明;
在这里插入图片描述

4.归并排序的实现

4.1 递归实现

/**
     * 归并排序
     * 时间复杂度:O(n*logn)
     * 空间复杂度:O(n)
     * 稳定性:稳定
     * @param array
     */
    public static void mergeSort(int[] array){//为了使参数只有一个,保持统一性,调用了mergeFunc()方法
        mergeFunc(array,0, array.length-1);
    }

    public static void mergeFunc(int[] array,int left,int right){
        if(left >= right){
            return;
        }
        int mid = left + (right - left) / 2;
        mergeFunc(array,left,mid); //分解左边
        mergeFunc(array,mid+1,right);//分解右边
        //开始合并
        merge(array,left,mid,right);
    }

    public static void merge(int[] array,int left,int mid,int right){
        int s1 = left;
        int e1 = mid;
        int s2 = mid+1;
        int e2 = right;
        int[] tmpArray = new int[right - left + 1];
        int k = 0;
        //1.保证两个数组都有数据
        while(s1 <= e1 && s2 <= e2){
            if(array[s1] < array[s2]){
                tmpArray[k] = array[s1];
                k++;
                s1++;
            }else{
                tmpArray[k] = array[s2];
                k++;
                s2++;
            }
        }
        //2.其中一个数组已经拷贝完,将另一个数组剩下的部分拷贝回临时数组
        while(s1 <= e1){
            tmpArray[k] = array[s1];
            k++;
            s1++;
        }
        while(s2 <= e2){
            tmpArray[k] = array[s2];
            k++;
            s2++;
        }
        //3.将临时数组中的数据拷贝回原数组中
        for(int i = 0;i < k;i++){
            array[left + i] = tmpArray[i];//这一步array[]的下标要注意
        }
    }

4.1.1 一个易错点

在将临时数组tmpArray[]中的数据放回原数组array[]的过程中,要特别注意下标问题。如图:
在这里插入图片描述
黄线框里的代码不能写成array[i] = tmpArray[i]。以一张图说明:
在这里插入图片描述

4.1.2 运行结果

在这里插入图片描述

4.2 非递归实现

4.2.1 图示思路

在这里插入图片描述

4.2.2 代码实现

/**
 * 并排序(非递归实现)
 * @param array
 */
public static void mergeSortNotRecursion(int[] array){//为了使参数只有一个,保持统一性,调用了mergeFunc()方法
        int gapNumber = 1; //gapNumber表示每组数据的个数
        //最外层控制组数
        while(gapNumber < array.length){
            for(int i = 0;i < array.length;i += gapNumber){//i每次走gapNumber个距离
                int left = i;
                int mid = left + gapNumber - 1;
                int right = mid + gapNumber;
                merge(array,left,mid,right);
            }
            gapNumber *= 2;
        }
    }

 public static void merge(int[] array,int left,int mid,int right){
        int s1 = left;
        int e1 = mid;
        int s2 = mid+1;
        int e2 = right;
        int[] tmpArray = new int[right - left + 1];
        int k = 0;
        //1.保证两个数组都有数据
        while(s1 <= e1 && s2 <= e2){
            if(array[s1] < array[s2]){
                tmpArray[k] = array[s1];
                k++;
                s1++;
            }else{
                tmpArray[k] = array[s2];
                k++;
                s2++;
            }
        }
        //2.其中一个数组已经拷贝完,将另一个数组剩下的部分拷贝回临时数组
        while(s1 <= e1){
            tmpArray[k] = array[s1];
            k++;
            s1++;
        }
        while(s2 <= e2){
            tmpArray[k] = array[s2];
            k++;
            s2++;
        }
        //3.将临时数组中的数据拷贝回原数组中
        for(int i = 0;i < k;i++){
            array[left + i] = tmpArray[i];//这一步array[]的下标要注意
        }
    }

4.2.3 一个易错点

在这里插入图片描述

4.2.4 修改后的代码

while(gapNumber < array.length){
            for(int i = 0;i < array.length;i += gapNumber){//i每次走gapNumber个距离
                int left = i;
                int mid = left + gapNumber - 1;
                if(mid >= array.length){
                    mid = array.length - 1;
                }
                int right = mid + gapNumber;
                if(right >= array.length){
                    right = array.length - 1;
                }
                merge(array,left,mid,right);
            }
            gapNumber *= 2;
        }

4.2.5 运行结果

在这里插入图片描述

6. 时间复杂度

整个归并排序的过程相当于一棵完全二叉树的创建过程。假设待排序的数据的容量为n,则完全二叉树的高度为log2(n+1)(向上取整),每一层遍历n个数据,最终用时nlog2(n+1),故最终的时间复杂度为O(nlog2(n))。

7. 空间复杂度

由于每次归并两组数据的过程中都借用了临时数组tmpArray[],且tmpArray[]的长度至少要等于两组数据的元素个数之和,故最终的空间复杂度为O(n)。

8. 稳定性

归并排序是一种稳定排序。

9. 动图演示

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

h-table(表格列表组件的全封装)

文章目录 概要h-table的封装过程查询组件封装 h-highForm结果页右侧工具栏封装RightToolbar结果页列表组件h-table结果页vue页面使用js文件有需要的请私信博主&#xff0c;还请麻烦给个关注&#xff0c;博主不定期更新组件封装&#xff0c;或许能够有所帮助&#xff01;&#x…

如何用GPT进行成像光谱遥感数据处理?

第一&#xff1a;遥感科学 从摄影侦察到卫星图像 遥感的基本原理 遥感的典型应用 第二&#xff1a;ChatGPT ChatGPT可以做什么&#xff1f; ChatGPT演示使用 ChatGPT的未来 第三&#xff1a;prompt 提示词 Prompt技巧&#xff08;大几岁&#xff09; 最好的原则和策…

互动游戏团队如何将性能体验优化做到TOP级别

一、背景 随着互动游戏业务 DAU 量级增加&#xff0c;性能和体验重要性也越发重要&#xff0c;好的性能和体验不仅可以增加用户使用体感&#xff0c;也可以增加用户对于互动游戏的使用粘性。 对现状分析&#xff0c;主要存在首屏渲染速度慢、打开页面存在白屏、页面加载过多资…

app测试必掌握的核心测试:UI、功能测试!

一、UI测试 UI即User Interface (用户界面)的简称。UI 设计则是指对软件的人机交互、操作逻辑、界面美观的整体设计。好的UI设计不仅是让软件变得有个性有品味,还要让软件的操作变得舒适、简单、自由、充分体现软件的定位和特点。手机APP从启动界面开始, 到运行过程,直至退出,…

聊聊mysql的七种日志

进入正题前,可以先简单介绍一下,MySQL的逻辑架构, MySQL的逻辑架构大致可以分为三层: 第一层:处理客户端连接、授权认证,安全校验等。第二层:服务器 server 层,负责对SQL解释、分析、优化、执行操作引擎等。第三层:存储引擎,负责MySQL中数据的存储和提取。我们要知道…

云图极速版限时免费活动

产品介绍 云图极速版是针对拥有攻击面管理需求的用户打造的 SaaS 应用&#xff0c;致力于协助用户发现并管理互联网资产攻击面。 实战数据 (2023.11.6 - 2024.2.23) 云图极速版上线 3 个月以来&#xff0c;接入用户 3,563 家&#xff0c;扫描主体 19,961 个&#xff0c;累计发…

OpenCV笔记4:级联分类器实现嘴部检测

OpenCV 嘴部检测 """ 嘴部区域检测 1. 静态图像检测嘴部区域创建分类器加载特征文件检测图像绘制嘴部区域显示 2. 切换为摄像头 """ import cv2 import numpy as npclass FaceDetect:def __init__(self):# 级联分类器# 创建级联分类器&#xf…

云原生之容器管理工具Portainer

1. 简介 前面文章我们讲Docker、Docker Compose和Docker Swarm都是在Linux系统上手工命令行去操作&#xff0c;在第一次安装的时候可以命令行&#xff0c;以后运维和CICD流程操作中&#xff0c;如果还要命令行去各个节点操作&#xff0c;操作就麻烦了&#xff0c;工作效…

Seata 入门知识

目录 概述 工作流程 工作模式 AT模式 TCC模式 概述 Seata 是一款开源的分布式事务解决方案&#xff0c;致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式&#xff0c;为用户打造一站式的分布式解决方案。 AT模式是阿里首推…

Linux系统运维:离线安装sar-性能监视和分析工具

目 录 一、前言 二、系统环境 三、安装sar &#xff08;一&#xff09;准备工作 1、下载 sar 工具的安装包&#xff1a; 2、将安装包传输到 CentOS 服务器 &#xff08;二&#xff09;安装工作 1、解压 2、配置安装 3、编译 4、安装 &#xff08;三&#xff0…

C# Onnx 使用onnxruntime部署实时视频帧插值

目录 介绍 效果 模型信息 项目 代码 下载 C# Onnx 使用onnxruntime部署实时视频帧插值 介绍 github地址&#xff1a;https://github.com/google-research/frame-interpolation FILM: Frame Interpolation for Large Motion, In ECCV 2022. The official Tensorflow 2…

【Flink集群RPC通讯机制(四)】集群组件(tm、jm与rm)之间的RPC通信

文章目录 1. 集群内部通讯方法概述2. TaskManager向ResourceManager注册RPC服务3. JobMaster向ResourceManager申请Slot计算资源 现在我们已经知道Flink中RPC通信框架的底层设计与实现&#xff0c;接下来通过具体的实例了解集群运行时中组件如何基于RPC通信框架构建相互之间的调…

大数据 - Spark系列《十一》- Spark累加器详解

Spark系列文章&#xff1a; 大数据 - Spark系列《一》- 从Hadoop到Spark&#xff1a;大数据计算引擎的演进-CSDN博客 大数据 - Spark系列《二》- 关于Spark在Idea中的一些常用配置-CSDN博客 大数据 - Spark系列《三》- 加载各种数据源创建RDD-CSDN博客 大数据 - Spark系列《…

2024/02/23

使用消息队列完成两个进程间相互通信 A.c #include<myhead.h> struct msgbuf {long mtype;char mtext[1024]; }; //定义表示正文内容大小的宏 #define MSGSIZE sizeof(struct msgbuf)-sizeof(long)int main(int argc, const char *argv[]) {//创建一个key值key_t key;ke…

知乎66条高赞回答,句句醍醐灌顶!

-01- 穷人是小心翼翼地大方&#xff0c; 有钱人是大大方方地小气。 ——论如何判断一个人是真有钱还是装有钱 -02- 枕头要常晒&#xff0c; 因为里面装满了心酸的泪和发霉的梦。 ——一切终将随风而逝 -03- 人活得累&#xff0c;一是太认真&#xff0c;二是太想要。 …

第3部分 原理篇2去中心化数字身份标识符(DID)(3)

3.2.2.4. DID文档 (DID Document) 本聪老师&#xff1a;DID标识符和DID URL还都只是ID&#xff0c;必须为它附加一个基本属性才可以证明是该主体独有的。这个就是我们下面介绍的DID文档。 本聪老师&#xff1a;每个DID标识符都唯一对应一个DID文档&#xff0c;也可以说&#x…

计算机功能简介:EC, NVMe, SCSI/ISCSI与块存储接口 RBD,NUMA

一 EC是指Embedded Controller 主要应用于移动计算机系统和嵌入式计算机系统中&#xff0c;为此类计算机提供系统管理功能。EC的主要功能是控制计算机主板上电时序、管理电池充电和放电&#xff0c;提供键盘矩阵接口、智能风扇接口、串口、GPIO、PS/2等常规IO功能&#xff0c;…

docker自定义网络实现容器之间的通信

Background docker原理 docker是一个Client-Server结构的系统&#xff0c;Docker的守护进程运行在主机上。通过Socket从客户端访问。docker核心三大组件&#xff1a;image–镜像、container-容器、 repository-仓库。docker使用的cpu、内存以及系统内核等资源都是直接使用宿主…

A Novel Two-Layer DAG-based Reactive Protocol for IoT Data Reliability in Metaverse

在IOT 场景中&#xff0c;需要保证数据的完整性和可靠性。通常区块链可以用来做这件事&#xff0c;但是IoT 设备的计算能力和贷款都是有限的。 对于PBFT 要求的通信量太大。 本文提出的 two layer directed acycle graph (2LDAG) 是一种被动共识协议&#xff0c;除非有节点主动…

快速构建 Debezium MySQL Example 数据库

博主历时三年精心创作的《大数据平台架构与原型实现&#xff1a;数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行&#xff0c;点击《重磅推荐&#xff1a;建大数据平台太难了&#xff01;给我发个工程原型吧&#xff01;》了解图书详情&#xff0c;…