[leetcode 日记]寻找两个有序数组的中位数 - 分治&搜索

在这里插入图片描述
今天总结一下前几天写的这一道搜索相关的题目:
自己最开始的思路:
把这两个数组(假设A,B)分割,分割成
A =>
A[0] ~ A[i] A[i] ~ A[n - i] 且 (0 <= i < n, n == A.size() )
B =>
B[0] ~ A[j] A[j] ~ A[n - j] 且 (0 <= j < m, m == B.size() )
那么如何求解 i, j 就是这道题目的关键,观察可得

结论1: A 和 B的前半部分和后半部分的数目相等即

(i - 0 + 1) + (j - 0 + 1) == (n - i - 1) + (m - j - 1)
这样计算的前提是i j 不能为0,于是需要换一种思路

i,j不再表示下表而是前面有几个数字,
可以这么看 : i = 0 表示A中取0个元素, i = n 表示A中取所有的数字(同理可得j)
于是这个关系式为

i + j = n - i + m - j

但是这个地方在处理的时候有一个需要注意的地方

j = (m + n + 1) / 2 - i

  • 第一点:n必须小于m,以保证 j 大于等于0,且对小的数组二分会加快计算
  • 第二点: 这个 + 1很关键,保证了当m+n为奇数的时候,中位数落在左边,例如 m+n == 7 ,那么中位数在4这个位置,但是如果不加1,i+j为3,中位数会落在右边; 同时m+n为偶数的时候不会影响j的计算

结论2,对i进行二分,当 A[i] < B[j-1] || B[j] < A[i-1] 时说明都没有满足条件,前者说明i小了应该往后移增大, 后者说明i大了应该往前移,且这两个情况不会同时发生,证明很简单,反证法,假设都成立

因为 A[i] < B[j -1] 且 B[j] > B[j -1] 所以 A[i] < B[j]
又因为B[j] < A[j -1] 所以可得A[j] < A[j -1] 与条件矛盾,不成立。

下面就可以coding了, 代码我写的有点冗余没有怎么优化,对于特殊的情况我是分开写的,有些是可以合并的。一会我会对比官网实例code

 double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        //the size of n1 > the size of n2
        vector<int> &n1 = nums1.size() > nums2.size() ? nums1 : nums2;
        vector<int> &m1 = nums1.size() > nums2.size() ? nums2 : nums1;
        //n,m : size of two vectors
        int n = n1.size(), m = m1.size();
        //i -> m1, j -> n1
        int i = 0, j = 0, k = (n + m + 1) / 2;
        int iMin = 0, iMax = m;
		/*
		** 以上是在做准备为求解代码做准备,下面就可以根据i的取值
		** 范围对i进行二分查找了
		*/
        while(iMin <= iMax){
            i = iMin + (iMax - iMin) / 2;
            j = k - i;
            //特殊情况边界的处理
            if(i == 0)
                if(m == 0 || m1[i] >= n1[j -1])
                    break;
                else{
                    iMin = i + 1;
                    continue;
                }            
            
            if(i == m)
                if( n1[j] >= m1[i-1])
                    break;
                else{
                    iMax = i - 1;
                    continue;
                }
            
            if(j == n)
                if(m1[i] >= n1[j -1])
                    break;
                else{
                    iMin = i + 1;
                    continue; 
                } 
              
            if(j == 0)
                if(n1[j] >= m1[i - 1])
                    break;
                else{
                    iMax = i - 1;
                    continue;   
                }
           //正常情况下的二分
            if(m1[i] >= n1[j - 1] && n1[j] >= m1[i - 1]){
                break;
            }else{
                if(m1[i] < n1[j - 1]){
                    iMin = i+1;
                }else{
                    iMax = i-1;
                }
            }
        }
      //下面的代码是在一直i/j的情况下求解中位数,分了两种情况一个是
      //偶数一个是奇数,因为奇数的时候是一个数,偶数是两个数,且处理特殊情况(这部分代码可以和上面的合并一些)
        double ret = 0;
        if( (n + m) % 2 == 0){
            int min, max;
            if(i != 0 && j != 0)
                min = m1[i - 1] > n1[j -1] ? m1[i -1] : n1[j -1];
            else{
                if(i == 0) min = n1[j - 1];
                if(j == 0) min = m1[i - 1];
            }
                
            if(i != m && j != n)
                max = m1[i] < n1[j] ? m1[i] : n1[j];
            else{
                if(i == m) max = n1[j];
                if(j == n) max = m1[i];
            }
       
            ret = (double)(max + min) / 2;
        }else{
            if(i != 0 && j != 0)
                ret = m1[i - 1] > n1[j -1] ? m1[i - 1] : n1[j -1];
            else{
                if (i == 0) ret = n1[j -1];
                if (j == 0) ret = m1[i -1];
            }
        }
        return ret;
    }

有时间我会对代码进行优化。

对比官方代码

 public double findMedianSortedArrays(int[] A, int[] B) {
        int m = A.length;
        int n = B.length;
        if (m > n) { // to ensure m<=n
            int[] temp = A; A = B; B = temp;
            int tmp = m; m = n; n = tmp;
        }
        int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
        while (iMin <= iMax) {
            int i = (iMin + iMax) / 2;
            int j = halfLen - i;
            //这里有j 必不等于 0,因为i的数组是小数组
			//如上面说的B[j -1 ] > A[i] 和 A[i-1] >B[j]不会同时成立
			//来调整i的位置
			//i < iMax & i > iMin只是用来限制相等的情况,如果相等会在下面的情况处理
            if (i < iMax && B[j-1] > A[i]){
                iMin = i + 1; // i is too small
            }
            else if (i > iMin && A[i-1] > B[j]) {
                iMax = i - 1; // i is too big
            }
            else { // i is perfect
              	//求左边最大 和 右边最小
                int maxLeft = 0;
                if (i == 0) { maxLeft = B[j-1]; }
                else if (j == 0) { maxLeft = A[i-1]; }
                else { maxLeft = Math.max(A[i-1], B[j-1]); }
                if ( (m + n) % 2 == 1 ) { return maxLeft; }//奇数时直接return

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

                return (maxLeft + minRight) / 2.0;
            }
        }
        return 0.0;
    }

总结:
其实这道题目的方法并不是很复杂,就是二分求解,难就难在二分时的计算推导,特别是边界情况的处理,如果一开始分不清,也可以像我一样把所有特殊情况分开处理,虽然冗余,但是在最短时间内可以完成逻辑的实现。就好比数学的分情况讨论,在合并的过程,你不合并也不算错。

参考: https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-shu-b/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值