leetcode面试题 10.01. 合并排序的数组

这篇博客探讨了在Java中进行数组合并排序的多种方法,包括直接使用`Arrays.sort()`、优化的快速排序以及逆向双指针策略。文章详细分析了不同方法的实现原理和适用场景,并通过极端情况证明了逆向双指针方法的可行性,确保在不覆盖原有数组元素的情况下完成排序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

直接排序

直接使用Java已有的方法进行排序,这一招…大意了!

这题简单,就是个基本的排序,后面难题,可能这只是一小步,内个时候直接用排序算法比较合适,这个不合适。。

class Solution {
    public void merge(int[] A, int m, int[] B, int n) {
        for(int i = 0; i < n; i++){
            A[m+i] = B[i];
        }

        Arrays.sort(A);
    }
}

我们看看方法
在这里插入图片描述
这是一个更优的快速排序算法,对于某些数据,传统的快速排序可能会退化为二次方的事件复杂度,而此算法会更快一些,是O(n log(n))。

这种方式,顶多算应用黑箱子,没啥可以说的…

双指针 + 临时数组

我们可以对每个数组都添加一个索引指针,依次对比两个数组的值,让最小的进入临时数组,比较之后,把剩余的数直接放入临时数组,最后,临时数组再赋值给A。

leetcode的动图很好,直接放出来链接。

双指针

class Solution {
    public void merge(int[] A, int m, int[] B, int n) {
        int[] temp = new int[m+n];

        int i = 0; // array A pointer
        int j = 0; // array B pointer
        int k = 0; // array temp pointer
        while(i < m && j < n){
            if(A[i] < B[j]){
                temp[k++] = A[i++];
            } else if(A[i] >= B[j]){
                temp[k++] = B[j++];
            } 
        }

        // 剩余部分
        while(i < m){
            temp[k++] = A[i++];
        }

        while(j < n){
            temp[k++] = B[j++];
        }

        k = 0;
        i = 0;
        while(k < m + n){
            A[i++] = temp[k++];
        }
    }
}

这种方法,实现起来很简单,其实就是依次比较,但是开辟新的数组,再放回去,就很麻烦。

同样的思路,不同的实现方式

对于同样的逻辑,代码写起来其实也不一定一样的!我们看一看!

逆向双指针

所以,我们尝试一下在数组A直接动手脚,利用数组A中后半部分的剩余空间,看看可不可行!

关键点:A中的元素会不会被覆盖

在这里插入图片描述
我们可以,从两数组的后面开始,比较谁大,将大的放进A的最后面。

在这里插入图片描述
这里,我们列举极端例子

  1. B中的元素,全部比A中最大的还大

那么,B中的元素全部放入A的剩余空间中去,显然,没有问题!

在这里插入图片描述
2. B的元素,比A的元素都小

在这里插入图片描述
当然也能放进去。

最后,我们看看中间状态,也就是正常状态

在这里插入图片描述
很容易分析得出,不管怎么样,A中的剩余空间一定够用!

因此写代码实现:

class Solution {
    // 逆向双指针
    public void merge(int[] A, int m, int[] B, int n) {
        int ap = m - 1;
        int bp = n - 1;
        int final_pointer = m + n - 1;
        while(ap >= 0 && bp >= 0){
            if(A[ap] > B[bp]){
                A[final_pointer--] = A[ap--];
            } else{
                A[final_pointer--] = B[bp--];
            }
        }

        // 若A剩余,就不用管了;若B剩余,都扔进去
        while(bp >= 0){
            A[final_pointer--] = B[bp--];
        }
    }
}

或者可以

class Solution {
    // 逆向双指针
    public void merge(int[] A, int m, int[] B, int n) {
        int ap = m - 1;
        int bp = n - 1;
        int final_pointer = m + n - 1;
        while(bp >= 0){
            // 置换A的元素
            if(ap >= 0 && A[ap] > B[bp]){   // 注意顺序不要写反!
                A[final_pointer--] = A[ap--];
            } else {
            // 置换B的元素
                A[final_pointer--] = B[bp--];
            }
        }
    }
}

后者写法简洁一些,前者写法更加明了,是继承解法二的思想。

我们用严格的方式再说明一下A不会被覆盖的问题

我们只需要满足

  • A中可用的位置 >= (A已经置换的数量 + B已经置换的数量)

因此,我们分别表示一下

在这里插入图片描述

我们要求的是白格子的数量,是

  • n - (pb + 1) = n - pb - 1
  • pb是索引,从0开始,因此,橙色一共pb + 1个,总数是n,减一下就行了

A同理

  • m - pa - 1

在这里插入图片描述

当前数组A可插入数量应该是m + n - pa - 1

我们求的这三个数分别是(在同一时刻

  • f1:A扔到A后面去的
  • f2:B扔到A后面去的
  • f3:A后面总共可以插入的元素(不被覆盖的情况下

我们只需要验证f3 >= f1 + f2恒成立即可!

=> pa >= -1显然恒成立。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XV_

感谢您的认可,我会继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值