算法-从归并排序到归并分治

news2024/11/24 17:20:49

文章目录

    • 前言介绍
    • 1 . 简单的归并排序
    • 2 . 数组的最小和问题
    • 3 . 逆序数对问题
    • 4 . 翻转对数量的计算

前言介绍

归并排序是Merge sort)是一种有效、稳定的排序算法,它采用了分治法(Divide and Conquer)的典型应用,何为分治 ? 即把多个事件分为两个或者多个子问题来解决(其实是一种递归寻找子问题的思路)

归并分支的思想

  • 原理 : 在思考一个问题在大范围上的答案, 是否等于, 左部分的答案 + 右部分的答案 + 跨越左右的答案
  • 在计算跨越左右的答案的时候, 我们要思考如果加上左右都有序的这个条件, 会不会获得计算的便利性
  • 如果满足上述的两点, 那我们的这个题大概率是通过归并分治的思路去解决
  • 只需要改编我们的merge函数即可, 在求解问题的同时, 加上归并排序的过程即可

1 . 简单的归并排序

首先先讲一下我们的归并排序,归并排序是一种高效的排序算法
在这里插入图片描述
代码的实现细节如下

class Solution {
    public int[] sortArray(int[] nums) {

      if (nums == null || nums.length < 2) {
            return nums;
        }
        //下面是排序的主体
        mergeSort(nums, 0, nums.length - 1);
        return nums;
    }

    private void mergeSort(int[] nums, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = left + ((right - left) >> 1);
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        //其实质是每次通过merge函数之后,之前比较的结果就会被保留进行加速
        merge(nums, left, mid, right);
    }

    private void merge(int[] nums, int left, int mid, int right) {
        int[] temp = new int[right - left + 1];
        int pl = left;
        int pr = mid + 1;
        int index = 0;
        while (pl <= mid && pr <= right) {
            if (nums[pl] <= nums[pr]) {
                temp[index++] = nums[pl++];
            } else {
                temp[index++] = nums[pr++];
            }
        }
        while (pl <= mid) {
            temp[index++] = nums[pl++];
        }
        while (pr <= right) {
            temp[index++] = nums[pr++];
        }
        for (int i = 0; i < index; ++i) {
            nums[left + i] = temp[i];
        }
    }
}

有了上面的归并分治的基础,我们回头看我们的归并排序,其实就是分治法的一种标准应用, 总体有序 = 左半边有序 + 右半边有序 + 跨越左右有序

2 . 数组的最小和问题

在这里插入图片描述
用暴力方法,这道题是很好想的,时间复杂度是O(N^2)
我们先来尝试分析一下这道题

首先 :
逆向思维转换,求数组的小和,其实就是求一个数在小和里面贡献了多少
即为,对于任意一个数来说,该数右边有几个数比该数大,那就贡献了几份这个数字的大小
其次 :
进行分治法分析 , 总小和数 = 左小和数 + 右小和数 + 左跨右的小和数
请一定要记住 , 计算某一半边的小和数的时候 , 一定不能与另一半边关联(分)
最后 :
有了分治的思想 , 当左半边跟右半边有序的时候 , 题目会不会更简便 , 答案是肯定的 , 有序了之后 , 每次滑动的时候 , 指针是不会进行回退的(滑动窗口的思想) , 所以时间复杂度可以达到 O(N) (统计的时候)

