【排序算法】C语言实现归并排序,包括递归和迭代两个版本

在线体验各类最新模型,更有模型 免费Token 额度领取!
立即体验
简介: 【排序算法】C语言实现归并排序,包括递归和迭代两个版本

🚀前言

大家好啊!阿辉接着更新排序算法,今天要讲的是归并排序,这里阿辉将讲到归并排序递归实现迭代实现,话不多说,开始咱们今天的学习吧!!!!

🚀归并排序介绍及其思想

归并排序这是阿辉讲的第一个时间复杂度O(nlogn)的排序算法,额外空间复杂度是O(n),归并排序可以做到稳定性。

思想

归并排序的思想就是分治分治的思想是将一个大问题分解成若干个小问题,然后分别解决这些小问题,最后将这些小问题的解合并起来得到原问题的解

由分治的思想很容易,想到用递归来实现归并排序,我们接着看👇

🚀递归实现

关于归并排序的递归方法主要由三个大的逻辑组成:

  • 分解:将待排序的数组分成两个子数组
  • 解决:对每个子数组进行排序
  • 合并:将排序好的子数组合并成一个有序的数组

这里我们使用递归很轻松就能把主逻辑写好,大家都知道写递归必须要有限制条件否则会造成死递归,对于归并排序的限制条件很简单,对于数组只有一个元素时自然就是有序的,直接返回即可

主逻辑:

merge函数相当于黑盒,作用就是把两个有序数组合成一个大的有序数组

void MergeSort(int a[], int l, int r) {// C/C++归并排序递归版本,主逻辑
  if (r == l) {//递归限制条件
    return;
  }
  int m = l + ((r - l) >> 1);//数组中位置下标
  MergeSort(a, l, m);//左部分排序
  MergeSort(a, m + 1, r);//右部分排序
  merge(a, l, m, r);//两部分有序数组合并
}

整个归并排序最重要的部分也就是有序数组合并的部分:

merge函数实现,还是不太懂的可以看一下下面的代码,有详细的注释

C语言版本:

void merge(int a[], int l, int m, int r) {
  int* help = (int*)malloc((r - l + 1) * 4);//申请辅助空间
  int i = 0;//作为help指针的偏移量,存储两有序数组排好序的大数组
  int first = l;//作为左部分数组的起始下标
  int second = m + 1;//作为右部分数组的起始下标
  while (first <= m && second <= r) {//哪一方下标越界就说明不用继续比较了
    //三目运算代码更简洁,谁小谁在前先赋值给help,后置++,
    // 赋值后i向后偏移一个位置,second或first也向后偏移一个位置
    help[i++] = a[first] <= a[second] ? a[first++] : a[second++];
  }
  //下面虽然两个while循环但是只会进去一个
  //还没存进help数组的继续存
  while (first <= m) {
    help[i++] = a[first++];
  }
  while (second <= r) {
    help[i++] = a[second++];
  }
  //最后把help管理的值,还原到原数组a中
  for (i = 0; i < r - l + 1; i++) {
    a[l + i] = help[i];
  }
  free(help);//释放申请的堆空间
  help = NULL;//野指针系上绳子
}

C++版本:

也就是用了STL的容器更方便

void merge(vector<int>& arr, int l, int mid, int r) {//合并有序数组
  vector<int> help(r - l + 1, 0);//用一个额外的数组装排好的数
  int first = l;
  int second = mid + 1;
  int i = 0;
  //合并过程
  while (first <= mid && second <= r) {
    help[i++] = arr[first] <= arr[second] ? arr[first++] : arr[second++];
  }
  while (first <= mid) {
    help[i++] = arr[first++];
  }
  while (second <= r) {
    help[i++] = arr[second++];
  }
  //将help数组拷贝到原数组
  for (int i = 0; i < r - l + 1; i++) {
    arr[l + i] = help[i];
  }
}

🚀迭代实现

归并排序的迭代实现就是把主逻辑从递归改为迭代了,有序合并部分并没有改变还是上面实现的merge函数

其实就是从递归的条件返回那一步往后推:

