寻找两个有序数组的中位数(放松边界条件的朴素解法)

## 解题思路
这道题目初看挺简单的,一早就能想到用二分查找解法,但是要找到一个清晰的边界条件,实在让人痛苦
在测试用例中,总有那么一个直击代码的软肋,让程序的结果与正确答案失之交臂。
网上看到现成的解法使用各种技巧,从结果上,并不容易理解,这里给出一个朴素的解法

## 常规思路
有一个都能想到的基本思路是,对数组进行分割,然后,丢弃不包含中值的部分,问题出在一些边界条件上,分割的时候意外的把中值给割出去了,这里的解决思路就是放松分割的边界,避免误割,后面再谈

对于数组1和数组2,长度分别为m和n
分别设置(l1,r1)和(l2,r2),分别表示目前数组中保留的部分,其余为割出去的部分
pos = (l+r)/2 表示数组保留部分的中值(只是近似)

初始如下

 

1.png

更新l和r

 

2.png

相信大部分人都能想到这一点,但是这样做存在风险的,这个后面再谈
停止条件
想象一下,我们把从两个数组合并到一个有序的全数数组里面看

 

3.png

pivot1和pivot2就是要找的中值位置,m+n为奇数时重合,偶数的时候不同
L代表了保留下的数据在全数据数组的起始位置,R代表结束位置,可以根据(l1,l2,r1,r2)计算得到
那么只要停止条件设置为 L==pivot1或者 R == pivot2

## 放宽丢弃条件
目标点pivot1和pivot2
上面几步对边界处理有问题,极端的情况下面会将中值数据割出去

所有在每次分割的时候,放松边界条件,保证不把中值数据提出去
下面是几处边界处理的方法

  1. l和r的更新,更新后,增大保留的部分,l = l -1, r = r+1;
  2. L 或 R只有靠近pivot就可以停止切割了,从全数组的角度,向右或者向左搜索目标点
  3. 搜索时,为了确保中值数据没有被割出去,适当扩大保留数据范围

 

R靠近pivot2时,从右边开始找,依次找两个数组中,保留数据里最大的数据,直到找到pivot1,pivot2对应的数据

类似地,L靠近pivot2时,从左边找保留数据里面最小的

下面的代码没什么价值,纯属抛砖引玉,大家完全可以自己实现一个。

 

/*
 * @lc app=leetcode.cn id=4 lang=cpp
 *
 * [4] 寻找两个正序数组的中位数
 */
#include<vector>
using namespace std;
// @lc code=start
#define MIDDLE_RESULT(X,LEN) (LEN)%2==0?((double)X[(LEN)/2]+X[(LEN)/2-1])/2:X[(LEN)/2]
//从右开始搜索,找到全数组里面位于target位置的点
int  RightDriectSearch(vector<int> &nums1, int r1,vector<int> &nums2,int r2, int target)
{
     int m = nums1.size();int n = nums2.size();
//扩大搜索边界
     for(int i = 0;i<2;i++){
         if(r1<(m-1)) r1++;
         if(r2<(n-1)) r2++;
     }
     int l = (m+n-1)-(m-1-r1)-(n-r2-1);int val1=0,val2=0;
     while(l>=target){
         val1 = r1<0?INT32_MIN:nums1[r1];
         val2 = r2<0?INT32_MIN:nums2[r2];
         (val1>val2)?--r1:--r2;
         l--;
     }
     return val2>val1?val2:val1;
}
//从左开始搜索,找到全数组里面位于target位置的点
int LeftDriectSearch(vector<int> &nums1, int l1,vector<int> &nums2,int l2, int target)
{
     int m = nums1.size();int n = nums2.size();
//扩大搜索边界
     for(int i = 0;i<2;i++){
         if(l1>0) l1--;
         if(l2>0) l2--;
     }
     int l = l1+l2;int val1=0,val2=0; 
     while(l<=target){
         val1 = l1>=m?INT32_MAX:nums1[l1];
         val2 = l2>=n?INT32_MAX:nums2[l2];
         (val1<val2)?l1++:l2++;
         l++;
     }
     return val2<val1?val2:val1;
}
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    int m = nums1.size();
    int n = nums2.size();
    int l1 = 0, l2 = 0, r1 = m - 1, r2 = n - 1;
//存在一个数组为空,直接计算结果
    if (m == 0)
        return MIDDLE_RESULT(nums2, n);
    if (n == 0)
        return MIDDLE_RESULT(nums1, m);
    int pivot1 = (m+n-1)/2;
    int pivot2 = (m+n)/2;
    int val1,val2;
    while (1)
    {
        int R = (m+n-1)-(m-1-r1)-(n-r2-1);
      //靠近pivot1
        if ((l1 + l2 + 3) >= pivot1)
        {
           val1 = LeftDriectSearch(nums1,l1,nums2,l2,(m+n-1)/2);
           val2 = LeftDriectSearch(nums1,l1,nums2,l2,(m+n)/2);
          return (val1+val2)/2.0;
        }else if((R-3)<=pivot2){//靠近pivot2
            val1 = RightDriectSearch(nums1,r1,nums2,r2,(m+n-1)/2);
           val2 = RightDriectSearch(nums1,r1,nums2,r2,(m+n)/2);
          return (val1+val2)/2.0;
        }
//更新保留数据的边界
        int pos1 = (l1 + r1) / 2;
        int pos2 = (l2 + r2) / 2;
        int mid1 = nums1[pos1];
        int mid2 = nums2[pos2];
        if (mid1 > mid2){
            r1 = pos1<=(m-1)?pos1+1:pos1; 
            l2 = pos2>0?pos2-1:pos2; 
        }
        else{
            l1 = pos1>0?pos1-1:pos1;
            r2 = pos2<(n-1)?pos2+1:pos2;
        }
    }     
    return 0.0;
    }
};
// @lc code=end


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值