Leetcode练习 #4Median of Two Sorted Arrays

4. Median of Two Sorted Arrays    

              

 

题目简析:在两个有序数组里面找出总的中位数,第一个思路当然是对整体排序成为一个数组,在根据数字数目直接找到中位数,这样显然是毫无难度的,因此题目给出一个条件——O(log (m+n))的时间复杂度。看到O(log),第一个联想起来的自然是二分法,因此如何结合二分法找出中位数,这就是我基本的解题灵感所在。

 

        首先根据题目字母的意思,暂时我们拥有的条件就是“数组有序”这个条件,结合中位数的定义,我们可以数字有序这个特点,通过从两端逼近中央,不断缩小中位数所在的区间,当这个区间足够小的时候,自然就找到中位数了。那么问题就是——如何逼近?多大的区间才是足够小?

 

       首先解决第一个问题——如何逼近?

假设有数组A、B,大小分别为m、n(这里暂且认为m≥n),假如A、B都是有序的话,那么会有以下的特点:

       1.  这里假如将AB整体排好序后,有一个新的有序数组C,那么C显然大小为m+n,中位数为mid, 这里我们令k=(m+n)/2,根据中位数的定义,假如m+n是 奇数时,由于C是有序,那么中位数mid两侧分别有k个数字。

       2.  由于m≥n且均为非负整数,所以m/2和n/2必定小于k

       3.  分别从A、B两侧删除n/2个数字(比如删除A侧最左端n/2个数和B最右端n/2个数;或者删除A最右端n/2个数和B最左端n/2个数),中位数不会被删除,因为k>n/2,两端可删除的数字数目一定大于待删除数目

       4.  两侧对称删除同等量的数字后,特点1~3不变

       根据1~4这4点,也就是说不断重复3 的话,我们可以实现不断从AB数组中删除元素,从而缩短中位数所在区间。

 

       第二个问题——多大的区间才足够小?

      这里我们仍要看回上面的1~4点,从第3点,我们可以看到n是个很关键的数字,因为它限制了删除数字的上限,所以当我们将数组B逼近至无法再削减时,就达到我们需要的区间了。显然,达到这个区间需要的时间复杂度,仅仅是O(logn),没有超出题目的要求。

       由于奇偶数的不同,中位数的计算方法不同,由于每次删除的数字总量都是偶数,因此奇偶性不会变化,因此数组B需要保留的数字,是个要仔细探讨的问题。结论是什么呢?—— 结论是B要保留的数字是2个,为什么?

       (1)假如m+n是奇数时,计算中位数我们只需要查找一个数字,此时中位数mid也许在A中也许在B中,所以B必须至少有1个数。

       (2)假如m+n是偶数时,计算中位数我们需要查找两个数字mid1和mid2,(mid1+mid2)/2计算中位数,mid1和mid2也许在A中也许在B中,甚至两个数都在同一个数组里,所以B必须保留2个数字,才能保证中位数不会被删除掉

       这里还有一个小细节,假如给定的数组分别是A: [1, 2, 3] 和 B: [1]时,怎么办呢?为了解决这个问题,我们可以在分别在A、B的两侧添加一个数字,简化这个问题。

比如  :   数组A  [1, 2, 3] ——>[min, 1, 2, 3] 

             数组B  [1]   ——> [1,max]

       这里的min和max分别是所有数字中的最小值和最大值,使其分别能位于所有数字的最小端和最大端,这样就不会影响中位数的计算(因为最两端的数字必定不参与计算,那么添加数字前的计算情况和添加数字后的计算情况都是一样的)。 

       当然,这里还有几种特殊情况,通过if语句直接筛掉,留下最主要的情况进行计算,这些特殊情况分别包括:

       (1) 空数组  A:[ a …… b] B:[] 或者 A [] B [],这里直接计算A的中位数或者返回0即可

       (2) 单数字数组 A: [mid1]  B: [mid2] ,直接返回(mid1+mid2)/2

       (3) m<n,由于以上解析均建立在m≥n这个条件上,因此这里通过调用函数自身,把数组A、B互换即可

比如:

        if(m<n)
            return findMedianSortedArrays(nums2,nums1);

       现在,我们已经解决了如何逼近区间和区间大小的问题,剩下的就是计算中位数的问题。因为在以上操作后,我们把B的数目控制在了2 这个大小,根据m≥n,A的数目也是≥2的,而且A的数目也许还非常大,不过这里要提醒的是,我们每次缩减区间所删除的数字数目都是偶数,而且是从两端分别删除,所以这里有个很重要的特点——中位数两端的数字数目也是相同的,并且不影响计算中位数需要的数字数目(1位或2位)。

       这意味着,就算A剩余数字数目很大,两侧的大部分数字是可以不考虑的,因为他们既不参与计算,两端数字数目也是相同,所以我们只需要取数组A最中间的几个数字和数字B的2个数字进行比较,即可完成中位数的查找和计算,这里需要列举一下几种计算情况


1. m=n=2时

排列情况有

情况

 

 

 

中位数

1

a1

(a2 b1) or (b1 a2)

b2

(a2 + b1)/2

2

b1

(b2 a1) or (a1 b2)

a2

(a1 + b2)/2

3

a1

b1 b2

a2

(b1 + b2)/2

4

b1

a1 a2

b2

(a1 + a2)/2