下面是代码实现

 /**
     * 求一个数组的小和 :
     * 基本思路是首先用逆向思维法, 想要计算小和, 可以统计某一个数字在小和中的出现的次数来累加求和计算
     * 整个数组的小和 == 左侧数组的小和 + 右侧数组的小和 + 跨越左右的小和
     * 这道题, 如果不进行排序的话, 时间复杂度还是O(N^2)...
     * 在进行排序了之后,我们的统计的时间复杂度其实是O(N)...
     * 所以这道题可以采用归并分治的策略来解决
     *
     * @param nums
     * @param left
     * @param right
     * @return
     */
 public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int sz = in.nextInt();
        int[] nums = new int[sz];
        for (int i = 0; i < nums.length; ++i) {
            nums[i] = in.nextInt();
        }
        long res = getMinSum(nums,0,nums.length - 1);
        System.out.println(res);

    }
    public static long getMinSum(int[] nums, int left, int right) {
        if (left == right) {
            return 0;
        }
        int mid = left + ((right - left) >> 1);
        return getMinSum(nums, left, mid) + getMinSum(nums, mid + 1,
                right) + mergeMinSum(nums, left, mid, right);
    }

    private static long mergeMinSum(int[] nums, int left, int mid, int right) {
        int pl = left;
        int pr = mid + 1;
        long ans = 0;
        long tempSum = 0;

        //下面是进行统计的过程
        //下面这个循环的时间复杂度其实是O(N),因为我们的左指针是不发生回退的
        while (pr <= right) {
            while (pl <= mid && nums[pl] <= nums[pr]) {
                tempSum += nums[pl++];
            }
            ans += tempSum;
            pr++;
        }

        //下面我们进行排序的过程
        pl = left;
        pr = mid + 1;
        int index = 0;
        int[] temp = new int[right - left + 1];
        while (pl <= mid && pr <= right) {
            temp[index++] = nums[pl] <= nums[pr] ? nums[pl++] : nums[pr++];
        }
        while (pl <= mid) {
            temp[index++] = nums[pl++];
        }
        while (pr <= right) {
            temp[index++] = nums[pr++];
        }
        for (int i = 0; i < temp.length; ++i) {
            nums[left + i] = temp[i];
        }
        return ans;
    }

}

3 . 逆序数对问题

在这里插入图片描述
归并分治的分析
总交易逆序对数目 = 左边数目 + 右边数目 + 左跨右的数目
然后如果在每次统计完进行归并排序之后,我们又会加速这过程
其实就是归并排序(滑动窗口)
代码实现如下

class Solution {
    public int reversePairs(int[] record) {
        if (record == null || record.length <= 1) {
            return 0;
        }

        return process(record, 0, record.length - 1);
    }

    // 下面就是归并的思路了
    private int process(int[] nums, int left, int right) {
        // 递归终止条件
        if (left == right) {
            return 0;
        }

        int mid = left + ((right - left) >> 1);
        return process(nums, left, mid) + process(nums, mid + 1, right) + mergeOfrevers(nums, left, mid, right);
    }

    private int mergeOfrevers(int[] nums, int left, int mid, int right) {
        // 先统计,后进行排序
        int pl = left;
        int pr = mid + 1;
        int ans = 0;
        while (pl <= mid) {
            while (pr <= right && nums[pl] > nums[pr]) {
                pr++;
            }
            ans += pr - mid - 1;
            pl++;
        }

        // 下面就没什么可说的了,就是简单的归并排序的过程
        pl = left;
        pr = mid + 1;
        int index = 0;
        int[] temp = new int[right - left + 1];
        while (pl <= mid && pr <= right) {
            temp[index++] = nums[pl] <= nums[pr] ? nums[pl++] : nums[pr++];
        }
        while (pl <= mid) {
            temp[index++] = nums[pl++];
        }
        while (pr <= right) {
            temp[index++] = nums[pr++];
        }
        for (int i = 0; i < temp.length; ++i) {
            nums[left + i] = temp[i];
        }
        return ans;
    }
}

4 . 翻转对数量的计算

在这里插入图片描述
这道题也没什么可以说的点, 跟上一道题的思路, 完完全全一致
直接上代码实现 ,改动的时候, 记得把 nums[pl] <= nums[pr]* 2
换成 nums[pl] / 2.0 <= nums[pr]
因为要防止数字越界

class Solution {
    public int reversePairs(int[] record) {
        if (record == null || record.length <= 1) {
            return 0;
        }

        return process(record, 0, record.length - 1);
    }

    // 下面就是归并分治的思路了
    private int process(int[] nums, int left, int right) {
        // 递归终止条件
        if (left == right) {
            return 0;
        }

        int mid = left + ((right - left) >> 1);
        return process(nums, left, mid) + process(nums, mid + 1, right) + mergeOfrevers(nums, left, mid, right);
    }

