归并排序(Merge Sort)

news2024/9/28 3:21:44

什么是归并排序

        归并排序(Merge Sort)是一种经典的排序算法,它采用分治法(Divide and Conquer)策略,将一个大数组分为两个小数组,分别进行排序,然后将这两个已排序的小数组合并成一个有序的大数组。归并排序在最坏情况下的时间复杂度为 O(nlog⁡n)O(nlogn),并且是一种稳定的排序算法。

核心思想

归并排序的核心思想可以分为以下几个步骤:

  1. 分解(Divide):将待排序的数组分为两个子数组,直到每个子数组只包含一个元素。因为一个元素的数组是有序的。
  2. 解决(Conquer):递归地对这两个子数组进行归并排序。
  3. 合并(Merge):将两个已排序的子数组合并成一个有序的数组。

快速排序与归并排序的比较

        快速排序和归并排序都是经典的排序算法,均采用分治法(Divide and Conquer)的思想。尽管它们在基本原理上相似,但在实现细节、性能特征及适用场景上存在显著差异。

1. 基本思路

  • 快速排序
    • 选择一个基准元素(Pivot),通常是数组的第一个元素或随机选择。
    • 将数组分为两个部分:左侧部分包含小于基准的元素,右侧部分包含大于基准的元素。
    • 递归地对这两个部分进行快速排序,直到整个数组有序。
  • 归并排序
    • 将数组递归分为两个子数组,直到每个子数组只包含一个元素。
    • 然后将这两个已排序的子数组合并成一个有序的数组。
    • 合并过程是关键,它需要额外的空间来存放临时数组。

2. 稳定性

  • 快速排序:不稳定排序。在排序过程中,相等元素的相对顺序可能会被改变。
  • 归并排序:稳定排序。相等元素的相对顺序在排序后保持不变,这在某些应用中非常重要。

 示例一

题目描述

代码实现

import java.util.Scanner;

public class Main {
    final static int N = 1000000;
    private static int[] temp = new int[N];
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int arr[] = new int[n];

        for (int i = 0; i < n; i++) {
            arr[i] = scanner.nextInt();
        }
        mergeSort(arr, 0, n - 1);
        for (int i = 0; i < n; i++) {
            System.out.print(arr[i] + " ");
        }
    }
    public static void mergeSort(int[] arr, int l, int r) {
        if (l >= r) return;
        int mid = (l + r) >> 1;

        mergeSort(arr, l, mid);
        mergeSort(arr, mid + 1, r);

        int k = 0, i = l, j = mid + 1;
        while (i <= mid && j <= r) {
            if (arr[i] <= arr[j])
                temp[k++] = arr[i++];
            else
                temp[k++] = arr[j++];
        }
        //将剩余元素复制到temp数组中
        while (i <= mid) temp[k++] = arr[i++];
        while (j <= r) temp[k++] = arr[j++];

        //将temp数组中的元素复制回arr数组
        for (int t = l; t <= r; t++)
            arr[t] = temp[t - l];
    }
}

示例二

题目描述

求逆序对的数量

代码实现

import java.util.Scanner;

public class Main {
    final static int N = 1000000;
    private static int[] temp = new int[N];
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int arr[] = new int[n];

        for (int i = 0; i < n; i++) {
            arr[i] = scanner.nextInt();
        }
        scanner.close();
        System.out.println(mergeSort(arr, 0, n - 1));

    }
    public static long mergeSort(int[] arr, int l, int r) {
        if (l >= r) return 0;
        int mid = (l + r) >> 1;

        long res =  mergeSort(arr, l, mid)+mergeSort(arr, mid + 1, r);

        int k = 0, i = l, j = mid + 1;
        while (i <= mid && j <= r) {
            if (arr[i] <= arr[j])
                temp[k++] = arr[i++];
            else{
                res += (mid - i + 1);
                temp[k++] = arr[j++];
            }

        }
        while (i <= mid)
            temp[k++] = arr[i++];
        while (j <= r)
            temp[k++] = arr[j++];
        for(i=l,j=0;i<=r;i++,j++)
            arr[i]=temp[j];
        return res;
    }
}

逆序对数量算法思路与实现

        求逆序对的算法是利用了归并排序的思想,在归并排序的过程中会将序列分为两部分,此时逆序对可以分为三种情况:两个数都在左边的(设为s1)。两个数都在右边的(s2),一个数在左边一个数在右边的(s3)。现在假设我们在归并排序的时候写的函数mergeSort(int[] arr, int l, int r)可以返回l到r区间中逆序对数量。那么s1=mergeSort(a, l, mid),s2=mergeSort(a, mid + 1, r);,s3很显然没有直观的答案。

        那么核心问题就在于怎么求s3,以及怎么使我们的mergeSort(int[] arr, int l, int r)可以返回l到r区间中逆序对数量。

        

        在归并排序中,左右两边都是排序好的数列。因此,对于右图中的B我们只需要找到左边第一个大于B的数A,那么A后面的数都是大于B的。假设A的下标为i,不难得到我们要统计的数目为:mid-i+1,else中的语句即代表左边数列中的数大于右边中的数了,我们可以在此时将mid-i+1加到总答案中去。那么s3的问题就解决了。经过递归之后,res 即我们所求。

