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

news2024/9/21 2:39:39

目录

  • 🕒 1. 归并排序
    • 🕘 1.1 递归实现
    • 🕘 1.2 非递归实现

🕒 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
博客链接:🔎 作者博客主页

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

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

相关文章

蓝牙资讯|AirPods Pro 2推送最新开发者版固件,支持点头摇头控制Siri

苹果面向开发者推送了 AirPods Pro 2 的最新固件更新&#xff0c;版本号为 7A5266c。这并非一次常规更新是为即将在秋季发布的 iOS 18 系统做准备。 根据官方信息&#xff0c;新固件将为 AirPods Pro 2 引入一系列新功能&#xff0c;包括点头或摇头控制 Siri、增强嘈杂环境下…

Visual Studio Code 使用Git详细教程

再集成之前请确保已经安装Visual Studio Code 和Git&#xff0c;并且可以找到Git文件地址。如果找不到可以在系统环境变量里面看一眼之前的配置-需要找到Git下面的cmd目录&#xff0c;一会配置使用。 打开 Visual Studio Code找到设置根据图片指引 1.找到左下角设置按钮 2.展开…

支付宝沙箱模拟支付的实现

目录 1.登录支付宝开发平台 2.使用沙箱模拟支付 3.后端代码 4.前端代码 现在几乎所有应用或者网站都需要接入支付接口&#xff0c;因为一个产品最终目的还是要赚钱&#xff0c;所以我们今天就使用支付宝提供的模拟支付的接口来实现这个功能。 1.登录支付宝开发平台 支付宝…

deepin V23前瞻丨deepin V23与飞腾腾锐D3000完成适配,持续丰富deepin操作系统硬件生态

查看原文 近日&#xff0c;自主核心芯片研发飞腾公司宣布与deepin V23完成适配&#xff0c;包含飞腾新一代桌面CPU飞腾腾锐D3000、FT-2000/64、桌面芯片飞腾腾锐D2000等多款产品&#xff0c;为用户带来更智能、安全、稳定的使用体验。 飞腾腾锐D3000集成8个飞腾自主研发的新一…

微服务-分布式事务-seata

1. 事务 事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列SQL操作&#xff0c;这些操作作为一个整体一起向系统提交&#xff0c;要么执行&#xff0c;要么都不执行 1.1 ACID事务的特点 原子性&#xff1a;针对同一事务&#xff0c;要么都完成&#xff0c;要么都不完成 一致…

实例讲解Jmeter参数化的四种方式

JMeter的四种参数化方式包括&#xff1a; 1.用户参数&#xff08;UserParameters&#xff09;&#xff1a;适用于参数取值范围很小的时候使用 2.函数助手&#xff08;FunctionHelper&#xff09;&#xff1a;可作为其他参数化方式的补充项&#xff0c;如&#xff1a;随机数生成…

使用RestHighLevelClient进行Elasticsearch Function Score查询

简介 Function Score查询在Elasticsearch中是一个强大的工具&#xff0c;它允许我们根据一个或多个函数来调整查询结果的相关性得分。这使得我们可以基于某些条件对搜索结果进行更精细的控制。本文将介绍如何在Java应用程序中使用Elasticsearch的RestHighLevelClient执行Funct…

城市夜景车水马龙视频素材去哪里找?

在这个数字化与视觉化日益融合的时代&#xff0c;城市夜景已成为吸引观众眼球的热门视频主题。无论是个人爱好者还是企业&#xff0c;都致力于通过展示繁华都市的夜晚来俘获观众的心。今天&#xff0c;我将为您介绍几个优秀的视频素材网站&#xff0c;帮助您获取高质量的城市夜…

在小红书用AI做搞笑日常图文号,单号软广赚3000+!

今天&#xff0c;当我在小红书上刷案例时&#xff0c;突然眼前一亮&#xff0c;发现了一些以沙雕日常为主题的手绘插画图文账号。这些账号的笔记点赞量高得惊人&#xff0c;每篇都有上千的点赞&#xff0c;而且植入的软广告报价也是高得吓人。 像上图这个账号&#xff0c;它只发…

MetaLLM大语言模型文本生成算法分析报告

