【数据结构】—超级详细的归并排序(含C语言实现)

简介: 【数据结构】—超级详细的归并排序(含C语言实现)

♉️一、前置知识—什么是归并排序

    归并排序是一种基于分治思想的排序算法。它将待排序的数组分成两个子数组,对每个子数组进行排序,最后将子数组合并成一个有序的数组。具体来说,归并排序采用递归的方式将数组不断二分,直到每个子数组只有一个元素,然后再将相邻的两个子数组归并成一个有序的数组,然后不断合并,直到最终得到原数组的有序排列当然你也可以采用非递归的方法,总体的思路都是一样的与快速排序不同的是,归并排序在每轮排序中都会将数组完全拆分成两个子数组。


♊️二、归并排序

       归并排序的思想

      归并排序是基于分治思想的一种排序算法,它可以将一个大问题分解成若干个小问题,再通过合并小问题的解来得到大问题的解。

      具体来说,归并排序的思想如下:

 1.将待排序的数组划分为两个子数组,对每个子数组进行递归排序;

 2.将排好序的子数组合并成一个有序的数组。

      这一过程可以不断重复,直到将整个数组划分为单个元素的子数组,然后再将其合并成一个有序数组,排序完成。

 一图理解~

1.png动图了解~

2.gif 

       归并排序的递归实现

     归并排序的递归实现步骤如下:

1.如果数组长度为1,直接返回该数组;

2.将数组按中间位置划分成两个子数组,分别对这两个子数组进行递归排序,得到排好序的两个子数组;

3.将排好序的两个子数组合并成一个有序数组,具体方法为创建一个辅助数组,同时遍历两个子数组,比较两个子数组中当前位置的元素,将较小的元素放入辅助数组中;

4.将辅助数组中的元素复制回原数组中。

代码实现:

void _Mergesort(int* a, int* temp,int begin, int end)
{
  if (end <= begin)//递归结束条件
    return;
  int mid = (begin + end) / 2;//开始二分
  _Mergesort(a, temp, begin, mid);//左半边
  _Mergesort(a, temp, mid + 1, end);//右半边
  //正式的归并操作
  //分出来的两个数组的下标
  int begin1 = begin;
  int end1 = mid;
  int begin2 = mid + 1;
  int end2 = end;
  int index = begin;
  while(begin1 <= end1 && begin2 <= end2)//归并
  {
    if (a[begin1] < a[begin2])
    {
      temp[index++] = a[begin1++];
    }
    else
    {
      temp[index++] = a[begin2++];
    }
  }
  //当其中有一个条件不符合时,跳出循环,但是可能还有数据每遍历完
  while (begin1 <= end1)
  {
    temp[index++] = a[begin1++];
  }
  while (begin2 <= end2)
  {
    temp[index++] = a[begin2++];
  }
  // 拷贝回原数组
  memcpy(a + begin, temp + begin, (end - begin + 1) * sizeof(int));//拷贝对应的数据到对应的位置
}
void Mergesort(int* a, int n)
{
  int* temp = (int*)malloc(sizeof(int) * n);
  if (temp == NULL)
  {
    perror("malloc fail");
    return;
  }
  _Mergesort(a, temp,0,n-1);
  free(temp);
}

♒️归并排序的非递归实现(难点)

      归并排序的非递归实现,也称为迭代实现,采用的是自底向上的方式,

       步骤如下:

      1.将原数组按照长度为1的子数组进行划分,每个子数组都是有序的;

      2.将长度为1的有序子数组两两合并,得到长度为2的有序子数组;

      3.重复以上步骤,直到得到一个完整有序的数组。

      代码实现:

void MergesortNonR(int* a, int n)
{
  int* temp = (int*)malloc(sizeof(int) * n);
  if (temp == NULL)
  {
    perror("malloc fail");
    return;
  }
  //设置一个gap用于从间距为1开始(也就是最开始的归并),对于数组进行归并操作,然后归并完一遍后让gap*2继续归并,以此类推
  int gap = 1;
  while (gap < n)//gap停止的条件,实际上gap在n的一半时就已经排好了
  {
    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;
      //以下是处理越界的情况,有些时候当数组会越界
      // 如果第二组不存在,这一组不用归并了
      if (begin2 >= n)
      {
        break;
      }
      // 如果第二组的右边界越界,修正一下
      if (end2 >= n)
      {
        end2 = n - 1;
      }
      //以下的操作就是和递归的操作相同的归并操作
      int index = i;
      while (begin1 <= end1 && begin2 <= end2)
      {
        if (a[begin1] < a[begin2])
        {
          temp[index++] = a[begin1++];
        }
        else
        {
          temp[index++] = a[begin2++];
        }
      }
      while (begin1 <= end1)
      {
        temp[index++] = a[begin1++];
      }
      while (begin2 <= end2)
      {
        temp[index++] = a[begin2++];
      }
      // 拷贝回原数组
      memcpy(a + i, temp + i, (end2 - i + 1) * sizeof(int));
    }
    gap *= 2;
  }
  free(temp);
}

♋️三、归并排序的特性总结

      1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的

       外排序问题。

       2. 时间复杂度:O(N*logN)

       3. 空间复杂度:O(N)

       4. 稳定性:稳定


  感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

相关文章
|
15天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
90 9
|
14天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
57 16
|
14天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
63 8
|
17天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
44 4
|
18天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
50 3
|
18天前
|
存储 算法 C语言
C语言数据结构(2)
【10月更文挑战第21天】
|
17天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
37 0
|
6天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
15 1
|
9天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
12天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。