排序算法 — 归并排序

news2024/12/29 10:36:15

文章目录

  • 归并排序介绍
    • 从下往上的归并排序
    • 从上往下的归并排序
  • 归并排序实现
    • 从上往下的归并排序
    • 从下往上的归并排序
  • 归并排序的时间复杂度和稳定性
    • 归并排序时间复杂度
    • 归并排序稳定性
  • 代码实现
  • 核心&总结

每日一道算法,提高脑力。第五天(时隔7天,终于回归),归并排序。

归并排序介绍

根据具体的实现,归并排序包括"从上往下"和"从下往上"2种方式。

从下往上的归并排序

将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就得到了我们想要的排序结果。(参考下面的图片)

从上往下的归并排序

它与"从下往上"在排序上是反方向的。它基本包括3步:

  • 分解 – 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
  • 求解 – 递归地对两个子区间a[low…mid] 和 a[mid+1…high]进行归并排序。递归的终结条件是子区间长度为1。
  • 合并 – 将已排序的两个子区间a[low…mid]和 a[mid+1…high]归并为一个有序的区间a[low…high]。

在这里插入图片描述

归并排序实现

从上往下的归并排序

从上往下的归并排序采用了递归的方式实现。它的原理非常简单,如下图:

在这里插入图片描述

通过"从上往下的归并排序"来对数组{80,30,60,40,20,10,50,70}进行排序时:

将数组{80,30,60,40,20,10,50,70}看作由两个有序的子数组{80,30,60,40}和{20,10,50,70}组成。对两个有序子树组进行排序即可。

将子数组{80,30,60,40}看作由两个有序的子数组{80,30}和{60,40}组成。 将子数组{20,10,50,70}看作由两个有序的子数组{20,10}和{50,70}组成。

将子数组{80,30}看作由两个有序的子数组{80}和{30}组成。 将子数组{60,40}看作由两个有序的子数组{60}和{40}组成。将子数组{20,10}看作由两个有序的子数组{20}和{10}组成。将子数组{50,70}看作由两个有序的子数组{50}和{70}组成。

从下往上的归并排序

从下往上的归并排序的思想正好与"从下往上的归并排序"相反。如下图:

在这里插入图片描述

通过"从下往上的归并排序"来对数组{80,30,60,40,20,10,50,70}进行排序时:

  • 将数组{80,30,60,40,20,10,50,70}看作由8个有序的子数组{80},{30},{60},{40},{20},{10},{50}和{70}组成。
  • 将这8个有序的子数列两两合并。得到4个有序的子树列{30,80},{40,60},{10,20}和{50,70}。
  • 将这4个有序的子数列两两合并。得到2个有序的子树列{30,40,60,80}和{10,20,50,70}。
  • 将这2个有序的子数列两两合并。得到1个有序的子树列{10,20,30,40,50,60,70,80}。

归并排序的时间复杂度和稳定性

归并排序时间复杂度

归并排序的时间复杂度是O(N*lgN)。

假设被排序的数列中有N个数。遍历一趟的时间复杂度是O(N),需要遍历多少次呢? 归并排序的形式就是一棵二叉树,它需要遍历的次数就是二叉树的深度,而根据完全二叉树的可以得出它的时间复杂度是O(N*lgN)。

归并排序稳定性

归并排序是稳定的算法,它满足稳定算法的定义。

算法稳定性 – 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的!

代码实现


package com.zxn;

/**
 * @author zxn
 * @ClassName MergeSort
 * @Description
 * @createTime 2023年05月04日 23:25:00
 */
