2020-05-24 LeetCode 4 寻找两个正序数组的中位数 C

题目:

 给定两个大小为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。

 请你找出这两个正序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

 你可以假设 nums1 和 nums2 不会同时为空。

示例1:

nums1 = [1, 3]
nums2 = [2]

则中位数是 2.0

示例2

nums1 = [1, 2]
nums2 = [3, 4]

则中位数是 (2 + 3)/2 = 2.5

 今天又是道硬菜,这个题我很清楚的记得去年暑假夏令营的时候做过,来来去去折腾了两天才推算明白。现在解题思路还在,题目要求算法的时间复杂度为 O(log(m + n)),拍拍脑子就知道肯定是二分法,然后需要把两个数组A,B一起二分,也就是用两个下标来把两个数组分为四部分,然后左边的两个作为一个整体n1,右边的两个作为另一个整体n2。这里如果总数是偶数,那么n1和n2的长度是相等的,如果是奇数,那么n1长度应该比n2大1,结合C语言向下取整的int运算特性,得到推导式
i = ( i M i n + i M a x ) / 2 ; j = ( m + n + 1 ) / 2 − i ; i = (iMin + iMax) / 2; j = (m + n + 1) / 2 - i; i=(iMin+iMax)/2;j=(m+n+1)/2i;
 其中iMin和iMax二分法的上下界,m、n是两个输入数组A,B的长度,i和j则是两个输入数组的各自的划分点。

 最终要得到划分好的数组,应该要保证 max ( n1) <= min ( n2),因为 A 数组和 B 数组是有序的,所以只需要保证 B [ j - 1 ] < = A [ i ] 和 A [ i - 1 ] <= B [ j ] ,所以分两种情况:

  • B [ j - 1 ] > A [ i ],并且为了不越界,要保证 j != 0,i != m

    此时需要增加 i ,然后 j = ( m + n + B1) / 2 - i,j 自然会减少。

  • A [ i - 1 ] > B [ j ] ,并且为了不越界,要保证 i != 0,j != n

    与上边相反,此时需要增加j,即减少i

 按上述情况和二分法规则更新i和j,最后得到划分好的数组,左半部分小于右半部分,且长度满足偶数相等、奇数左半比右半大1的规则,按照中位数定义区分奇偶数来处理即可。

 之前是用Java写的所以这次打算用C写,结果按照之前的复现思路来写C,一直报heap-buffer-overflow的错误,本地调试重点关注了下访问数组的下标,发现二分查找的时候按照两边均匀的思想很容易导致其中一个下标变成负数,本地是不会报错的,LeetCode会提示内存溢出。

 尝试把官方的题解改成C也会报错,按照那种遍历过程储存结果的复现思路硬是没给写出来,几个热门题解也没找到按照这种思路的简洁C复现,用java和C++改成C也报heap-buffer-overflow,于是就看了下几个热门题解另外的复现思路,挑了个我觉得比较清晰的改成了C复现。

#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))


double findMedianSortedArrays(int* A, int m, int* B, int n){
        if (m > n) { 
            return findMedianSortedArrays(B,n,A,m); // 保证 m <= n
        }
        int iMin = 0, iMax = m;
        int i,j;
        while (iMin <= iMax) {
            i = (iMin + iMax) / 2;
            j = (m + n + 1) / 2 - i;
            //j=0即B全部划分在右边,i=m即A全部划分到左边,是特殊的边界情况,已经满足退出条件
            if (j != 0 && i != m && B[j-1] > A[i]) // i 需要增大
                iMin = i + 1; 
            //i=0即A全部划分在右边,j=n即B全部划分到左边,是特殊的边界情况,已经满足退出条件
            else if (i != 0 && j != n && A[i-1] > B[j]) // i 需要减小
                iMax = i - 1; 
            else  //左边已经全部小于右边或者是上述的边界情况,达到退出要求
                break;
        }
        //并且将边界条件列出来单独考虑
        int maxLeft = 0;
        if (i == 0)         maxLeft = B[j-1]; 
        else if (j == 0)    maxLeft = A[i-1];
        else   maxLeft = max(A[i-1], B[j-1]); 
        if ( (m + n) % 2 == 1 ) 
         return maxLeft;  // 奇数的话中位数在左半部分最右,不需要考虑右半部分

        int minRight = 0;
        if (i == m) minRight = B[j]; 
        else if (j == n) { minRight = A[i]; }
        else { minRight = min(B[j], A[i]); }

        return (maxLeft + minRight) / 2.0; //偶数结果为左最大与右最小的平均

}

运行结果

2020-05-24 LeetCode 4 寻找两个正序数组的中位数 C_第1张图片

 题目思路想起来就花了十几分钟,自己之前写的Java解法也是比较乱,一点都不简洁,就想着再写个C复现,但是复现用了一上午,还是太菜了

参考:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/

你可能感兴趣的:(C,LeetCode,leetcode,二分法,算法)