一、题目描述

合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。你可以假设 nums1 的空间大小等于 m + n,这样它就有足够的空间保存来自 nums2 的元素。

示例 1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]

示例 2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]

二、思路分析

常规解析

  • 相信大家首先想到的就是两个数组融合然后对整体数组进行排序,不得不说这种方法是最快的因为我们有线程的api直接操作。撩撩几行代码就可以解决问题
  • 本题中说明了这两个数组是有序的 , num1前半部分是有序的,后半部分是为了存储num2数组准备的预留空间。
  • 根据题意我们也可以得出本题想让我们不接触第三方变量的情况下实现两个数组的合并。但是刚才说的整合后在合并这个就没有用到第三方

image-20210525193732712

  • 自始至终我们没有引入第三方变量。这种的确是最快最简单的实现方式

双指针

  • 双指针的意思就是定义两个指针(变量索引)指向两个数组,将两个数组看做是两个队列每次将两个队列中较小值去除塞到第三个数组中。最终第三个数组就是我们排序好的合并数组

image-20210525194330292

  • 之前也分析了本题题意是不想让我们借助第三方变量来实现的。那么如果不借助第三个数组我们是否可以利用双指针来实现合并呢?
  • 答案是可以的。因为题目中指出了num1就是合并后数组的长度。很明显就是让我们将num1作为合并后的数组输出的。
  • 我们可以逆向从num1数组开始将集合中最大数填入num1尾部,然后将其次大的填入倒数第二个位置,一次类推最后就会全部填入num1数组中。

image-20210526141857309

  • 最极端的情况是num2正好全部填充在num1后半部分,那么这样我们num1前半部分元素正好不需要移动。

image-20210526142031061

  • 只要num2没有全部填入后半部分,那么num1前半部分肯定有最大的值发生移动。那么发生移动的地方肯定优先是前半部分的末尾,那么num2就机会参与。
  • 所以不管什么情况,都不会发生num1数组中元素被占用的情况

三、AC 代码

public void merge(int[] nums1, int m, int[] nums2, int n) {
    int p1 = m - 1;
    int p2 = n - 1;
    for (int i = nums1.length - 1; i >= 0; i--) {
        int num1=Integer.MIN_VALUE, num2 = Integer.MIN_VALUE;
        if (p1 >= 0) {
            num1 = nums1[p1];
        }
        if (p2 >= 0) {
            num2 = nums2[p2];
        }
        if (num1 > num2) {
            p1--;
            nums1[i] = num1;
        } else {
            p2--;
            nums1[i] = num2;
        }
    }
}
  • 主要思路就是针对num1开始循环填充数据,每次都通过双指针从数组尾部获取最大值填入num1中。

image-20210526143554833

四、总结

  • 起初肯定是全部合并,然后排序这种做法简单直接也很粗暴。没有考虑数组的特性即题目的意思
  • 然后就是双指针合并,缺点是借助第三变量。
  • 转变下思路我们通过双指针逆向开始填充数据,彻底解决第三变量的问题

点赞+评论哦

版权声明:本文为zhangxinhua原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zhangxinhua/p/14984536.html