public class MergeSort {
    /*
     * 将一个数组中的两个相邻有序区间合并成一个
     *
     * 参数说明:
     *     a -- 包含两个有序区间的数组
     *     start -- 第1个有序区间的起始地址。
     *     mid   -- 第1个有序区间的结束地址。也是第2个有序区间的起始地址。
     *     end   -- 第2个有序区间的结束地址。
     */
    public static void merge(int[] a, int start, int mid, int end) {
        int[] tmp = new int[end-start+1];    // tmp是汇总2个有序区的临时区域
        int i = start;            // 第1个有序区的索引
        int j = mid + 1;        // 第2个有序区的索引
        int k = 0;                // 临时区域的索引

        while(i <= mid && j <= end) {
            if (a[i] <= a[j])
                tmp[k++] = a[i++];
            else
                tmp[k++] = a[j++];
        }

        while(i <= mid)
            tmp[k++] = a[i++];

        while(j <= end)
            tmp[k++] = a[j++];

        // 将排序后的元素,全部都整合到数组a中。
        for (i = 0; i < k; i++)
            a[start + i] = tmp[i];

        tmp=null;
    }

    /*
     * 归并排序(从上往下)
     *
     * 参数说明:
     *     a -- 待排序的数组
     *     start -- 数组的起始地址
     *     endi -- 数组的结束地址
     */
    public static void mergeSortUp2Down(int[] a, int start, int end) {
        if(a==null || start >= end)
            return ;

        int mid = (end + start)/2;
        mergeSortUp2Down(a, start, mid); // 递归排序a[start...mid]
        mergeSortUp2Down(a, mid+1, end); // 递归排序a[mid+1...end]

        // a[start...mid] 和 a[mid...end]是两个有序空间,
        // 将它们排序成一个有序空间a[start...end]
        merge(a, start, mid, end);
    }


    /*
     * 对数组a做若干次合并: 数组a的总长度为len,将它分为若干个长度为gap的子数组;
     *             将"每2个相邻的子数组" 进行合并排序。
     *
     * 参数说明:
     *     a -- 待排序的数组
     *     len -- 数组的长度
     *     gap -- 子数组的长度
     */
    public static void mergeGroups(int[] a, int len, int gap) {
        int i;
        int twolen = 2 * gap;    // 两个相邻的子数组的长度

        // 将"每2个相邻的子数组" 进行合并排序。
        for(i = 0; i+2*gap-1 < len; i+=(2*gap))
            merge(a, i, i+gap-1, i+2*gap-1);

        // 若 i+gap-1 < len-1,则剩余一个子数组没有配对。
        // 将该子数组合并到已排序的数组中。
        if ( i+gap-1 < len-1)
            merge(a, i, i + gap - 1, len - 1);
    }

    /*
     * 归并排序(从下往上)
     *
     * 参数说明:
     *     a -- 待排序的数组
     */
    public static void mergeSortDown2Up(int[] a) {
        if (a==null)
            return ;

        for(int n = 1; n < a.length; n*=2)
            mergeGroups(a, a.length, n);
    }

    public static void main(String[] args) {
        int i;
        int a[] = {80,30,60,40,20,10,50,70};

        System.out.printf("before sort:");
        for (i=0; i<a.length; i++)
            System.out.printf("%d ", a[i]);
        System.out.printf("\n");

        mergeSortUp2Down(a, 0, a.length-1);        // 归并排序(从上往下)
        //mergeSortDown2Up(a);                    // 归并排序(从下往上)

        System.out.printf("after  sort:");
        for (i=0; i<a.length; i++)
            System.out.printf("%d ", a[i]);
        System.out.printf("\n");
    }
}


核心&总结

  • 针对从上之下的归并排序,要先把数组分解 – 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
  • 求解 – 递归地对分解出的两个子区间a[low…mid] 和 a[mid+1…high]进行归并排序。
  • 终止点 – 递归的终结条件是子区间长度为1。
  • 合并 – 将已排序的两个子区间a[low…mid]和 a[mid+1…high]归并为一个有序的区间a[low…high]。

个人认为算法的重点在于要知道他的定义,这样才能看得懂、学得会。

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

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

相关文章

mac下用git客户端生成ssh秘钥并配置到souretree进行使用

