归并排序有多简单?一幅图教你看懂【C语言】

简介: 归并排序有多简单?一幅图教你看懂【C语言】

000000000000000000000000.png

目录


归并排序的递归实现  

代码实现

归并排序的非递归实现  

代码实现


前言


归并排序的思想很简单——分治法。简单地说,归并排序的是将序列拆分成几段子序列,将每一段子序列分别进行排序,排好之后再将有序的子序列归并(有点像合并两个有序数组)成为一个有序的序列。


例如要排序数列:10、6、7、1、3、9、4、2;


7.png


将序列拆分为 2 个子序列;


继续拆分;


6.png

继续拆分;


5.png


至此,每个子序列的长度都为 1 ,因为只有一个数,所以可认为是有序序列;


现将子序列两两归并,即合并两个有序序列;


4.png


继续归并;

3.png

继续归并;

2.png

以上就是归并排序的整个过程,很显然归并排序的实现应该离不开递归的思想。

1.png


正文


归并排序的递归实现  


归并排序的递归实现较为简单,需要注意的有两点:

1. 归并的过程并非在原数组上直接改动,而是开辟一个临时数组,在临时数组上进行排序,排好之后将临时数组的内容全部拷贝到原数组;

2. 代码中使用的是二路归并(如上图所示,每次将序列拆分为两个子序列)。


代码实现


void _MergeSort(int* a, int begin, int end, int* tmp)
{
  //递归的结束条件//当序列只有一个元素时或序列不存在时
  if (begin >= end)
    return;
  //将序列进行拆分 //[begin,mid]  [mid+1,end]
  int mid = (begin + end) / 2;
  //拆分的过程
  _MergeSort(a, begin, mid, tmp);
  _MergeSort(a, mid+1, end, tmp);
  //以下为归并的过程
  int begin1 = begin, end1 = mid;
  int begin2 = mid+1, end2 = end;
  int i = begin;
  //归并:合并两个有序序列
  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 + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}
void MergeSort(int* a,int n)
{
  //开辟一个临时数组
  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;
}


归并排序的非递归实现  


非递归与递归作用思想基本相同。递归实现时,因为拆分序列时采用的是递归的方式,所以通过传递参数就可以控制子序列的长度。但是非递归不行,非递归通过变量 rangeN 来控制序列的长度(或间隔),每次让 rangeN *= 2 例如:

8.png

但是由于 rangeN 每次都 *2 ,而我们排序的序列长度不可能总是 2 的倍数,所以 可能会有数组越界访问的风险。例如:9.png

现将两个子序列归并,并将数据拷贝回原数组时,就会发生越界:10.png

当然这只是其中一种越界的可能情况——第二段序列发生越界,原因是右边界 end2 大于 n;


实际操作中,一共会有三种情况导致越界:


两段序列的区间分别为: [begin1,end1]  [begin2,end2]


1. end1 > n;


2. begin > n;


3.end2 > n;


所以,当这三种情况发生时,需要修正区间,以上述用例为例, end2 大于 n 时,令 end2 = n-1即可;


代码实现


void MergeSortNonR(int* a, int n)
{
  //开辟一个临时数组
  int* tmp = (int*)malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  int rangeN = 1;
  while (rangeN < n)
  {
        // i 控制访问子序列的位置
    for (int i = 0; i < n; i += 2 * rangeN)
    {
      //拆分为两段子序列//[begin1,end1] [begin2,end2]
      int begin1 = i, end1 = i + rangeN - 1;
      int begin2 = i + rangeN, end2 = i + 2 * rangeN - 1;
      int j = i;
            //判断是否发生越界的三种情况,如果有,就修正区间
      if (end1 >= n)
      {
        end1 = n - 1;
        //将第二段序列改为不存在的序列即可
        begin2 = n;
        end2 = n - 1;
      }
      else if (begin2 >= n)
      {
        //将第二段序列改为不存在的序列即可
        begin2 = n;
        end2 = n - 1;
      }
      else if (end2 >= n)
      {
                //修正区间
        end2 = n-1;
      }
      while (begin1 <= end1 && begin2 <= end2)
      {
        if (a[begin1] <= a[begin2])
        {
          tmp[j++] = a[begin1++];
        }
        else
        {
          tmp[j++] = a[begin2++];
        }
      }
      //如果第二段序列先结束
      while (begin1 <= end1)
      {
        tmp[j++] = a[begin1++];
      }
      //如果第一段序列先结束
      while (begin2 <= end2)
      {
        tmp[j++] = a[begin2++];
      }
    }
    //将临时数组的内容拷贝回原数组
    memcpy(a, tmp, sizeof(int) * n);
    //控制间隔
    rangeN *= 2;
  }
  //释放与置空
  free(tmp);
  tmp = NULL;
}
目录
相关文章
|
6天前
|
C语言
【C语言】拿捏冒泡排序(图解)
【C语言】拿捏冒泡排序(图解)
|
6天前
|
搜索推荐 算法 C语言
C语言实现冒泡排序算法
C语言实现冒泡排序算法
19 0
|
6月前
|
C语言
C语言初阶,矩阵交换
C语言初阶,矩阵交换
C语言初阶,矩阵交换
|
10月前
|
机器学习/深度学习 搜索推荐 算法
选择排序(附代码详解)(C语言)
选择排序(附代码详解)(C语言)
326 0
|
11月前
|
算法 C语言
简单算法之冒泡排序——C语言 (1)
冒泡排序的规则是相邻的两个数字依次比较,如果前面的数字比后面的数字大,则交换它们的位置,否则保持不变,直到遍历完所有的数字。这个过程会不断地进行,直到所有的数字都按照从小到大的顺序排列好。
69 0
|
11月前
|
搜索推荐 算法 C语言
简单算法之选择排序——C语言 (1)
简单算法之选择排序——C语言 (1)
149 0
|
11月前
|
算法 C语言
简单算法之二分搜索——C语言
线性搜索效率低下,而二分搜索可以将数组中的数字分为两半,从而提高搜索效率。
80 1
|
11月前
|
存储 算法 C语言
简单算法之线性搜索——C语言
线性搜索是一种最简单的搜索方案,它通过遍历数组中的每一个数据来实现目的,即找出目标数字的位置或确认该数字是否存在。在本篇文章中,我们将介绍如何使用C语言实现线性搜索算法。
104 0
|
11月前
|
算法 C语言
【C语言】经典算法之二分查找
【C语言】经典算法之二分查找
62 0
|
12月前
|
算法 C语言
【C语言】二分法查找——————细节讲解
概念 二分法查找,是一种在有序数组中快速目标数字的一种算法,也可以叫做折半查找。 要掌握二分法查找,首先我们要明白二分法查找是怎么运作的,为什么要用二分法查找。