【数据结构详解】——归并排序(动图详解)

归并排序——动图+逐步讲解(C语言) 归并排序(Merge-Sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有 序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 效果演示:先来看看流程图: 3.2 处理越界 修正边界法: 多步拷贝法:...... 阅读详情
AI摘要

本文详细介绍了归并排序的概念、算法思想、递归与非递归实现方式,通过动图演示,直观展示排序过程。归并排序是一种高效的排序算法,基于分治策略,将大问题分解为小问题解决,最终合并结果。文中还提供了代码实现,帮助读者理解排序细节。

🕒 1. 归并排序

💡 算法思想:归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。即先使每个子序列有序,再使子序列段间有序,若将两个有序表合并成一个有序表,称为二路归并。

请添加图片描述

简单来说,快速排序类似于二叉树的前序遍历:首先选择一个基准值(key),然后不断划分区间,对区间内的元素进行排序,直到整个序列有序。这个过程中,一棵二叉树也随之构建完成。

相对地,归并排序则类似于二叉树的后序遍历:它会持续划分区间,直到区间内元素有序,然后利用额外空间对元素进行排序,并将它们合并回原区间,直至整个序列有序。这个过程中,划分区间相当于达到树的最底层,而归并排序则相当于从树的底层开始向上遍历。

🕘 1.1 递归实现

💡 算法思想:首先,编译器并不知道数组中是否存在有序的序列。因此,可以将数组进行划分,一分为二,二分为四…直至每个序列只剩下一个数字。毕竟,单个数字可以被视为已经排序好的。最终,将这些被划分的序列合并起来。

具体思路是:

  1. 确定递归的结束条件,求出中间数mid;
  2. 进行分解,根据mid来确定递归的区间大小;
  3. 递归分解完左边,然后递归分解右边;
  4. 左右分解完成后,进行合并;
  5. 申请新数组进行合并,比较两个数组段,记得查漏补缺;
  6. 合并的时候要对齐下标,每个tmp的下标要找到array中对应的下标。
void _MergeSort(int* a, int left, int right, int* tmp)
{
    // 区间中没有元素时不再合并
    if (left >= right)
    {
        return;
    }
 
    // 划分数组,每次一分为二
    int mid = (left + right) / 2;
    _MergeSort(a, left, mid, tmp);   // 划分左区间
    _MergeSort(a, mid + 1, right, tmp); // 划分右区间
 
    // 合并有序序列
    int begin1 = left, end1 = mid;      // 有序序列1
    int begin2 = mid + 1, end2 = right; // 有序序列2
    int i = left;				// 辅助数组的起始位置

    // 注意结束条件为一个序列为空时就停止
    while (begin1 <= end1 && begin2 <= end2)
    {
        if (a[begin1] < a[begin2])
        {
            tmp[i++] = a[begin1++];	// 将较小的元素放入辅助数组中
        }
        else
        {
            tmp[i++] = a[begin2++];	// 将较小的元素放入辅助数组中
        }
    }

    // 两序列不可能同时为空,将剩余元素合并到辅助数组中
    while (begin1 <= end1)
    {
        tmp[i++] = a[begin1++];
    }

    while (begin2 <= end2)
    {
        tmp[i++] = a[begin2++];
    }

    // 将辅助数组中排好序的部分拷贝回原数组中
    memcpy(a + left, tmp + left, (right - left + 1) * sizeof(int));
}

// 归并排序递归实现
void MergeSort(int* a, int n)
{
    assert(a);

    // 为归并过程分配一个临时数组,用于存储中间结果
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL)
    {
        perror("malloc fail!");
        exit(-1);
    }

    // 递归调用归并排序
    _MergeSort(a, 0, n - 1, tmp);

    // 释放临时数组
    free(tmp);
    tmp = NULL;
}

🕘 1.2 非递归实现

💡 算法思想:非递归实现与递归实现的思想相似。区别在于,非递归是从单个元素的组开始,逐步扩大为2个元素、4个元素的组(二倍数扩大组数),即序列划分过程和递归是相反的。如此继续,直至完成所有元素的归并。

在这里插入图片描述