    private int mergeOfrevers(int[] nums, int left, int mid, int right) {
        // 先统计,后进行排序
        int pl = left;
        int pr = mid + 1;
        int ans = 0;
        while (pl <= mid) {
            while (pr <= right && nums[pl]  / 2.0 >  (double)nums[pr]) {
                pr++;
            }
            ans += pr - mid - 1;
            pl++;
        }

        // 下面就没什么可说的了,就是简单的归并排序的过程
        pl = left;
        pr = mid + 1;
        int index = 0;
        int[] temp = new int[right - left + 1];
        while (pl <= mid && pr <= right) {
            temp[index++] = nums[pl] <= nums[pr] ? nums[pl++] : nums[pr++];
        }
        while (pl <= mid) {
            temp[index++] = nums[pl++];
        }
        while (pr <= right) {
            temp[index++] = nums[pr++];
        }
        for (int i = 0; i < temp.length; ++i) {
            nums[left + i] = temp[i];
        }
        return ans;
    }
}

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

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

相关文章

【Linux】Linux基本指令2

我们接着上一篇&#xff1a;http://t.csdnimg.cn/bSJx8 我们接着完善ls指令 我们可以直接匹配对应格式的文件匹配出来 1.man指令&#xff08;重要&#xff09;&#xff1a; Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助…

【贪心算法】C++解决回文串、增减字符串匹配、分发饼干、跳跃游戏、加油站问题

1. 前言 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在每一步选择中都采取当前状态下最优决策的算法。贪心算法通常用来解决最优化问题&#xff0c;其核心思想是通过局部最优解逐步推导出全局最优解。 在贪心算法中&#xff0c;我们并不总是考虑到未来可能发生的…

【PingPong_注册安全分析报告】

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

如何下载b站(哔哩哔哩bilibili)的学习视频教程

方法1&#xff1a; 打开粘贴视频链接下载即可哔哩哔哩(bilibili)视频解析下载 - 保存B站视频到手机、电脑哔哩哔哩高清视频解析下载工具是一个免费的B站视频在线解析提取工具,支持提取B站APP和bilibili网站上的任何视频,提取出来的视频无水印.我们可以借助此下载器方便地将视频…

使用jdk自带jhat工具排查OOM问题