这里我们引入一个概念步长,用来表示左部分和右部分的数组长度,步长从1开始,然后每次倍增,就相当于递归的回溯过程

上图:

步长为左右部分长度,左右部分进行merge操作,没有右部分的跳过

主逻辑:

void MergeSort(int a[], int l, int r) {
  int len = 1;//步长
  while (len <= r) {
    l = 0;
    while (l <= r) {
      int m = l + len - 1;//计算左部分的最后一个位置
      if (m >= r) {//此时说明没有右部分,可以跳出循环进行下一轮了
        break;
      }
      //m + len是右部分的最后一个位置与r做比较防止越界,拿到一个正确的merge右边界
      int n = r <= m + len ? r : m + len;
      merge(a, l, m, n);
      l = n + 1;
    }
    if (len > r / 2) {//假如数组很长len×2可能溢出,防止溢出变成负数死循环
      break;
    }
  }
}

merge函数和前面的一样,阿辉就不水了

相关文章
|
搜索推荐 C语言
【排序算法】快速排序升级版--三路快排详解 + 实现(c语言)
本文介绍了快速排序的升级版——三路快排。传统快速排序在处理大量相同元素时效率较低,而三路快排通过将数组分为三部分(小于、等于、大于基准值)来优化这一问题。文章详细讲解了三路快排的实现步骤,并提供了完整的代码示例。
845 4
|
存储 算法 Java
算法系列之递归反转单链表
递归反转链表的基本思路是将当前节点的next指针指向前一个节点,然后递归地对下一个节点进行同样的操作。递归的核心思想是将问题分解为更小的子问题,直到达到基本情况(通常是链表末尾)。
551 5
算法系列之递归反转单链表
|
存储 C语言
【C语言程序设计——函数】递归求斐波那契数列的前n项(头歌实践教学平台习题)【合集】
本关任务是编写递归函数求斐波那契数列的前n项。主要内容包括: 1. **递归的概念**:递归是一种函数直接或间接调用自身的编程技巧,通过“俄罗斯套娃”的方式解决问题。 2. **边界条件的确定**:边界条件是递归停止的条件,确保递归不会无限进行。例如,计算阶乘时,当n为0或1时返回1。 3. **循环控制与跳转语句**:介绍`for`、`while`循环及`break`、`continue`语句的使用方法。 编程要求是在右侧编辑器Begin--End之间补充代码,测试输入分别为3和5,预期输出为斐波那契数列的前几项。通关代码已给出,需确保正确实现递归逻辑并处理好边界条件,以避免栈溢出或结果
847 16
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
676 7
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
982 8
|
算法 Python
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果
在Python编程中,分治法、贪心算法和动态规划是三种重要的算法。分治法通过将大问题分解为小问题,递归解决后合并结果;贪心算法在每一步选择局部最优解,追求全局最优;动态规划通过保存子问题的解,避免重复计算,确保全局最优。这三种算法各具特色,适用于不同类型的问题,合理选择能显著提升编程效率。
389 2
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
1060 7
|
C语言
c语言回顾-函数递归(下)
c语言回顾-函数递归(下)
|
存储 算法 C语言
【C语言程序设计——函数】素数判定(头歌实践教学平台习题)【合集】
本内容介绍了编写一个判断素数的子函数的任务,涵盖循环控制与跳转语句、算术运算符(%)、以及素数的概念。任务要求在主函数中输入整数并输出是否为素数的信息。相关知识包括 `for` 和 `while` 循环、`break` 和 `continue` 语句、取余运算符 `%` 的使用及素数定义、分布规律和应用场景。编程要求根据提示补充代码,测试说明提供了输入输出示例,最后给出通关代码和测试结果。 任务核心:编写判断素数的子函数并在主函数中调用,涉及循环结构和条件判断。
939 23
|
10月前
|
存储 C语言
`scanf`是C语言中用于按格式读取标准输入的函数
`scanf`是C语言中用于按格式读取标准输入的函数,通过格式字符串解析输入并存入指定变量。需注意输入格式严格匹配,并建议检查返回值以确保读取成功,提升程序健壮性。
1553 0

热门文章

最新文章