T4 寻找两个正序数组的中位数

一、题目描述

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

二、暴力做法

对两个数组统计个数,计算出中位数的位置,两个迭代器(类似指针)对两个数组进行遍历,并记录步数。

double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int size1=nums1.size();
        int size2=nums2.size();//获取两个容器的大小
        int index1=0,index2=0;//记录中位数索引位置 ,index1记录大的值
        int mid1=0,mid2=0;   //分别记录中位数
        int isD=0;//是否偶数个
        if((size1+size2)%2==0){    //偶数个
            isD=1;
            index2=(size1+size2)/2;
            index1=index2+1;  //index1更大
        }else index1=(size1+size2)/2+1;    //奇数个
        vector<int>::iterator it1=nums1.begin();  //第一个迭代器
        vector<int>::iterator it2=nums2.begin();
        int pace1=0,pace2=0;
        int pace3=0;//记录总步数
        while(pace1<size1&&pace2<size2&&pace3<index1){
            while(*it1<=*it2&&pace1<size1&&pace3<index1){//小的往前走
                pace1++;
                pace3++;
                if(pace3==(index1-1))mid2=*it1;
                if(pace3==index1) mid1=*it1;
                it1++;
                if(pace1==size1){it1--;}  //如果是数组最后一个,迭代器前移,防止越界。
            }
            while(*it1>=*it2&&pace2<size2&&pace3<index1){
                        pace2++;
                        pace3++;
                    if(pace3==index1-1)mid2=*it2;
                    if(pace3==index1) mid1=*it2;
                    it2++;
                if(pace2==size2)it2--;
                }
        }

        while(pace1<size1&&pace3<index1){  //容器1还有剩余
            pace1++;
            pace3++;
            if(pace3==index1-1)mid2=*it1;
            if(pace3==index1) mid1=*it1;
            it1++;
            if(pace1==size1){it1--;}
        }
        while(pace2<size2&&pace3<index1){  //容器1还有剩余
            pace2++;
            pace3++;
            if(pace3==index1-1)mid2=*it2;
            if(pace3==index1) mid1=*it2;
            it2++;
            if(pace2==size2){it2--;}
        }
        double res=0; //中位数
        if(isD){   //双数
          return (mid1+mid2)/2.0;
        }else{
          return mid1;
        }
}

三、问题与优化

1.vector 的size和capacity并不相等,容易出现迭代器越界
  对每个数组进行步数计算,防止出现越界
2.在下面例子中,循环条件pace3<index1。而在循环体中对index1的值进行更改,同样会导致越界
  采用 mid_value来记录循环体中的值
 while(pace1<size1&&pace2<size2&&pace3<index1){
            while(*it1<=*it2&&pace1<size1&&pace3<index1){//小的往前走
                if(pace1==size1){it1--;}
                it1++;
                pace1++;
                pace3++;
                if(pace3==(index1-1))index2=*(it1-1);
                if(pace3==index1) index1=*(it1-1);
            }

四、二分查找,递归

题解的方法三

因为题目要求时间复杂度O(log(m+n)

很容易想到二分法。

找中位数,本质找第k小的数,又因为两个数组有序,每次比较两个数列中[k/2]位置的数据,因为第k个数一定大于或者等于[k/2],所以大胆排除较小的前[k/2]个数据。

此时k=k-淘汰的数。继续循环。

利用递归,整合奇数与偶数,不用迭代器,采用数组的形式输出,len1始终指向短的,且在改变

这些思路确实厉害

class Solution {
public:
    double getKey(vector<int>& nums1, int start1,int end1,vector<int>& nums2,int start2,int end2,int k);
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
    int n=nums1.size();
    int m=nums2.size();
    int left=(n+m+1)/2;    //2
    int right=(n+m+2)/2;   //将奇偶合并同样都是求两次k   3
    int left_value=getKey(nums1,0,n-1,nums2,0,m-1,left);
    int right_value=getKey(nums1,0,n-1,nums2,0,m-1,right);
    return (right_value+left_value)/2.0;
    }
};
double Solution::getKey(vector<int>& nums1, int start1,int end1,vector<int>& nums2,int start2,int end2,int k){       //递归
    //计算数组长度
    int len1=end1-start1+1;
    int len2=end2-start2+1;
    //终止条件
    /*
     * [1,2][-1,3]会出现偶数个中位数都在同一个数组中   if(k==0)  return nums2[start+k] 就不成立
     * */
    //想办法让长度上nums1<nums2
    if(len1>len2){
        return getKey(nums2,start2,end2,nums1,start1,end1,k);
    }
    if(len1==0) return nums2[start2+k-1];  //短的已经遍历完成
    if(k==1){                        //倒数第一小,返回小的值
        if(nums1[start1]<nums2[start2]) return nums1[start1];
        else return nums2[start2];
    }
    //递归主体,要判断两数组中[k/2]位置值的大小关系
    //小心越界,当k/2>len时 让其指向最后一位数
    int i=start1+std::min(len1,k/2)-1;
    int j=start2+std::min(len2,k/2)-1;
    if(nums1[i]<nums2[j]){   //如果数组一中更小,丢弃小的
        return getKey(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1));   //新的k等于 原来的k减去被抛弃的小的数,并不等于k/2,因为数组不一定够长
    }else{
        return getKey(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1));
    }
}