void MergeSortNonR(int* a, int n)
{
    assert(a);

    // 为归并过程分配一个临时数组,用于存储中间结果
    int* tmp = (int*)malloc(sizeof(int) * n);
    if (tmp == NULL)
    {
        perror("malloc fail!"); 
        exit(-1);
    }

    // 初始化每组的元素个数为1
    int gap = 1;

    // 当gap小于数组长度时继续归并
    while (gap < n)
    {
        // 记录tmp数组中的元素下标
        int index = 0;

        // 遍历数组,将每两个gap长度的子数组进行归并
        for (int i = 0; i < n; i += 2 * gap)
        {
            // 归并 取小的尾插
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
            
            // 当原数组中元素个数不是2^n时,最后两组会出现元素不匹配的情况
            // 情况1:第一组越界或第二组全部越界
            if (end1 >= n || begin2 >= n)
            {
                break;
            }
            
            // 情况2:第二组部分越界
            if (end2 >= n)
            {
                end2 = n - 1;	// 修正一下end2,继续归并
            }

            // 归并两个子数组到tmp数组中
            while (begin1 <= end1 && begin2 <= end2)
            {
                if (a[begin1] < a[begin2])
                {
                    tmp[index++] = a[begin1++];
                }
                else
                {
                    tmp[index++] = a[begin2++];
                }
            }

            // 如果第一个子数组还有剩余元素,直接复制到tmp中
            while (begin1 <= end1)
            {
                tmp[index++] = a[begin1++];
            }

            // 如果第二个子数组还有剩余元素,直接复制到tmp中
            while (begin2 <= end2)
            {
                tmp[index++] = a[begin2++];
            }
            
            // 将辅助数组中排好序的部分拷贝回原数组中
    		memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));
        }

        // 每次循环后将每组元素的个数翻倍
        gap *= 2;
    }

    // 释放临时数组的内存
    free(tmp);
    tmp = NULL;
}

