力扣打卡 DAY 02

今天打卡力扣题库第四题,两个正序数组的中位数

题目如下:

在最开始看到这个题目的时候,我的想法如下:

解法一:

将两个数组使用快速排序进行排序,然后分别在奇偶两种情况下找中位数。

随后得到我的代码如下,但是此代码有个不足就是它的时间复杂度为O(m+n)超过了时间限制,而且同样需要O(m+n)的空间复杂度。

代码如下

class Solution {
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
// 寻找两个数组组合而成的数组的中位数并且要求时间复杂度不超过O(log(m+n)),那么首先要做的就是将两个数组进行合并,使用快速排序就可以得到,然后将得到的数组进行对半砍就可以得到最后的结果
    int m=nums1.length;
    int n=nums2.length;
    int middle=0;
    int []nums3=new int[m+n]; 
    for(int i=0;i<m;i++){
        nums3[i]=nums1[i];
    }
    for(int j=0;j<n;j++){
        nums3[j+m]=nums2[j];
    }
    // int i=0;
    // int j=nums3.length-1;
    quickSort(0,nums3.length-1,nums3);
    if(nums3.length%2==0){
        middle=(nums3[nums3.length/2]+nums3[nums3.length/2+1])/2;
        // System.out.println("中位数为:"+middle);
        return middle;
    }
    else{
        middle=nums3[nums3.length/2];
        // System.out.println("中位数为:"+middle);
        return middle;
    }
    //调用快速排序

    }
    public void quickSort(int l,int r,int []s)
    {
        int t;
        int left=l;
        int right=r;
        int temp=s[l];
        while (left < right)
        {
            while(left < right && s[right] >= temp) // 从右向左找第一个小于x的数
                right--;  
                       
            while(left < right && s[left] < temp) // 从左向右找第一个大于等于x的数
                left++;  
           if(left<right){
               t=s[left];
               s[left]=s[right];
               s[right]=t;
           }
        }
        s[l] = s[left];
        s[left]=temp;
        quickSort(l,left-1,s); // 递归调用 
        quickSort(left+1,r,s);

    }
}

解法二:

另外一个想法就是我们已知两个数组的分别长度为多长,我们可以在进行排序的时候添加一个标记,在排序的同时就会找到对应的中位数的值。这里就不再赘述。

解法三:

这个方法是在力扣上学习而来

解题思路如下:

在拿到这个题目之后我们想到的要解决的问题是:
1. 奇偶个数据的中位数的求解不同

2. 采用什么样的方法可以寻找到中位数

3. 如何让时间达到O(log(m+n))

首先我们解决第一个问题,我们如何得到不同个数据的中位数?

我们可以想到的就是,偶数个数据的中位数就是中间两个数的和除以2;奇数个数据的中位数就是最中间那个数,具体的如何在不将两个数据进行合并的情况下找到中位数的方法我们在后面讲解如何寻找到中位数的时候进行讲解。

然后解决第二个问题,采用什么样的方法才可以寻找到中位数?

其实在上面的方法一中我们有提到一种讲解方法,那就是先将两个数组进行合并,然后通过奇偶个数的方法进行寻找中位数。显然使用那种方法得不到我们想要的时间复杂度,那么我们可以采用下面的方法。

我们定义一个数据m用来表示数组nums1的长度,定义数据n用来表示nums2的长度,定义一个变量totalLeft来表示中位数的位置。

在讨论一个数组序列的中位数的时候我们需要采用奇偶的方法来进行判定,对于奇数我们的中位数的位置为 totalLeft=(m+n+1)/2 【你可能会好奇,为什么要在这个基础上加一,因为我们这里人为规定,假设两个数组进行合并了,然后将整个的数组进行划分的时候我们让左边的数据比右边的数据多一个或者两边数量一样多,这也是我们存储中间数据的变量的名字取为totalLeft的原因】

对于偶数,我们中位数的位置为totalLeft=(m+n)/2,这里为了保证减少代码的数量以及方便,我们也将偶数个数据的中位数位置写为totalLeft=(m+n+1)/2,你会发现并没有什么影响,因为int类型是下取整的,所以位置并不会改变。

因此,totalLeft的值为(m+n+1)/2

在有了上面的初步定义之后,我们需要做的就是如何可以找到具体的中位数。我们最开始在知道中位数需要的个数之后,我们取第一个数组的一半,第二个数组的一半。

定义一个数据i表示在第一个数组中取的数据的个数,j表示在第二个数组中取的数据的个数,即i=0+(m-0+1)/2;j=totalLeft-i

然后因为题目已给我们这两个数组是正序数组,所以我们只需要保证以下条件 

                   nums1[i - 1] <= nums2[j] && nums2[j - 1] <= nums1[i]

对于不满足条件的情况下我们进行调整,如果 nums1[i - 1] > nums2[j],那么说明这个数据并不满足,需要将i向左移,同时由于j=totalLeft-i,所以j也会相应的向右移动;同理,如果nums2[j - 1] > nums1[i],我们就需要将i向右移动。

最后我们就可以得到在两个数组中的位置,当然我们在讨论两个数组中的位置的时候还需要排除边界条件

① int nums1LeftMax =nums1[i - 1];  这个时候我们需要讨论边界越界的问题,即当i=0的时候,nums[i-1]就没有意义,所以应该修改为 int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];

同理如下:
② int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
③ int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
④ int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];

最后呢我们只需要讨论奇偶的情况将两个数据加入就可以了

为奇数的时候

Math.max(nums1LeftMax, nums2LeftMax);

为偶数的时候

(double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) / 2;

在解决了上面的问题之后就达到了要求的时间复杂度,问题得到解决!!

代码如下:

public class Solution {

    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if (nums1.length > nums2.length) {
            int[] temp = nums1;
            nums1 = nums2;
            nums2 = temp;
        }

        int m = nums1.length;
        int n = nums2.length;

        // 分割线左边的所有元素需要满足的个数 m + (n - m + 1) / 2;
        int totalLeft = (m + n + 1) / 2;

        // 在 nums1 的区间 [0, m] 里查找恰当的分割线,
        // 使得 nums1[i - 1] <= nums2[j] && nums2[j - 1] <= nums1[i]
        int left = 0;
        int right = m;

        while (left < right) {
            int i = left + (right - left + 1) / 2;
            int j = totalLeft - i;
            if (nums1[i - 1] > nums2[j]) {
                // 下一轮搜索的区间 [left, i - 1]
                right = i - 1;
            } else {
                // 下一轮搜索的区间 [i, right]
                left = i;
            }
        }

        int i = left;
        int j = totalLeft - i;
        int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
        int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
        int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
        int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];

        if (((m + n) % 2) == 1) {
            return Math.max(nums1LeftMax, nums2LeftMax);
        } else {
            return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) / 2;
        }
    }
}

好,今天就记录到这里,如果有描述不正确的地方希望大家纠正!

【 fromwxy.2022.1.14  】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值