时间复杂度

  • 最佳情况:O(nlog⁡n)O(nlogn)
  • 平均情况:O(nlog⁡n)O(nlogn)
  • 最坏情况:O(nlog⁡n)O(nlogn)

        归并排序在所有情况下的时间复杂度都是 O(nlog⁡n)O(nlogn),这使得它在处理大规模数据时非常高效。

空间复杂度 

归并排序的空间复杂度为 O(n)O(n),因为在合并过程中需要额外的存储空间来存放临时数组。

优点

  • 稳定排序,能够保持相等元素的相对顺序。
  • 在处理大规模数据时,性能稳定。

缺点

  • 需要额外的存储空间,不适合内存受限的环境。

应用场景

        归并排序广泛用于需要稳定排序的场合,如外部排序(处理无法完全放入内存的数据集)和链表排序。由于其稳定性和性能,归并排序在许多编程语言的标准库中被实现为排序算法。

总结

        归并排序是一种高效且稳定的排序算法,适用于大规模数据的排序。通过分治法的思想,归并排序能够在最坏情况下保持 O(nlog⁡n)O(nlogn) 的时间复杂度。虽然它需要额外的空间,但在许多实际应用中,归并排序仍然是一种非常有用的算法。

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

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

相关文章

Cortex-M3架构学习:异常

异常类型 Cortex-M3 在内核水平上搭载了一个异常响应系统&#xff0c;支持为数众多的系统异常和外部中断。其 中&#xff0c;编号为 1&#xff0d;15 的对应系统异常&#xff0c;大于等于 16 的则全是外部中断。 Cortex-M3支持的中断源数目为 240 个&#xff0c;做成芯片后&…

docker进入容器运行命令详细讲解

​ 大家好&#xff0c;我是程序员小羊&#xff01; 前言&#xff1a; 在 Docker 中&#xff0c;进入容器并运行命令是常见的操作&#xff0c;尤其是当你想要调试、检查日志或手动运行某些程序时。Docker 提供了几种方式来进入容器和执行命令。 前提条件 确保你的 Docker 容器…

C++基础面试题 | 什么是C++中的虚继承?

文章目录 回答重点菱形继承问题虚继承解决菱形继承问题虚继承的二义性解决 虚继承总结拓展知识&#xff1a;virtual关键字的用法1. 虚函数 (Virtual Function)2. 纯虚函数 (Pure Virtual Function)3. 虚析构函数 (Virtual Destructor)4. 虚继承 (Virtual Inheritance)5. 虚函数…

一篇文章带你入门机器学习 Part1 -->Machine Learning from Scratch

学习网站&#xff1a;Machine Learning from Scratch Machine Learning from Scratch (Part1神经网络&#xff09; 神经网络——Neural Networks神经网络是如何工作的&#xff1f;训练神经网络 神经网络——Neural Networks 在人工神经网络的背景下&#xff1a;一个神经元是一…

046全排列

题意 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 提示&#xff1a; 1 < nums.length < 6 -10 < nums[i] < 10 nums 中的所有整数 互不相同 难度 中等 示例 示例 1&#xff1a; 输入&#xff1…

uniapp+若依 开发租房小程序源码分享

1、使用Uniapp开发的前台&#xff0c;基于 Vue.js 开发所有前端应用的框架&#xff0c;开发者编写一套代码&#xff0c;可发布到iOS、Android、Web&#xff08;响应式&#xff09;、以及各种小程序 2、基于SpringBoot的权限管理系统&#xff0c;易读易懂、界面简洁美观。 核心…

WordBN字远笔记!更新1.2.2版本|Markdown编辑器新增高亮功能,界面新增深色模式

WordBN字远笔记1.2.2版本更新描述 WordBN字远笔记在1.2.2版本中进行了多项重要的更新与改进&#xff0c;旨在提升用户的编辑体验和视觉舒适度。 以下是本次更新的两大亮点&#xff1a;Markdown编辑器新增高亮功能以及界面新增深色模式。 1. Markdown编辑器新增高亮功能 在1…

零倾覆力矩点(ZMP)

系列文章目录 前言 在机器人学中&#xff0c;零倾力矩点&#xff08;ZMP&#xff09;是一个特征点&#xff0c;主要用于足式运动。在下文的一些假设中&#xff0c;我们将看到&#xff0c;它非正式地代表了一个系统接触反作用力的结果点。例如&#xff0c;下图中的刚体处于静态平…