我自己写的,很多问题,最终也没跑通,指针不能越界,等等问题。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int size1=nums1.size();
        int size2=nums2.size();//获取两个容器的大小
        int index1=0,index2=0;//记录中位数索引位置 ,index1记录大的值
        int isD=0;//是否偶数个
        if((size1+size2)%2==0){    //偶数个
            isD=1;
            index2=(size1+size2)/2;
            index1=index2+1;  //index1更大
        }else index1=(size1+size2)/2;    //奇数个
        int pace1=0,pace2=0;


        int k=size1+size2;   //中位数的一半
                if(size1==0||size2==0){    //特护情况,有一个数组为空
          if(size1==0){     //数组1为空
              if(isD){
                  return (nums2[index1-1]+nums2[index2-1])/2.0;
              }else return nums2[index1-1];
          }else{   //数组二为空
              if(isD){
                  return (nums1[index1-1]+nums1[index2-1])/2.0;
              }else return nums1[index1-1];
          }
        }
        while(k!=1){
            k=k/2;
            if(size1>=k&&size2>=k){    //存在足够的长度
               if(nums1[pace1+k-1]<=nums2[pace2+k-1]){ //小的往前移动
                 if(pace1+k<size1)
                    pace1+=k;
               }else{
                   if(pace2+k<size2)
                   pace2+=k;
               }
            }
        }
        if(isD){  //偶
            return (nums1[pace1]+nums2[pace2])/2.0;
        }else{     //奇数
            if(nums1[pace1]<=nums2[pace2]){return nums1[pace1];
            }else{return nums2[pace2];}
        }
    }
};

五、划分数组的方法

这就是官方给出的方法

a[99]和b[1]

中位数50,51如果在a中进行j 的调整,显然当j==1时 ,i会越界,所以要在短的数组中进行调整

i 是nums1的中位线,j是nums2的中位线

起始的i满足i=(left+right+1)/2   第一个有序数组的中位数。

i+j==(m+n+1)/2一定满足

此时可以在nums1中进行left和right遍历,来调整i的位置i(小心越界),同时j的位置也会相应的发生改变。当满足

  1. 总数组左边个数<=右边个数
  2. i和j分界左右满足特定的大小关系时

即找到了两个数组中位数分界的位置,再对分界线两边的数进行比较即可

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() > nums2.size()) {
            return findMedianSortedArrays(nums2, nums1);
        }
        
        int m = nums1.size();
        int n = nums2.size();
        int left = 0, right = m;
        // median1:前一部分的最大值
        // median2:后一部分的最小值
        int median1 = 0, median2 = 0;

        while (left <= right) {
            // 前一部分包含 nums1[0 .. i-1] 和 nums2[0 .. j-1]
            // 后一部分包含 nums1[i .. m-1] 和 nums2[j .. n-1]
            int i = (left + right) / 2;
            int j = (m + n + 1) / 2 - i;

           
            int nums_im1 = (i == 0 ? INT_MIN : nums1[i - 1]);
            int nums_i = (i == m ? INT_MAX : nums1[i]);
            int nums_jm1 = (j == 0 ? INT_MIN : nums2[j - 1]);
            int nums_j = (j == n ? INT_MAX : nums2[j]);

            if (nums_im1 <= nums_j) {    //分界线满足条件
                median1 = max(nums_im1, nums_jm1);
                median2 = min(nums_i, nums_j);
                left = i + 1;  //缩小区间
            } else {
                right = i - 1;
            }
        }

        return (m + n) % 2 == 0 ? (median1 + median2) / 2.0 : median1;
    }
};

作者:力扣官方题解
链接:https://leetcode.cn/problems/median-of-two-sorted-arrays/solutions/258842/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值