2.m为偶数且m≥4时

排列情况有

序号

 

 

 

中位数

1

a1  a2

a3  a4

b1  b2

(a3+a4)/2

2

b1  b2

a1  a2

a3  a4

(a1+a2)/2

3

a1  a2

b1  b2

a3  a4

(b1+b2)/2

4

(a1 b1)or(b1 a1)

a2  a3

(a4 b2)or(b2 a4)

(a1+a3)/2

5

(a1 b1)or(b1 a1)

(a2  b2)or(a2 b2)

a3 a4

(a2+b2)/2

6

a1  a2

(b1 a3)or(a3 b1)

(a4 b2)or(b2 a4)

(a3+b1)/2

 

 

 

3.m为奇数且m≥3时

 

排列情况有

情况

 

 

 

中位数

1

b1 b2

a1

a2 a3

a1

2

a1 a2

a3

b1 b2

a3

3

(b1 a1)or(a1 b1)

a2

(a3 b2)or(b2 a3)

a2

4

a1 a2

b1

 (b2 a3)or(a3 b2)

b1

5

(a1 b1)or(b1 a1)

b2

a2 a3

b2

 

 

到这里整体的算法思路都已经描述完毕,剩下的是全部代码,仅供参考

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m=nums1.size();
        int n=nums2.size();
        int mid_flag=(m+n)%2;
       
        if(m==n){
            if(m==1)
                return (nums1[0]+nums2[0])/2.0;
            if(m==0)
                return 0/1.0;
            else if(nums1[0]<nums2[0])
                return findMedianSortedArrays(nums2,nums1);
        }
            
        if(m<n)
            return findMedianSortedArrays(nums2,nums1);
        else if(m>1&&n==1){
            nums2.push_back(max(nums2[0],nums1[m-1]));
            const int insert_ele=min(nums1[0],nums2[0]);            
            nums1.insert(nums1.begin(),insert_ele);
            m++;
            n++;
        }

        if(n==0){
            if(m%2==0)
                return (nums1[m/2-1]+nums1[m/2])/2.0;
            else
                return nums1[m/2]/1.0;
        }
        
        
        int start1=0;
        int start2=0;
        int end1=m-1;
        int end2=n-1;
        int p1,p2;
        while(start2!=end2-1){
            p1=(end1-start1)/2;
            p2=(end2-start2)/2;
     
            if(nums1[start1+p1]>nums2[start2+p2]){
                start2+=p2;
                end1-=p2;
            }
            else if(nums1[start1+p1]<nums2[start2+p2]){
                start1+=p2;
                end2-=p2;
            }
            else if(nums1[start1+p1]==nums2[start2+p2]){
                if(m==n && mid_flag==1)
                    return (nums1[start1+p1]+nums2[start2+p2])/2.0;       
                if((end2-start2)>p2*2){
                    start2+=p2;
                    end2-=p2;
                }
                else{              
                      start1++;
                      end2--;
                }

            }
        }
        int mid=(end1-start1)/2+start1;
        if(mid_flag==1){
            if(nums2[start2] >= nums1[mid+1])
                return nums1[mid+1]/1.0;
            else if(nums2[end2] <= nums1[mid-1])
                return nums1[mid-1]/1.0;
            else if(nums2[start2] <= nums1[mid] && nums2[end2] >= nums1[mid])
                return nums1[mid]/1.0;
            else if(nums2[start2]>=nums1[mid]&&nums2[start2]<=nums1[mid+1])
                return nums2[start2];
            else if(nums2[end2]>=nums1[mid-1]&&nums2[end2]<=nums1[mid])
                return nums2[end2];
        }
        else if(mid_flag==0){
            if((end1-start1)==1){
                if(nums1[mid]>=nums2[start2] && nums1[mid+1]>=nums2[end2])
                    return (nums1[mid]+nums2[end2])/2.0;
                else if(nums1[mid]<=nums2[start2] && nums1[mid+1]<=nums2[end2])
                    return (nums1[mid+1]+nums2[start2])/2.0;
                else if(nums1[mid]>=nums2[start2] && nums1[mid+1]<=nums2[end2])
                    return (nums1[mid]+nums1[mid+1])/2.0;
                else
                    return (nums2[start2]+nums2[end2])/2.0;
                
            }
            
            if(nums1[mid-1]>=nums2[end2])
                return (nums1[mid-1]+nums1[mid])/2.0;
            else if(nums1[mid+2]<=nums2[start2])
                return (nums1[mid+1]+nums1[mid+2])/2.0;
            else if(nums1[mid]>=nums2[start2] && nums1[mid+1]<=nums2[end2])
                return (nums1[mid]+nums1[mid+1])/2.0;
            else if(nums1[mid]<=nums2[start2] && nums1[mid+1]>=nums2[end2])
                return (nums2[start2]+nums2[end2])/2.0;
            else if(nums1[mid]>=nums2[start2] && nums1[mid+1]>=nums2[end2])
                return (nums1[mid]+nums2[end2])/2.0;
            else if(nums1[mid]<=nums2[start2] && nums1[mid+1]<=nums2[end2])
                return (nums1[mid+1]+nums2[start2])/2.0;
        }
        
    return 0;
    }
};
 Submission Result: Accepted

 Runtime: 0 ms


题目链接:https://leetcode.com/problems/median-of-two-sorted-arrays/description/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章