一、算法安全与监测 算法安全 信息内容安全方面&#xff0c;MetaLLM算法必须确保生成的文本不包含有害信息&#xff0c;如不当言论、歧视性内容等。这需要在训练数据中进行严格的筛选&#xff0c;并在模型设计时加入过滤机制。信息源安全则关注于训练数据的质量和多样性&…

【sgCreateAPIFunction】自定义小工具:敏捷开发→自动化生成API接口方法代码片段脚本(接口方法代码生成工具)

sgCreateAPIFunction源码 <template><!-- 前往https://blog.csdn.net/qq_37860634/article/details/141159084 查看使用说明 --><div :class"$options.name"><div class"sg-head">接口方法生成工具<el-dropdown:show-timeou…

vue2+OpenLayers 地图上添加渐变色(6)

渐变还有些问题需要晚上 引入 import sxs from "/views/json/sx.json"; import sx from "/views/json/sx1.json"; import GeoJSON from "ol/format/GeoJSON"; import Polygon, { fromExtent } from "ol/geom/Polygon"; import Linear…

文件上传漏洞-防御

防御文件上传的方法各种各样 1、限制文件上传的类型&#xff08;不让上传php等脚本类文件、只允许上传图片&#xff09; 2、给上传的文件重命名&#xff0c;让攻击者找不到自己传的文件在哪 3、限制文件上传大小 4、压缩上传文件 5、把上传的文件存储到文件服务器或者OSS平…

uniapp获取头像文件(二进制文件显示图片)

一、描述 由于在获取头像文件过程中&#xff0c;传递参数之后&#xff0c;请求成功了&#xff0c;但是后端给我返回了一串二进制数据流&#xff0c;傻傻的我&#xff0c;以为是乱码&#xff0c;跑去问后端大哥&#xff0c;人家跟我说这不是二进制吗&#xff0c;突然就觉得自己傻…

引领端侧多模态新时代:MiniCPM-V 2.6重磅登场

前沿科技速递&#x1f680; 在人工智能领域&#xff0c;每一次技术的进步都伴随着参数规模的提升和计算力的突破。然而&#xff0c;面壁智能公司最新推出的MiniCPM-V 2.6端侧多模态模型&#xff0c;却以相对“小巧”的8B参数量级&#xff0c;打破了传统思维&#xff0c;实现了端…

初学者入门的可视化超级色彩公式

色彩不仅是视觉元素&#xff0c;也是数据表达的重要工具。在临床数据的可视化过程中&#xff0c;合理的色彩搭配能帮助观众迅速理解数据背后的意义。例如&#xff0c;高危状态的患者可能用红色表示&#xff0c;而健康状态用绿色表示。不同色彩之间的对比度和相对位置将决定数据…

蓝牙耳机怎么连接手机?苹果用户关注这3个方法

在这个无线连接日益普及的时代&#xff0c;蓝牙耳机已成为我们日常生活中不可或缺的伴侣。然而&#xff0c;对于初次使用或遇到连接问题的用户来说&#xff0c;如何解决蓝牙耳机怎么连接手机的问题可能会有些许困惑。本文将为您详细介绍3种简单易行的方法&#xff0c;帮助您轻松…

Transformer动画讲解-多模态

Transformer模型是一种基于自注意力机制的神经网络架构&#xff0c;广泛应用于自然语言处理任务&#xff0c;如机器翻译、文本摘要等。 Transformer模型在多模态数据处理中扮演着重要角色&#xff0c;其能够高效、准确地处理包含不同类型数据&#xff08;如图像、文本、音频等&…

Polars简明基础教程十一:可视化(一)

到本次讲座结束时&#xff0c;你将能够&#xff1a; 使用Polars的内部plot方法从Polars创建图表使用外部绘图库从Polars创建图表了解这些库如何支持Polars 通常&#xff0c;需要可视化库的最新版本来实现最大程度的兼容性 import polars as plimport hvplot as hv import ma…

陈丽华珍藏梁永和书画作品,展现中华艺术之美

近两年来&#xff0c;富华国际集团董事局主席、中国紫檀博物馆馆长陈丽华女士收藏了多件由人民艺术家、著名画家梁永和先生亲笔创作的书画作品&#xff0c;其中包括《松鹤延年》、《香荷》、《江山多娇》和《硕果累累》等佳作。此外&#xff0c;梁永和先生还特地为陈丽华女士创…