使用jdk自带jhat工具排查OOM问题 OOM java.lang.OutOfMemoryError: Java heap space排查步骤 编写一个测试类 public class TestJVM {Testpublic void test1() throws InstantiationException, IllegalAccessException {List<A> list new ArrayList<>();for (i…

ArcGIS不同图斑设置不同的透明度

对于设置一个图层的整体的透明度&#xff0c;我们在 ArcGIS制作带蒙版的遥感影像地图http://mp.weixin.qq.com/s?__bizMzIzNjM2NTYxMg&mid2247509080&idx1&sn38dccf0a52bb3bb3758f57114ee38b72&chksme8da161bdfad9f0d363da90959a8524dcf2b60d0e8d999f8ebeef0…

OrangePi Alpro开箱体验 ubuntu 与 openEuler 实时性对比

OrangePi Alpro开箱体验 & ubuntu 与 openEuler 实时性对比 1 介绍1.1 概述1.2 OrangePi Kunpeng Pro vs OrangePi AIpro 2 开箱3 芯片介绍OrangePi AIpro(8T)Atlas 200I DK A2 4 开机连接鼠标、键盘、显示器桌面查看系统信息配置网络查看IP远程SSHWinSCP 5 GPIO Toolgpio_…

MATLAB分类与判别模型算法:基于LVQ神经网络的乳腺肿瘤诊断分类程序【含Matlab源码 MX_003期】

说明 实现基于LVQ&#xff08;Learning Vector Quantization&#xff0c;学习向量量化&#xff09;神经网络的乳腺肿瘤诊断分类任务。LVQ是一种监督学习算法&#xff0c;通常用于模式识别和分类任务。 算法思路介绍&#xff1a; 导入数据&#xff1a; 加载名为"data.mat&…

C++学习~~对于二进制文件的读写命名空间再认识异常处理

目录 1.将数据以二进制形式放到磁盘 2.将上述的数据读入内存并且显示在显示器上面 3.异常处理机制 4.抛出异常的应用实例 1.将数据以二进制形式放到磁盘 &#xff08;1&#xff09;使用student定义结构体数组stud,并对其进行初始化&#xff0c;创建输出文件流对象outfile,这…

朋友圈定时发送设置

人日常中不可缺少的一件事&#xff0c;同时也是企业用来触达客户的重要渠道&#xff0c;下面一起来了解下微信朋友圈怎么定时发送呢&#xff1f;

音乐传奇告别之作:《杰作》未解之谜❗❗

坂本龙一的《杰作》不仅是一部音乐会纪录电影&#xff0c;更是他赠予世界的一封深情告别信。 这部影片精心收录了这位音乐巨匠生前最后一场钢琴独奏音乐会的珍贵瞬间&#xff0c; 其中涵盖了《圣诞快乐&#xff0c;劳伦斯先生》、《末代皇帝》、《水》等二十首令人陶醉的经典…

设计模式20——职责链模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 职责链模式&#xff08;Chain …

手推车式电缆故障定位系统

武汉凯迪正大一体化电缆故障高压发生器用于测试各种型号的380V,600V,10kV,35kV,110kV,220kV,380kV电压等级的铜铝芯电力电缆、同轴通信电缆和市话电缆的各类故障&#xff0c;如电缆全长、开路、短路、断线、低阻故障、高阻故障、高阻泄露、高低阻抗接地、接地故障、铠装接地故障…

数据集——高分辨率图像分割成大小均匀图像(附Python代码)

将高分辨率图像分割成大小均匀的图像用于训练&#xff0c;可以提高训练效率&#xff0c;提高模型性能&#xff0c;并提供更大的灵活性。 目录 一、大小均匀图像用于训练优势二、代码2.1 代码参数修改2.2 代码2.3 输出2.4 分割结果 三、总结 一、大小均匀图像用于训练优势 内存…

3步操作助您轻松实现苹果手机照片一键传输至电脑

对于很多使用苹果手机的用户来说&#xff0c;随着手机中照片和视频数量的不断积累&#xff0c;如何将这些珍贵的回忆从手机转移到电脑&#xff0c;以便更好地保存、整理和分享&#xff0c;成为了一个值得关注的问题。那么&#xff0c;苹果手机怎么把照片导入电脑呢&#xff1f;…

职校老师的工资待遇怎么样

工资水平一直是教师们关注的焦点&#xff0c;毕竟&#xff0c;工资不仅关系到个人的生活品质&#xff0c;还影响着教师的职业满意度和工作动力。职校教师的工资待遇究竟是怎样的呢&#xff1f; 职校教师的工资水平受多种因素影响&#xff0c;包括地区、学校类型、个人资历和教学…

Qt 配置Eigen矩阵库 - 并简单测试

Qt 配置Eigen矩阵库 - 并简单测试 引言一、在Qt中配置Eigen二、低通Demo源码三、参考链接以及其他 引言 Eigen是一个开源的C模板库&#xff0c;提供了线性代数和矩阵运算的功能。它被设计为一个高性能、可扩展和易用的库&#xff0c;可以用于科学计算、机器学习和计算机图形学等…

Django 里如何使用 sqlite (操作步骤)

在 settings.py 里&#xff0c;已经设定好 sqlite 了 DATABASES {default: {ENGINE: django.db.backends.sqlite3,NAME: BASE_DIR / db.sqlite3,} }必须得设置好app # 在 settings.py 里INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contentt…

安卓开发板_开发评估套件_4G/5G联发科MTK安卓主板定制开发

安卓开发板采用了联发科八核A53 CPU&#xff0c;主频2.0GHz&#xff0c;采用12nm制程工艺&#xff0c;拥有强大的通用计算性能。配备GE8300 GPU&#xff0c;支持1080P视频编码和H.264硬解码&#xff0c;能够解析目前流行的视频和图片格式&#xff0c;非常适合各种功能APP的测试…

16:00面试,16:08就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…