一、使用git 生成 ssh 密钥 1、Mac 安装 git 客户端 打开终端&#xff0c;执行命令&#xff1a; $ brew install git2、执行命令 $ git config --global user.name "xxx" 你自己的名字 $ git config --global user.email "xxxxxx.com&q…

Educational-Codeforces-Round-147-Rated-for-Div-2

title: Educational Codeforces Round 147 (Rated for Div. 2) date: 2023-04-21 15:47:29 categories: AlgorithmCodeforces tags:codeforcesdiv2 Educational Codeforces Round 147 (Rated for Div. 2) A. Matching 题目大意 给你一个字符串&#xff0c;里面包含数字和?,…

Redis缓存穿透和雪崩

Redis缓存穿透和雪崩 Redis缓存的使用&#xff0c;极大的提升了应用程序的性能和效率&#xff0c;特别是数据查询方面。但同时&#xff0c;它也带来了一些问题。其中&#xff0c;最要害的问题&#xff0c;就是数据的一致性问题&#xff0c;从严格意义上讲&#xff0c;这个问题…

AI 工具合辑盘点(七)持续更新 之 AI 音频生成工具

AI 音频生成工具 想要不亲自录制&#xff0c;快速将文本转换为语音&#xff1f;AI 音频生成工具为你提供数千种语音选择&#xff0c;从“普通人”的声音到模仿演员、政治家或电影角色的合成声音&#xff0c;各种声音应有尽有 &#x1f5e3; AI 音频生成工具可用于创建商业用途…

模糊PID模糊控制(清晰化方法梯形图实现)

模糊PID的模糊化请参看下面的博客文章: 博途PLC模糊PID三角隶属度函数指令(含Matlab仿真)_plc 模糊pid_RXXW_Dor的博客-CSDN博客三角隶属度函数FC,我们采用兼容C99标准的函数返回值写法,在FB里调用会更加直观,下面给大家具体讲解代码。常规写法的隶属度函数FC可以参看下…

Python小姿势 - Python中的列表推导式

Python中的列表推导式 Python中的列表推导式是一种很好的创建列表的方式。它允许你将一个操作应用于列表中的每个元素&#xff0c;并将结果放入一个新的列表中。 例如&#xff0c;假设你有一个包含数字的列表&#xff0c;但是你想将每个数字都乘以2&#xff0c;并将结果放入一个…

第三十二章 Unity Mecanim动画系统(上)

在上一章节中&#xff0c;我们介绍了Unity的旧版动画系统&#xff0c;本章节来介绍新版的Mecanim动画系统。新版的Mecanim动画系统实际是对旧版动画系统的升级。新版的Mecanim动画系统仍然是建立在动画片段的基础上的&#xff0c;只不过它给我们提供了一个可视化的窗口来编辑动…

服务攻防-数据库安全-服务应用的安全问题以及测试流程-MysqlHadoop未授权访问RCE-漏洞复现

目录 一、服务应用的安全问题 1、配置不当——未授权访问 2、安全机制——特定安全漏洞 3、安全机制——弱口令爆破攻击 二、服务应用的安全测试思路 1、判断服务是否开放 2、判断服务类型 3、判断利用方式 三、Mysql-未授权访问-CVE-2012-2122 利用 1、漏洞概述 2、…

Detours HOOK

参考文本 如何使用Detours库进行DLL注入&#xff0c;拦截API - 知乎 (zhihu.com) 解决‘nmake‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。_nmake 不是内部或外部命令,也不是可运行的程序 或批处理文件。_AI浩的博客-CSDN博客 Detours使用方法&#xff0c;简单…

五音不全?手把手教你用自己声音唱任何歌;最详细的Auto-GPT整理;4月AI绘画模型推荐;HayoAI平台简直太酷了 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『ChatGPT Code Interpreter Magic』魔法&#xff01;离谱&#xff01;正在怀疑人生… OpenAI 近期面向部分用户发放了 Code Interp…

