[LeetCode] 4. Median of Two Sorted Arrays

題目連結

 

//令中位數為M
            //runt的次數,取得n+1 ,偶數M=(n + n+1)/2,奇數M=n;
            int maxRun = (1 + nums1.Length + nums2.Length) / 2 + 1;
            //奇數中位數此值為false
            //偶數中位數此值為true 要在耶後一個元素相除
            bool isNeedGetPreElement = (1 + nums1.Length + nums2.Length) % 2 != 0;

            int element1 = -1, element2 = -1;
            int temp = maxRun;

            if (maxRun == 1) {
                return 0;
            }

            if (nums1.Length == 0) {
                return isNeedGetPreElement ? (nums2[maxRun - 1] + nums2[maxRun - 2]) / 2.0 : nums2[maxRun - 2] / 1.0;
            }

            if (nums2.Length == 0) {
                return isNeedGetPreElement ? (nums1[maxRun - 1] + nums1[maxRun - 2]) / 2.0 : nums1[maxRun - 2] / 1.0;
            }

            int nAdd1 = 0;
            int n = 0;
            for (int run = 1, nums1Index = 0, nums2Index = 0; run <= maxRun; run++) {
                int tempVal = 0;
                //nums1所有元素比較完畢,直接計算
                if (nums1Index == nums1.Length) {
                    nAdd1 = nums2[nums2Index + temp - 1];
                    n = temp - 2 >= 0 ? nums2[nums2Index+temp - 2] : n;
                    break;
                }

                if (nums2Index == nums2.Length) {
                    nAdd1 = nums1[nums1Index + temp - 1];
                    n = temp - 2 >= 0 ? nums1[nums1Index+temp - 2] : n;
                    break;
                }
                element1 = nums1[nums1Index];
                element2 = nums2[nums2Index];

                //每一輪比較 取出最小的 直到取到 maxElementIndex
                if (element1 >= element2) {
                    tempVal = element2;
                    nums2Index++;
                } else if (element1 < element2) {
                    tempVal = element1;
                    nums1Index++;
                }

                if (temp == 2) n = tempVal;
                if (temp == 1) nAdd1 = tempVal;

                temp--;
            }

            return isNeedGetPreElement ? (n + nAdd1) / 2.0 : n;

悲慘的效率

 

討論內有一篇 Share my O(log(min(m,n)) solution with explanation

為了解決這個問題,我們需要了解"什麼是中位數",統計上,中位數是用於分割一個集合為兩個等長的子集,其中一個子集總是大於另一個。如果我們了解中位數的分割方法,我們離答案非常這了。

首先,我們在位置i 分割A為兩部份。

因為A有m個元素,所以有m+1種分割方法(i=0~m)。且我們知道:len(left_A)=i,len(right_A) = m-i。注意:當i = 0,left_A是空的,且當i = m,right_A是空的。

同樣的方法,把B在隨機位置j分成兩部份。

把left_A,left_B放在同一個集合內,right_A,right_B放在另一個集合,我們命名為left_part,right_part

如果我們可以確認

我們把所有元素分成等長的{A,B},且其中一個總是大於另一個,然中位數 = (max(left_part) + min(right_part)) /2。

為了確保這兩個條件,我們必需保證。

ps.1 為了簡化,我假設 A[i-1],B[j-1],A[i],B[j]總是合法,即使i=0/i=m/j=0/j=n.之後我會談論怎麼處理這些邊界值。

ps2 為什麼n>=m?因為當0<=i<=m and j = (m+n+1)/2 -i。我必需確保j是非負值。如果n < m ,j可能是負值,會產生錯誤結果。

所以,所有我們應該做的是:

然後我們可以依下面步驟做二元搜尋:

當i被找到,中位數是:

現在我們考慮邊界值 i=0,i=m,j = 0,j =n,使A[i-1],B[j-1],A[i],B[j]不一定存在,事實上這狀況比你想的簡單。

我們要做的是確認max(left_part) <= min(right_part),所以如果i,j不是邊界值(指A[i-1],B[j-1],A[i],B[j]都存在),然後我們必需檢查B[j-1] <= A[i] and A[i-1] <= B[j]

但如果A[i-1],B[j-1],A[i],B[j]有不存在的,我們就不需要確認一個、或兩個條件,例如,如果i =0,A[i-1]不存在,我們就不需要檢查A[i-1] <= B[j]

所以我們需要做的是:

在迴圈中我們只會遇到三種情況:

感謝@Quentin.chen,他指出了i < m ==> j > 0 and i > 0 ==> j < n,因為:

所以在狀況b和c,我們不需要檢查是否j > 0或j < n

下面是通過的代碼:

C#

int m = nums1.Length, n = nums2.Length;
            //假設n >= m 反過來就調換
            if(m > n) {
                int[] temp = nums1;
                nums1 = nums2;
                nums2 = temp;
                m = nums1.Length;
                n = nums2.Length;
            }

            if (n == 0) throw new Exception();

            int imin =0, imax =m, half_len = (m + n + 1) / 2;
            int max_of_left, min_of_right;
            while (imin <= imax) {
                int i = (imin + imax) / 2;
                int j = half_len - i;
                if (i < m && nums2[j - 1] > nums1[i])
                    //i is too small, must increase it
                    imin = i + 1;
                else if (i > 0 && nums1[i - 1] > nums2[j])
                    // i is too big, must decrease it
                    imax = i - 1;
                else {
                    //i is perfect
                    if (i == 0) max_of_left = nums2[j - 1];
                    else if (j == 0) max_of_left = nums1[i - 1];
                    else max_of_left = new int[] { nums1[i - 1], nums2[j - 1] }.Max();

                    if ((m + n) % 2 == 1) return (double)max_of_left;

                    if (i == m) min_of_right = nums2[j];
                    else if (j == n) min_of_right = nums1[i];
                    else min_of_right = new int[] { nums1[i ], nums2[j ] }.Min();

                    return (max_of_left + min_of_right) / 2.0;

                }
            }

            return -1.0;

 

posted on 2019-11-15 10:15  seako  阅读(72)  评论(0编辑  收藏  举报