排序算法总结——归并排序

2023-10-29

1. 算法原理及步骤

2. 代码实现

3. 复杂度分析

4. 稳定性分析


1. 算法原理及步骤

       归并排序体现的是一种分治+合并的思想,我们知道,数组长度越小,排序越简单,而不管数组有多大,都是由小数组构成的,因此,要想对一个长度为N的数组进行排序,就可以将其进行分割,分割到大小为1的小数组,然后再将每个小数组进行排序再合并,最终合并为有序的原数组,如图所示:

       由图可知,归并排序主要是将原数组拆分成长度为1的小数组中,然后再将这些小数组两两归并再排序,然后再将归并后的数组两两归并再排序.....最终归并为长度为n的数组再对其进行排序即可。那么问题来了:归并最终也要对n的数组排序呀,它的效率体现在哪里呢?

        实际上,在每两个小数组归并的过程中,都是会对其归并结果进行排序的。最小的数组长度就是1,本身就是有序的,两个长度为1的数组再进行合并成长度为2的小数组并对其进行排序,然后再归并、再归并、....再归并为n的数组,可以发现,每次进行归并的两个小数组都是分别有序的,这就是归并排序能体现其效率的关键:相比于对无序数组排序,合并两个有序数组的时间复杂度只需要O(N)。

       因此,归并排序最关键的就是对两个有序数组进行排序了,可以采用双指针的方法,一开始分别指向两个数组头部元素,然后比较二者大小,将较小的作为合并结果的第一个元素,然后将较小数的指针往后移动一步,然后再比较,再将较小的作为合并结果的第二个元素......直到其中某一个指针走到尾部了,那么此时只需要将还没到尾的另一个数组剩下的元素直接按序放入合并数组的后面即可,整个过程如图所示:

     通过以上可以知道,归并排序实际上就是数组分割+合并排序。而其中数组分割是将数组二分为左半区间和右半区间,然后再对左半区间、右半区间进行二分.....直到最终数组长度为1,而合并排序即是对长度为1的数组开始自下而上开始两两归并并排序,最终得到有序的原数组。(以上图片来自于https://www.cnblogs.com/chengxiao/p/6194356.html

       总结可得递归排序的步骤:

       ①找到中点;

       ②根据中点划分左子区间及右子区间,并且分别递归两子区间;

       ③合并递归后的子区间。


2. 代码实现

void mergeTwoSortedParts(vector<int>& nums,int left,int right,int mid)//合并数组中的两个有序子区间,需要指定整个区间范围以及划分出子区间的中点
{
    int i=left;   //指向左区间头
    int j=mid+1;  //指向右区间头
    vector<int>temp;  //临时数组存放合并结果

    while(i<=mid&&j<=right)
    {
        if(nums[i]<nums[j])temp.push_back(nums[i++]);  //如果左区间数较小就放到临时数组中,然后左指针右移
        else temp.push_back(nums[j++]); //如果右区间数较小就放到临时数组中,然后右指针右移
    }

    if(i<=mid)  //如果左区间还没遍历完,直接将左区间剩下的接在临时数组后面
    {
        for(int l=i;l<=mid;l++)temp.push_back(nums[l]);
    }

    if(j<=right) //如果右区间还没遍历完,直接将右区间剩下的接在临时数组后面
    {
        for(int l=j;l<=right;l++)temp.push_back(nums[l]);
    }
    for(auto x:temp)nums[left++]=x;  //此时临时数组已经排好序,将临时数组复制到原数组中即可
    return ;
}
void divArraySort(vector<int>& nums,int left,int right)//分割排序函数,参数为待排序数组及区间
{
    if(right-left==0)return ; //数组分割到长度为1时返回

    int mid=left+(right-left)/2;  //取中点二分

    divArraySort(nums,left,mid); //分割左子区间并排序
    divArraySort(nums,mid+1,right); //分割右子区间并排序
    
    mergeTwoSortedParts(nums,left,right,mid); //对排序好的左子区间和右子区间进行合并排序
    return ;

}
void MergeSort(vector<int>& nums,int len) //归并排序函数,参数为待排序函数及其长度
{
    if(!len)return;
    divArraySort(nums,0,len-1);  //归并排序
    return;
}

3. 复杂度分析

       不难看出,归并排序的过程实际就是将原数组不断二分,到最后子数组长度为1,二分的次数当然就是logN次了,而另一方面,每一次二分都还需要将二分的数组再合并排序,这个排序的过程就是O(N)了,因此整个归并排序不管是最好情况还是最坏情况都一样,时间复杂度就是O(NlogN),当然其中还包括临时数组向原数组赋值的O(N)时间复杂度。在空间复杂度方面,开销主要是由合并排序引起的,合并排序需要一个与原数组大小相同的临时数组来存放合并结果,因此空间复杂度为O(N)

4. 稳定性分析

       由归并过程可知,只会改变两个有大小关系的元素间的位置,对于相等元素是不会改变二者相对位置的,因此归并排序是稳定的。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

排序算法总结——归并排序 的相关文章

  • 各类排序算法的比较总结

    排序算法是最基本最常用的算法 不同的排序算法在不同的场景或应用中会有不同的表现 我们需要对各种排序算法熟练才能将它们应用到实际当中 才能更好地发挥它们的优势 今天 来总结下各种排序算法 下面这个表格总结了各种排序算法的复杂度与稳定性 各种排
  • 剑指offer(简单)

    目录 数组中重复的数字 替换空格 从尾到头打印链表 用两个栈实现队列 斐波那契数列 青蛙跳台阶问题 旋转数组的最小数字 二进制中的1的个数 打印从1到最大的n位数 删除链表的节点 调整数组顺序使奇数位于偶数前面 链表中倒数第k个节点 反转链
  • 每日编程一题刷之有序数组的平方(暴力解法+双指针)

    每日编程一题刷之有序数组的平方 暴力解法 双指针 目录侠 文章目录 每日编程一题刷之有序数组的平方 暴力解法 双指针 题目链接以及描述 题解分析 双指针解法 小结 题目链接以及描述 977 有序数组的平方 力扣 LeetCode leetc
  • 转-各种排序动图

    1 快速排序 介绍 快速排序是由东尼 霍尔所发展的一种排序算法 在平均状况下 排序 n 个项目要 n log n 次比较 在最坏状况下则需要 n2 次比较 但这种状况并不常见 事实上 快速排序通常明显比其他 n log n 算法更快 因为它
  • 数据结构之快速排序算法

    文章目录 快速排序的思想 快速排序的递归实现 快速排序的非递归实现 快速排序的思想 设置两个变量i j 排序开始的时候 令i 0 j length 1 以第一个数组元素作为比较 赋值给temp 即temp nums 0 从j开始向前扫描 找
  • (SUB)选择排序时间、空间复杂度

    基本思想 将一组数据分为两部分 前面是已排序部分 后面是未排序部分 初始状态可认为位置 0 为已排序部分 数组下标从0开始 其余为未排序部分 每一次都从未排序部分选择一个最小元素放在已排序部分的末尾 然后已排序部分增加一个元素 未排序部分减
  • Java往字符串数组中追加一个数据

    public class Test public static void main String args 原字符串数组 String arr 原字符串数据1 原字符串数据2 执行数据添加 arr insert arr 需要追加的字符串数据
  • 【数据结构】——八大排序

    文章目录 1 插入排序 2 冒泡排序 3 希尔排序 4 选择排序 5 快速排序 快排优化 递归改非递归 6 堆排序 7 归并排序 递归归并排序 改成非递归 8 计数排序 9 题目 总结 排序的时间检验 对于不同排序的时间复杂度分析 1 插入
  • 算法学习01-选择、冒泡、插入排序

    1 选择排序 选择排序 0到n 1位置 找到最小值 放到0位置 1到n 1位置 找到最小值 放到1位置 i到n 1位置 找到最小值 放到i位置 以此类推 public class SelectionSort public static vo
  • 插入排序总结

    插入排序 Insertion Sort 的算法描述是一种简单直观的排序算法 它的工作原理是通过构建有序序列 对于未排序数据 在已排序序列中从后向前扫描 找到相应位置并插入 排序思路 假设按照升序排序 1 从索引为1的元素开始向前比较 一旦前
  • 非递归算法——快速排序、归并排序

    哈喽大家好 我是保护小周 本期为大家带来的是常见排序算法中的快速排序 归并排序 非递归算法 分享所有源代码 粘贴即可运行 保姆级讲述 包您一看就会 快来试试吧 目录 一 递归的缺陷 1 1 栈是什么 数据结构 栈 又是什么 他们之间有什么区
  • 九种常见排序的比较和实现

    首先排序算法大的可以分为 关键字比较 非关键字比较 关键字比较 关键字比较就是通过关键字之间的比较和移动 从而使整个序列有序 而关键字比较的算法 又可以像下面这样划分 对于排序算法之间的比较 无异于时间复杂度和空间复杂度 看下面这张表格 由
  • C语言冒泡排序和选择排序

    一 冒泡排序法 假设从小到大排序 例一数组 int arr 2 1 34 5 arr 0 先跟相邻的arr 1 比较大小 如果比它大则交换两个数值位置 大的数值放在后面 然后比较arr 1 和arr 2 的大小 以此类推 直至第n 2个和第
  • 快速排序算法详解(原理,时间复杂度,实现代码)

    快速排序算法详解 原理 实现和时间复杂度 快速排序是对冒泡排序的一种改进 由 C A R Hoare Charles Antony Richard Hoare 东尼 霍尔 在 1962 年提出 快速排序的基本思想是 通过一趟排序将要排序的数
  • 2023华为OD机试真题【双指针/优雅子数组】

    题目内容 如果一个数组中出现次数最多的元素出现大于等于K次 被称为K 优雅数组 k也可以被称为优雅阈值 例如 数组1 2 3 1 2 3 1 它是一个3 优雅数组 因为元素1出现次数大于等于3次 数组1 2 3 1 2就不是一个3 优雅数组
  • 快速排序(三种算法实现和非递归实现)

    快速排序 Quick Sort 是对冒泡排序的一种改进 基本思想是选取一个记录作为枢轴 经过一趟排序 将整段序列分为两个部分 其中一部分的值都小于枢轴 另一部分都大于枢轴 然后继续对这两部分继续进行排序 从而使整个序列达到有序 递归实现 v
  • 算法导论 学习笔记 第七章 快速排序

    快排最坏时间复杂度为 n 但它的平均性能很好 通常是实际排序应用中最好的选择 它的期望时间复杂度为 nlgn 且 nlgn 中隐含的常数因子非常小 且它还能进行原址排序 快排也使用了分治思想 1 分解 数组被划分为两个子数组 使得一个子数组
  • 基数排序代码实现

    详情请看排序总结 传送门 https blog csdn net m0 52711790 article details 121914543 基数排序的知识点我就不贴出来 相信都能搜到对应概念解释 下面就直接上代码 代码解释其实也很清晰了
  • 详解十大经典排序算法(四):希尔排序(Shell Sort)

    算法原理 希尔排序是一种基于插入排序的排序算法 也被称为缩小增量排序 它通过将待排序的序列分割成若干个子序列 对每个子序列进行插入排序 然后逐步缩小增量 最终使整个序列有序 算法描述 希尔排序 Shell Sort 是一种基于插入排序的算法
  • 冒泡排序/选择排序/插入排序/快速排序/归并排序/桶排序/堆排序/希尔排序/计数排序/基数排序/二分查找/广度优先搜索/深度优先搜索

    排序算法 冒泡排序 Bubble Sort 通过重复地比较相邻的元素并交换它们 使得最大 或最小 的元素逐渐移动到列表的一端 从而实现排序 选择排序 Selection Sort 在未排序的部分中 选择最小 或最大 的元素 并将其放置在已排

随机推荐