排序篇(四)----归并排序

简介: 排序篇(四)----归并排序

排序篇(四)----归并排序

1.归并(递归)

基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

归并排序核心步骤:

//归并
void _MergeSort(int* a, int* tmp, int begin, int end)
{
  if (end <= begin)
  {
    return;
  }
  int mid = (begin + end) / 2;
  _MergeSort(a, tmp, begin, mid);
  _MergeSort(a, tmp, mid + 1, end);
  // a->[begin, mid][mid+1, end]->tmp
  //归并
  int begin1 = begin, end1 = mid;
  int begin2 = mid + 1, end2 = end;
  int index = begin;
  while (begin1 <= end1 && begin2 <= end2)
  {
    if (a[begin1] <= a[begin2])
    {
      tmp[index++] = a[begin1++];
    }
    else
    {
      tmp[index++] = a[begin2++];
    }
  }
  while (begin1 <= end1)
  {
    tmp[index++] = a[begin1++];
  }
  while (begin2 <= end2)
  {
    tmp[index++] = a[begin2++];
  }
  memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}
//归并(递归版)
void MergeSort(int* a, int n)
{
  int* tmp = malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  _MergeSort(a, tmp, 0, n - 1);
  free(tmp);
}

归并需要开一个同样大的tmp数组来存放数据,相当于是拿空间来换时间了.

代码详解:

  1. 首先,将整个序列分为两部分,分别递归调用_MergeSort函数对左右两部分进行排序。
  2. 在_MergeSort函数中,首先判断递归终止条件,如果end小于等于begin,则表示当前子序列只有一个元素或者为空,无需排序,直接返回。
  3. 然后,计算中间位置mid,并分别递归调用_MergeSort函数对左右两部分进行排序。
  4. 接下来,进行归并操作。首先,设置四个指针begin1、end1、begin2、end2分别指向左右两部分的起始和结束位置,以及一个指针index指向当前归并结果的位置。
  5. 然后,使用两个循环比较左右两部分的元素大小,并将较小的元素放入tmp数组中,同时移动相应的指针。
  6. 最后,将剩余的元素复制到tmp数组中。
  7. 最后,将tmp数组中的元素复制回原数组a中,完成归并排序。

2.归并(非递归)

非递归版是在递归上进行了修改,将其递归改为了循环.

//归并(非递归版)
void _MergeSortNotR(int* a, int n)
{
  int* tmp = (int*)malloc(sizeof(int) * n);
  if (tmp == NULL)
  {
    perror("malloc fail");
    exit(-1);
  }
  int gap = 1;
  while (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])
        {
          tmp[index++] = a[begin1++];
        }
        else
        {
          tmp[index++] = a[begin2++];
        }
      }
      while (begin1 <= end1)
      {
        tmp[index++] = a[begin1++];
      }
      while (begin2 <= end2)
      {
        tmp[index++] = a[begin2++];
      }
      memcpy(a+i, tmp+i, (end2-i+1) * sizeof(int));
    }
    gap *= 2;
  }
  free(tmp);
}

代码详解:

分配一个临时数组tmp,用于存储归并结果。

然后,设置一个变量gap为1,表示归并的间隔大小。

接下来,循环执行以下操作,直到gap大于等于n:

  1. 遍历整个数组,每次取两个相邻的子序列进行归并。
  2. 计算左右两个子序列的起始和结束位置。
  3. 判断右子序列的结束位置是否超过了数组的长度,如果超过,则将结束位置设置为数组的最后一个元素的下标。
  4. 使用两个指针begin1和begin2分别指向左右两个子序列的起始位置,使用指针end1和end2分别指向左右两个子序列的结束位置。
  5. 使用一个指针index指向当前归并结果的位置。
  6. 使用一个循环,比较左右两个子序列的元素大小,并将较小的元素放入临时数组tmp中,同时移动相应的指针。
  7. 如果左子序列还有剩余元素,则将剩余元素复制到tmp数组中。
  1. 如果右子序列还有剩余元素,则将剩余元素复制到tmp数组中。
  2. 将tmp数组中的元素复制回原数组a中。
  3. 将gap乘以2,进行下一轮归并。

最后,释放临时数组tmp的内存空间。

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  1. 中的元素复制回原数组a中。
  2. 将gap乘以2,进行下一轮归并。

最后,释放临时数组tmp的内存空间。

归并排序的特性总结:

  1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
  2. 时间复杂度:O(N*logN)
  3. 空间复杂度:O(N)
  4. 稳定性:稳定
目录
相关文章
|
7月前
|
算法 搜索推荐
排序——归并排序
排序——归并排序
51 0
|
7月前
|
搜索推荐 算法 Shell
排序——插入排序
排序——插入排序
49 0
|
存储 搜索推荐 测试技术
排序篇(一)----插入排序
排序篇(一)----插入排序
51 0
|
算法 搜索推荐 索引
排序篇(二)----选择排序
排序篇(二)----选择排序
38 0
|
存储 算法 搜索推荐
排序(4)——归并排序
今天给大家带来比较排序的最后一种,归并排序,这个排序,需要我们对递归,循环控制要有着较强的理解,我相信大家通过前面和小编的一起学习,这里肯定也是得心应手。
101 0
|
存储 搜索推荐 测试技术
排序(1)之插入排序
从今天小编就开始给大家介绍排序的内容,对于排序内容我们一共分,插入,选择,交换,归并这几个大类,那么今天小编给大家带来的就是插入排序
105 0
|
搜索推荐
排序(2)之选择排序
继插入排序后,今天小编就给大家另一个模块,选择排序的学习,那么话不多说,我们直接进入正题。
122 0
|
算法 搜索推荐
排序——归并排序和计数排序
介绍归并排序和计数排序
115 0
排序——归并排序和计数排序
|
算法
排序——快速排序
排序——快速排序
125 0
排序——快速排序
|
算法 Java
排序系列之插入排序
排序系列之插入排序
130 0