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/

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