leetcode:布尔运算(动态规划版)

最近又要考试&#xff0c;勉励自己复习一些之前学过的&#xff01;&#xff01;&#xff01; 开始使用的是DFS&#xff0c;遍历所有可能的情况&#xff0c;发现超时&#xff01; 下面的是动态规划的一个模板&#xff0c;dp[i][j][result]表示从s的第i个元素到第个元素&#xf…

Auracast认证:蓝牙广播音频的革新之旅

低功耗音频&#xff08;LE Audio&#xff09;技术的突破&#xff0c;为蓝牙世界带来了前所未有的广播音频功能。Auracast™&#xff0c;作为蓝牙技术联盟精心打造的音频广播解决方案&#xff0c;正引领着一场全新的音频分享革命。它不仅革新了传统蓝牙技术的局限&#xff0c;更…

HuggingFace Embedding 转为 Ollama Embedding

Ollama 是基于 LlamaCpp 开发的 CPU 上的推理引擎&#xff0c;通过 LlamaCpp 提供的脚本可以将大语言模型装换为 gguf 的二进制跟是文件&#xff0c;从而通过 Ollama 就行推理。Ollama 支持HuggingFace 大多开源模型&#xff0c;例如 Llama、Qwen、Gemma 和 Phi3 等等。 GGUF …

【Leetcode:2848. 与车相交的点 + 模拟计数】

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

软件研制功能点拆分

最近需要进行软件研制概算明细表中的估算对象原始功能点&#xff0c;记录一下学习过程&#xff0c;共有EI(external input 外部输入)、EO(外部输出)、EQ(外部查询)、ILF(internal logic 内部逻辑文件)、EIF(外部接口文件)五个。 功能点计数项分为数据功能&#xff08;逻辑文件&…

【数据仓库】数据仓库常见的数据模型——范式模型

目录 一、范式 1、第一范式 2、第二范式 3、第三范式 4、进一步范式化&#xff1a;BCNF、4NF 和 5NF 简介 &#xff08;1&#xff09;Boyce-Codd 范式&#xff08;BCNF&#xff09; &#xff08;2&#xff09;第四范式&#xff08;4NF&#xff09; &#xff08;5&#x…

光华里社区“电亮生活”行动:智能科技携手志愿温情,老旧小区焕发新生机

在朝阳区建外街道光华里社区&#xff0c;一场关于“电”的革命正悄然改变着居民的生活面貌。面对老旧小区普遍存在的电力设施陈旧、线路老化、电压不稳等老大难问题&#xff0c;社区党委没有坐视不管&#xff0c;而是携手北京中兴物业管理有限公司广联物业管理中心党支部&#…

泽众P-One性能测试平台支持分布式全链路压测

在当今数字化转型加速的时代&#xff0c;高性能、高可用性的系统已成为企业竞争力的核心要素之一。为了确保系统能够在高并发、大数据量的环境下稳定运行&#xff0c;分布式全链路压测成为了不可或缺的一环。P-One凭借其强大的功能&#xff0c;支持分布式全链路压测&#xff0c…

什么是 SMB 服务器以及它如何工作?

在本文中&#xff0c;您将了解 SMB 服务器以及它们如何促进网络文件共享。 我们将介绍它们的基本功能、主要特性以及如何安全地设置它们。无论您是新手还是需要复习&#xff0c;本指南都将帮助您更好地了解 SMB 服务器。 什么是 SMB 服务器&#xff1f; SMB&#xff08;服务器…

day19JS-AJAX数据通信

1. 什么是AJAX 原生生js中有两种通信&#xff0c;一个ajax&#xff0c;还有一个是fetch。 AJAX 并不是编程语言&#xff0c;是一种从网页访问 Web 服务器的技术。AJAX 代表异步 JavaScript 和 XML。 AJAX 使用浏览器内建的 XMLHttpRequest 对象从 web 服务器请求数据&#xff0…

【开放词汇检测】MM-Grounding-DINO论文翻译

摘要 Grounding-DINO 是一种先进的开放式检测模型&#xff0c;能够处理包括开放词汇检测&#xff08;Open-Vocabulary Detection&#xff0c;OVD&#xff09;、短语定位&#xff08;Phrase Grounding&#xff0c;PG&#xff09;和指代表达理解&#xff08;Referring Expressio…

Java多线程——模拟接力赛跑

题目&#xff1a; 多人参加1000米接力跑 每人跑100米&#xff0c;换下个选手 每跑10米显示信息 解题思路&#xff1a; 1.必须要用到多线程的锁&#xff0c;否则就会出现三个选手乱跑的情况&#xff0c;我们需要一个一个跑 2.使用给oneRunner上锁的方式更细的控制资源比直接给…