归并排序的特性总结:

  1. 归并的缺点在于需要 O(N) 的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(NlogN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定

❗ 转载请注明出处
作者:HinsCoder
博客链接:🔎 作者博客主页

归并排序算法 时间复杂度为O(nlogn) 简介 归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 作为一种典型的分而治之思想的算法应用,归并排序的实现由两种方法: ... 阅读详情

相关推荐

C++排序算法:归并排序详解

C++排序算法:归并排序详解

小天狼星_布莱克 的博客 3万+

排序之归并排序

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。所谓归并,就是将两组有序的数据合成为一组有序的数据,例如有如下两个有序数组,将两个有序数组归并为一个有序数组,这就是一次归并操作。

dong132697的博客 2万+

Kali Linux实战:Aircrack-ng破解Wi-Fi密码的完整流程与常见问题解决

本文详细介绍了在Kali Linux中使用Aircrack-ng套件进行Wi-Fi密码破解的完整流程,包括环境准备、无线网卡配置、网络扫描、握手包捕获与字典破解等关键步骤。同时针对无线网卡不支持监听模式、无法捕获握手包等常见问题提供了实用解决方案,帮助安全研究人员有效评估无线网络安全性。

weixin_29323049的博客 296

归并排序(看了就会)

归并排序,是创建在归并操作上的一种有效的排序算法。上图中首先把一个未排序的序列从中间分割成2部分,再把2部分分成4部分,依次分割下去,直到分割成一个一个的数据,再把这些数据两两归并到一起,使之有序,不停的归并,最后成为一个排好序的序列。不管元素在什么情况下都要做这些步骤,所以花销的时间是不变的,所以该算法的最优时间复杂度和最差时间复杂度及平均时间复杂度都是一样的为:O( nlogn )归并排序算法中,归并最后到底都是相邻元素之间的比较交换,并不会发生相同元素的相对位置发生变化,故是稳定性算法。

Circ 2万+

Cadence Allegro新手避坑:从原理图到PCB,解决‘netrev.lst does not exist’的完整流程

本文详细介绍了Cadence Allegro从原理图设计到PCB导入网表的完整流程,特别针对新手常见的'netrev.lst does not exist'错误提供了系统解决方案。内容包括环境准备、原理图设计规范、网表生成与导入技巧,以及设计数据管理最佳实践,帮助工程师高效完成PCB设计任务。

weixin_42685438的博客 487

微信小程序:errno: 1500104, errCode: 10008, errMsg: “writeBLECharacteristicValue:fail:system error, status

微信小程序errno: 1500104, errCode: 10008, errMsg: "writeBLECharacteristicValue:fail:system error, status: UNKNOWN (239)"微信小程序调用蓝牙API,wx.writeBLECharacteristicValue()报错,微信小程序蓝牙写入错误,在开发uniapp微信小程序蓝牙通信功能时报错,蓝牙开发设备控制核心api,微信小程序调用蓝牙API “wx.writeBLECharacteristicValu

✅ 精选推荐文章 3929

排序算法-归并排序详细讲解(MergeSort)

归并,指合并,合在一起。归并排序(Merge Sort)是建立在归并操作上的一种排序算法。其主要思想是分而治之。若将两个有序集合并成一个有序表,称为2-路归并,与之对应的还有多路归并。借助一个辅助空数组,把左右两边的数组按照大小比较,按顺序放入辅助数组中即可。以下面两个有序数组为例: public static void mergeSort(int[] arr){ if (arr == null || arr.length<2){ return;

信竞星球_少儿编程题库 1万+

华为 MetaERP 集成盘古大模型、天筹 AI 求解器等生成式 AI 相关技术能力,结合华为云数智融合平台的支撑,在供应链预测中围绕需求预测、物料匹配

华为 MetaERP 集成盘古大模型、天筹 AI 求解器等生成式 AI 相关技术能力,结合华为云数智融合平台的支撑,在供应链预测中围绕需求预测、物料匹配、风险预警等核心场景开展实践,大幅提升了供应链预测的精准度与效率,以下是具体实践内容及成效: 动态需求预测,降低库存偏差华为供应链涉及数万种采购物料,物料清单层级高,且市场需求波动大。MetaERP 集成华为云 ModelArts 等工具构建专属动态需求预测模型,融合深度学习算法,纳入市场趋势、历史销售、产能数据等多维度信息进行综合预测。在华为自身供应链应

金牌架构师 700

数据结构排序算法详解(4)——归并排序(附动图)

本文围绕归并排序展开讲解,开篇介绍其核心思想与步骤(搭配动图辅助理解)。随后阐述递归实现归并排序的代码及核心内容分析;重点讲解非递归版本,说明其实现思路,解析关键的gap迭代与分组逻辑,以及“分组→合并→覆盖”的执行闭环,并附代码实现。 文章还说明归并排序的时间复杂度,阐述其特性,将其与四大排序做对比,最后提供总代码。全文清晰呈现归并排序的两种实现方式、核心细节与排序对比,助力读者全面掌握归并排序。

梦是现实的延续,现实是梦的结束。 1480

向华为学习——解读158页 华为敏捷园区网技术解决方案【附全文阅读】

华为敏捷园区网解决方案通过策略随行和体验随身技术,满足移动办公场景下的网络需求。该方案采用敏捷交换机和防火墙自动执行策略配置,实现用户身份认证与业务策略的无缝衔接,且策略配置与IP地址解耦,简化了管理流程。相比传统技术,该方案有效解决了用户分级、QoS保障、无线接入等关键问题,确保用户在移动过程中获得一致的网络体验。

cdfunlove的博客 379

提示词工程中的上下文隔离

已经进入context的prompt是没有办法让大模型真正的忘记的,但是可以通过prompt技巧强约束模型的后续推理中“不再使用、不再提及、不再引用”某部分提示词,这其实是一种行为抑制(behavior suppression),并不是删除。

Criss@陈磊 234

【C语言】-- 数据结构 -- 归并排序类(归并排序)(超详解+动图+源码)

包括时间复杂度,空间复杂度,稳定性(带图讲解)

weixin_64609308的博客 2365

【智能车Code review】——拐点的寻找

CROSSROAD:十字拐点 ROUNDISLAND:环岛拐点 rightfindflag[i]:表示第i行的右线是否被找到,1为找到,0为没有找到 flag_find_huan_rightdown_point :找到环岛右下拐点标志 flag_find_huan_rightmiddle_point:找到环岛右中拐点标志 找拐点的思路:观察一段数据中数据点的走向趋势,并且保证这段数据是准确的。在摄像头畸变比较严重的情况下这种方法不再适用 关于函数的调用请见:https://blog.youkuaiyun.com/qq.

hanhan的博客 1万+

查找与排序之归并排序

归并排序 思路: package com.gh.check01; public class _04_排序之归并排序 { private static int[] helper; public static void main(String[] args) { int[] arr = {2,5,3,1,6,4,7,4,0}; sort(arr); for(int i=0; i...

weixin_43945486的博客 215

如何使用CPU-Z获取内存条频率信息(编程)

当我们需要获取计算机内存条的频率信息时,可以利用CPU-Z这个工具来实现。CPU-Z是一款专门用于获取计算机硬件信息的工具,它提供了API接口,我们可以通过编程的方式来获取内存条频率信息。你可以从CPU-Z官方网站下载适用于你的操作系统的CPU-Z程序,并从安装目录中找到。函数,我们可以初始化CPU-Z,如果返回值不为0,则表示初始化失败。函数来获取内存条频率信息,如果返回值小于等于0,则表示获取失败。函数来初始化CPU-Z并获取内存条频率信息。最后,我们将获取到的内存条频率信息打印输出,并使用。

ByteProwl的博客 457

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

动态图文超详解十大排序算法

超级详细动图详解十大排序算法 https://www.jianshu.com/p/47170b1ced23

bad_boy的博客 232

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

【数据结构】排序算法---归并排序(动图演示)

归并排序算法详细讲解,还含有动图演示

2301_80191662的博客 2942

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

【数据结构】常见八大排序算法(附动图)

本文围绕八种常见排序算法展开讲解,包含算法思想、动图、源代码和对算法的优化等

Eristic0618的博客 9992

归并排序,动图演示

归并排序 #include&lt;stdio.h&gt; #include&lt;algorithm&gt; using namespace std; #define BL for(int i=0;i&lt;n;i++) int n; int a[10000], t[10000]; void Bsort(int a[], int L, int mid ,int R, int t[]) { ...

qq_40472328的博客 4097

【数据结构】八大排序(超详解+附动图+源码)

前言 所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 生活中各种地方都需要用到排序,所以学好排序算法是非常重要的。 排序分为 内部排序 和 外部排序。 内部排序:数据元素全部放在内存中的排序。外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。 这部分主要是内部排序。排序讲解都以升序为例。 常见的排序算法: // 排序实现的接口 // 插入排序 void InsertSort(int* arr..

Edward_Asia的博客 13万+

数据结构-八大排序详解(动图+实现详解+总结)

数据结构-八大排序详解(动图+实现详解+总结)

吴名氏的博客 4万+

【八大排序】归并排序 | 计数排序 + 图文详解!!

归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。O(N*logN)O(N)稳定计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。数排序适用于整数且范围较小的情况。对于范围较大的整数或小数,需要更复杂的排序算法。O(MAX(N,范围)),由于算法只涉及到一次遍历输入数组和一次遍历计数数组,所以时间复杂度为O(MAX(N,范围))。O(范围),由于需要创建一个与范围大小相等的计数数组,所以空间复杂度为O(范围)。

深入解析C/C++编程技巧,带你探索高效开发之路 1925

【数据结构】经典八大排序(算法+动图+代码详解)

前言 我们在学习数据结构或者算法的过程中不可避免地需要用到排序, 而在数据结构中主要介绍了,八种常见排序,本次就让我们一起深入学习下吧

m0_54385790的博客 2264

【数据结构与算法之美】十大排序思路及动图演示 代码实现

1.冒泡排序 1.1 实现思路 冒泡排序思想是通过将两个互相相邻的元素进行比较,如果前一个元素大于后一个元素,进行交换位置。否则不交换。外层循环控制层数,内存循环控制个数。每进行一轮都会将最大数字移动到最后,所以经过多次交换就可以实现将数组中的元素进行有序排列了。 note:我们在内存循环中加入一个判断标志 如果当前内存循环没有进行交换数据的操作 说明这个数组的元素已经是有序的 就可以退出循环操...

芝兰生于深谷,不以无人而不芳 527

java归并算法实例_Java实现-归并排序算法-动图详解

归并排序详解(后序遍历)大家可能都对二叉树的后序遍历比较熟悉,实际上归并排序本质框架就是二叉树的后序遍历,根结点的遍历只不过换成了治(Merge方法的调用),本文将结合动图+代码的方式进行最通俗的讲解。「基本思想」:利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则...

weixin_35836405的博客 182

经典四大排序(动图实现)

这个大佬整理的很仔细: https://blog.youkuaiyun.com/m0_37741420/article/details/106981276?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162777450716780262565601%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162777450716780262565601

Myblog 2万+
上一篇: 【数据结构详解】——快速排序(动图详解)
下一篇: 【数据结构详解】——计数排序(动图详解)
HinsCoder
博客等级 码龄5年 1万+粉丝 220原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值