【归并排序】| 详解归并排序 力扣912

🎗️ 主页:小夜时雨
🎗️专栏:归并排序
🎗️如何活着,是我找寻的方向

优雅

1. 题目解析

题目链接: https://leetcode.cn/problems/sort-an-array/

在这里插入图片描述

我们上道题讲过归并排序的核心代码,建议先看一下这道题:合并两个有序数组 https://leetcode.cn/problems/merge-sorted-array/description/

归并排序的核心代码区间就是合并两个有序数组, 所以还是十分重要的, 接下来我们再来分析一下合并这个过程:

  • 首先这两个是非递减的整数数组,那么很自然的一个想法就是从头开始遍历两个数组,谁小取出来排队即可。
  • 取出来排队这个操作我们巨化为创建一个辅助数组,将数组中二者比较小的放入到这个辅助数组中, 直到遍历结束。
  • 最后再将辅助数组拷贝到原始数组中即可。整体的思路还是比较符合实际我们进行比较排序的情况的。

接下来我们来说一下归并排序的实现过程

归并排序具体实现过程:

  1. 首先我们先确定一个 key,把数组分为左右两个区间进行排序,此处通常都是选为中间区域,key = (left + right)/ 2 。
  2. 左右区间在进行排序,怎么排序:同样是在左区间找一个key,使得左区间又分为左右两个区间,同理右区间也是。我们发现都是先进行划分区间,一直划分。
  3. 划分直到左右区间内只有一个元素或者区间不存在,假设只有一个元素,那么不用排序天然就是有序的,此时要做的就是归并排序的核心操作:把左右区间的两个元素继续有序的合并,合并成一个有序的区间向上返回。
  4. 也就是说我们一直划分到划分不了区间为止,然后开始合并变成有序向上返回,越往上返回此时的数组也就是越有序的,直到最后的两个左右有序区间进行合并,那么最后这个区间就是有序的了,也就是排好序了。

看下面的分析图可能会更容易理解:

在这里插入图片描述

  • 也就是说我们想先排整体有序,以左区间为例:得先让左区间进行有序,让左区间有序就得让左区间的左右区间进行有序,然后合并,
  • 就这样一直划分下去,直到划分不了区间。我们发现这个过程其实有点像是二叉树的后序遍历,左右区间有序之后(类比先遍历左右子树),合并之后整体才会有序(遍历根节点)。
  • 接下来,后面会写到快排,快排则是相反的一个过程,类似于二叉树的前序遍历,后面很快会写到。

2. 代码

看下面的代码对照着上面的流程解析可能会更加的清楚。

   int[] tmp;  // 用一个全局的辅助数组
   public int[] sortArray(int[] nums) {
      int n = nums.length;
      tmp = new int[n];
      mergeSort(0, n - 1, nums);
      return nums;
   }

   private void mergeSort(int left, int right, int[] nums) {
       // 区间内只有一个元素或者元素不存在, 那么直接返回没有排序的必要
       if(left >= right) return;

       // 归并排序的主逻辑过程
       // 1. 找到一个中间位置
       int mid = (left + right) / 2;
       // [left, mid] [mid + 1, right] 划分为两个区间

       // 2. 先进行左右两边的分割,排个序。相信这两个函数能完成左右区间的排序
       // (递归不用太关注于具体的展开过程)
       mergeSort(left, mid, nums);
       mergeSort(mid + 1, right, nums);
       
       // 3. 两边排好序之后, 合并有序数组。(核心操作)
       // cur1 遍历 [left, mid] 这个区间
       // cur2 遍历 [mid + 1, right] 这个区间
       // i 遍历 tmp 辅助数组
       int cur1 = left, cur2 = mid + 1, i = 0;
       // while(cur1 <= left && cur2 <= right) {
       while(cur1 <= mid && cur2 <= right) {
           tmp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
       }
       
       // 4.处理cur1 或者 cur2 还没有遍历完的数组
       while(cur1 <= mid) tmp[i++] = nums[cur1++];
       while(cur2 <= right) tmp[i++] = nums[cur2++];
       
       // 5. 把辅助数组中的数组拷贝到原数组中
       // j 遍历的是原数组, nums中的 【left, right】 这个区间
       for (int j = left; j <= right; j++) {
           // tmp 要从 0 开始遍历
           nums[j] = tmp[j - left];
       }
   } 

用到了一个全局的辅助数组,而不是每次进入递归都要重新new一下,这样写起来会方便一点。

🎗️🎗️🎗️ 好啦,到这里有关本题的分享就没了,如果感觉做的还不错的话可以点个赞,关注一下,你的支持就是我继续下去的动力,我们下期再见,拜了个拜~ ☆*: .。. o(≧▽≦)o .。.:*☆

  • 45
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值