【每日一题】LeetCode. 4. 寻找两个正序数组的中位数

每日一题,防止痴呆 = =

一、题目大意

给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。
请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
你可以假设 nums1 和 nums2 不会同时为空。
【每日一题】LeetCode. 4. 寻找两个正序数组的中位数_第1张图片
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays

二、题目思路以及AC代码

前两天要搞毕设的查重降重,无奈拖了两天,今天就搞两道吧 = =。

这道题有两个思路,感觉第一个思路常规一些,也比较好想,第二个思路的话,看官解是懂了,但我觉得在做题的时候可能很难想到,看看就好。

思路一:数组中第k小的数

既然题目要求在两个数组中求中位数,设两个数组长度分别为size_1和size_2,那中位数无非就是在总长度是奇数的时候,是第(size_1 + size_2) / 2 + 1个数;在总长度是偶数的时候,是第(size_1 + size_2) / 2 + 1个数和第(size_1 + size_2) / 2个数的平均值。

那么我们只要找到求解两个数组中第k小的数的方法,就可以求解该问题了。而又由于题目要求O(log(m+n))的复杂度,那么不难想到二分。

想要求解两个数组中第k小的数,设两个数组为A和B,我们可以每次比较A[k/2 - 1]和B[k/2 - 1]的大小,如果前者大,那么B[k/2 - 1]则最大是第k-1个数,不可能是第k个,所以B[k/2 - 1]以及其之前的数都可以排除,然后将排除了多少个数,需要同时让k减去多少,然后再在新的两个数组上求第k小的数,直到k为1,那么此时两个数组的第一个数,哪一个小,就是哪个。

当然,如果中途某一个数组的元素全部被排除,则直接返回另一个数组第k小的数即可(这里k不再是最开始的k,而是更新之后的)。还有就是,如果下标k/2 - 1超过了数组长度,那直接比较数组最后一个元素即可。

思路二:划分

思路一感觉还是挺好理解的,由于题目提示时间复杂度,也比较好想到二分。而这个思路,我觉得就很难想到了。

思想就是,中位数其实就把原数组分为两个大小一致的数组(或者差一个),其中一个数组中的值全部大于另一个数组中的值。这样的话,我们要找中位数,其实就是找一个划分的位置就可以了,又由于总长度是知道的,所以在两个数组中的划分位置是有确定关系的。

设在两个数组中的划分位置为i和j,为了保证划分之后两部分的长度一致,则要满足 i + j = m - i + n - j, i + j = m - i + n - j + 1,前者是m+n为偶数的情况,后者是m+n为奇数,其中m是第一个数组的长度,n是第二个数组的长度。所以就可以统一用 i + j = (m + n + 1) / 2,这个式子来约束,因为除是整除,所以对于m+n是奇数还是偶数均适用,这就满足了第一个条件。

第二个条件是一部分的值必须均小于另一部分,那么只要一部分的最大值小于另一部分的最小值就可以了,又因为原数组是正序,那么其实只要保证 A[i-1] <= B[j] 并且 B[j-1] <= A[i]即可,上面的条件又可以转化为在[0, m]中找一个最大的i,满足A[i-1] <= B[j],我们把第二个条件转化为了找最大的i,因为最大的i,意味着i+1是不满足的,所以将i+1带入,得到A[i] > B[j-1],即是第二个条件。

综上所述,我们最后只要用二分法去查找[0, m],找到最大的i,满足A[i-1] <= B[j]即可,其中i和j满足上述提到的约束。至于为什么能用二分,是因为随着i的增大,A[i-1]增大,B[j]减小。

AC代码

这里就只给出了第一个思路的AC代码,第二个思路我也就是理解了一下 = =

int getKthElement(vector<int>& nums1, vector<int>& nums2, int k) {
    int size_1 = nums1.size();
    int size_2 = nums2.size();

    int p_1 = k/2 - 1;
    int p_2 = k/2 - 1;
    int l_1 = 0;
    int l_2 = 0;

    while (l_1 < size_1 && l_2 < size_2) {
        if (k == 1) return nums1[l_1]<nums2[l_2]?nums1[l_1]:nums2[l_2];

        p_1 = min(l_1 + k/2-1, size_1 - 1);
        p_2 = min(l_2 + k/2-1, size_2 - 1);

        if (nums1[p_1] >= nums2[p_2]) {
            k -= (p_2 - l_2 + 1);
            l_2 = p_2 + 1;
        }
        else {
            k -= (p_1 - l_1 + 1);
            l_1 = p_1 + 1;
        }
    }

    if (l_1 < size_1) return nums1[l_1 + k - 1];
    return nums2[l_2 + k - 1];
}

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int total_len = nums1.size() + nums2.size();
        if (total_len & 1) {
            return getKthElement(nums1, nums2, total_len/2 + 1);
        }
        else {
            return (getKthElement(nums1, nums2, total_len/2 + 1) + getKthElement(nums1, nums2, total_len/2)) / 2.0;
        }
    }
};

如果有问题,欢迎大家指正!!!

你可能感兴趣的:(每日一题)