一起单测引起的项目加载失败惨案 | 京东云技术团队

作者&#xff1a;京东科技 宋慧超 一、前言 最近在开发一个功能模块时&#xff0c;在功能自测阶段&#xff0c;通过使用单测测试功能的完整性&#xff0c;在测试单测联通性使用到静态方法测试时&#xff0c;发现单测报错&#xff0c;通过查阅解决方案发现需要对Javaassist包进…

vue3+webpack4 前端优化首屏时间

项目背景 中小项目&#xff0c;Vue-cli3 vue2 webpack4 目标 缩短白屏时间&#xff0c;用户能够更快的看到我的页面&#xff01; 白屏时间&#xff1a;从打开页面到看到页面&#xff0c;中间白屏停留的时间。 方向 1.减少资源体积&#xff0c;从而缩短请求时间 2.减少资…

企业数字化转型为什么难?低代码平台能为企业带来什么?

企业数字化转型困难原因是多方便的&#xff0c;比如&#xff1a; 遗留系统&#xff1a;许多企业在难以替换或与新技术集成的遗留技术系统上投入了大量资金。 变革阻力&#xff1a;企业越大&#xff0c;参与决策的人就越多&#xff0c;让每个人都接受新工作方式的难度就越大。 …

前端架构师-week4-Node多进程开发入门

目录 学习路径 官方文档 什么是进程 child_process 用法 exec & execFile 用法 spawn 用法以及与exec & execFile的区别 fork用法及父子进程通信机制讲解 child_process同步方法使用教程 学习路径 官方文档 中文版&#xff1a;http://nodejs.cn/api/child_pr…

Flutter 如何将代码显示到界面上

前言 如何优雅的将项目中的代码&#xff0c;亦或是你的demo代码展示到界面上&#xff1f;本文对使用简单、便于维护且通用的解决方案&#xff0c;进行相关的对比和探究 为了节省大家的时间&#xff0c;把最终解决方案的相关接入和用法写在前面 预览代码 快速开始 接入&…

C. Permutation Game(博弈 + 拓扑的思想)

Problem - C - Codeforces 经过漫长的一天&#xff0c; Aice和Bob决定玩一个小游戏。游戏棋盘由n个格子组成&#xff0c;在一条直线上&#xff0c;编号从1到n,每个格子包含一个数字4;,qy在1到n.之间&#xff0c;而且没有两个格子包含相同的数字。 一个棋子被放在其中一个格子里…

nestJS入门cli 创建项目以及集成swagger和mysql

nestJs 1. 简介 介绍 NestJS NestJS 是一个基于 TypeScript 的渐进式 Node.js 框架&#xff0c;它结合了 OOP、FP 和 FRP 的元素&#xff0c;以提供一种现代且可扩展的开发体验。NestJS 建立在 Express.js 之上&#xff0c;但是提供了更加抽象和模块化的方式来编写应用程序。…

蓝牙资讯|苹果与谷歌起草蓝牙定位追踪设备行业规范

苹果与谷歌于当地时间5月2日联合提交了一份行业规范草案&#xff0c;以帮助应对蓝牙定位追踪设备遭滥用的问题。目前已有包括三星在内的追踪设备制造厂商宣布支持该草案。 据了解&#xff0c;苹果与谷歌此次联合提交的行业规范草案将云熙蓝牙定位追踪设备兼容跨iOS以及Android平…

【LeetCode: 518. 零钱兑换 II | 暴力递归=>记忆化搜索=>动态规划 | 背包模型】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

十五周算法训练营——二叉搜索树(BST)

今天是十五周算法训练营的第五周&#xff0c;主要讲二叉搜索树专题&#xff0c;包含&#xff1a;验证二叉搜索树、不同的二叉搜索树、二叉树的最近公共祖先、二叉搜索树的最近公共祖先。&#xff08;欢迎加入十五周算法训练营&#xff0c;与小伙伴一起卷算法&#xff09; BST的…