zoukankan      html  css  js  c++  java
  • [LeetCode] 4. Median of Two Sorted Arrays

    There are two sorted arrays nums1 and nums2 of size m and n respectively.

    Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

    You may assume nums1 and nums2 cannot be both empty.

    Example 1:

    nums1 = [1, 3]
    nums2 = [2]
    
    The median is 2.0
    

    Example 2:

    nums1 = [1, 2]
    nums2 = [3, 4]
    
    The median is (2 + 3)/2 = 2.5

    寻找两个正序数组的中位数。这个题是有时间复杂度的要求的,O(log (m+n))。如果没有这个要求,因为两个数组都是各自有序的,所以就可以把两个数组合并然后找中位数就行了。我这里给出最优解,思路参见这个帖子时间复杂度更好,是O(log(min(m, n)))。

    既然是找两个数组的中位数,让我们先回到比较直观的做法。我们可以先将两个数组合并,根据合并后的数组长度(很容易知道)返回中间的那个元素或者中间两个元素的平均值(这个也很容易知道)。如果只是一般情况,那么就是nums1里面拿出来几个元素,nums2里面也拿出来几个元素凑成合并数组的左半边,同样的,合并数组的右半边也会是这样组成的。如果我知道nums1里面拿出来几个元素就能凑成合并数组的左半边的话,我同样也就能知道nums2需要贡献几个元素。为什么呢?因为中位数的index = (len1 + len2) / 2。nums2需要贡献的元素个数是index - nums1贡献的元素个数。所以这个题的时间复杂度可以被控制在O(log(min(m, n))),找到短的那个数组的切分位置,你也就找到了另一个数组的切分位置。

    找到切分位置的时候,如何这一刀切分的对不对呢?可以记录四个元素L1, R1, L2, R2分别表示在数组1数组2的切分位置左边和右边的元素都是什么。如果这一刀的位置是正确的,则应该有的结果是

    • L1<=R2
    • L2<=R1

    这样就能确保,左边的元素都小于右边的元素了。下图帮助理解。

    时间O(log(min(m, n)))

    空间O(1)

    Java实现

     1 class Solution {
     2     public double findMedianSortedArrays(int[] nums1, int[] nums2) {
     3         int lenA = nums1.length;
     4         int lenB = nums2.length;
     5         // make sure the first array is shorter
     6         if (lenA > lenB) {
     7             return findMedianSortedArrays(nums2, nums1);
     8         }
     9 
    10         // 如果一个数组为空,直接计算另一个数组的中位数
    11         if (lenA == 0) {
    12             return ((double) nums2[(lenB - 1) / 2] + (double) nums2[lenB / 2]) / 2;
    13         }
    14 
    15         // normal case
    16         int len = lenA + lenB;
    17         // 对nums1做二分的范围
    18         int aLeft = 0;
    19         int aRight = lenA;
    20         int cutA;
    21         int cutB;
    22         while (aLeft <= aRight) {
    23             cutA = aLeft + (aRight - aLeft) / 2;
    24             cutB = (len + 1) / 2 - cutA;
    25             double L1 = (cutA == 0) ? Integer.MIN_VALUE : nums1[cutA - 1];
    26             double R1 = (cutA == lenA) ? Integer.MAX_VALUE : nums1[cutA];
    27             double L2 = (cutB == 0) ? Integer.MIN_VALUE : nums2[cutB - 1];
    28             double R2 = (cutB == lenB) ? Integer.MAX_VALUE : nums2[cutB];
    29             if (L1 > R2) {
    30                 aRight = cutA - 1;
    31             } else if (L2 > R1) {
    32                 aLeft = cutA + 1;
    33             } else {
    34                 if (len % 2 == 0) {
    35                     return (Math.max(L1, L2) + Math.min(R1, R2)) / 2;
    36                 } else {
    37                     return Math.max(L1, L2);
    38                 }
    39             }
    40         }
    41         return -1;
    42     }
    43 }

    JavaScript实现

     1 /**
     2  * @param {number[]} nums1
     3  * @param {number[]} nums2
     4  * @return {number}
     5  */
     6 var findMedianSortedArrays = function(nums1, nums2) {
     7     /*
     8         nums3 => nums1 + nums2 => nums1
     9         只需考慮 nums1 為最短邊,則計算最少
    10     */
    11     if (nums1.length > nums2.length) {
    12         return findMedianSortedArrays(nums2, nums1);
    13     }
    14     let len = nums1.length + nums2.length;
    15     let cut1 = 0;
    16     let cut2 = 0;
    17     let cutL = 0;
    18     let cutR = nums1.length;
    19     while (cut1 <= nums1.length) {
    20         /*
    21         *            L1          R1
    22         *           (cut1)-1    cut1
    23         * nums1 = 3   5      |   8     9
    24         *
    25         *                L2           R2
    26         *               (cut2)-1     cut2
    27         * nums2 = 1   2   7       |   10   11    12
    28         *
    29         *         L1 + L2  = L3     R3 = R1 + R2
    30         * nums3 = 1  2  3  5  7  |  8  9   10   11  12
    31         *
    32         * cut 1 為 L3 提供 [3, 5] 所以 cut2 則提供 5 - 2 = 3([1, 2, 7]) 元素
    33         */
    34         cut1 = parseInt(cutL + (cutR - cutL) / 2);
    35         cut2 = parseInt((len / 2)  - cut1);
    36         // 不可使用 MAX_VALUE 要用 Number.MIN_SAFE_INTEGER,否則會超過時間
    37         // 若超出界線 則使用最小值
    38         let L1 = (cut1 === 0) ? Number.MIN_SAFE_INTEGER : nums1[cut1-1];
    39         let L2 = (cut2 === 0) ? Number.MIN_SAFE_INTEGER : nums2[cut2-1];
    40         // 不可使用 MIN_VALUE 要用 Number.MIN_SAFE_INTEGER,否則會超過時間
    41         // 若超出界線 則使用最大值
    42         let R1 = (cut1 === nums1.length) ? Number.MAX_SAFE_INTEGER : nums1[cut1];
    43         let R2 = (cut2 === nums2.length) ? Number.MAX_SAFE_INTEGER : nums2[cut2];
    44         /*
    45             假如情況為:
    46             nums1 = 3 5 8 | 9 (<<)
    47             nums2 = 1 2 | 7  11 12
    48             8 > 7
    49             則 nums1 必須 在往上一格移動 << 所以為 (cut1 - 1)
    50         */
    51         if(L1 > R2) {
    52             cutR = cut1 - 1;
    53             /*
    54             假如情況為:
    55             nums1 = 3 | 5  8  9 (>>)
    56             nums2 = 1 2 7  |  11  12
    57             7 > 5
    58             則 nums1 必須 在往下一格移動 >> 所以為 (cut1 + 1)
    59             */
    60         } else if (L2 > R1) {
    61             cutL = cut1 + 1;
    62             /*
    63                 滿足 以下條件
    64                 R1 >= L2
    65                 R2 >= L1
    66             */
    67         } else {
    68             // 假如 nums3 為偶數
    69             // 如果是偶數 L 選擇最大的部分, R 選擇最小的部分 除以 2
    70             /*
    71             *          L1 + L2  =  L3    R3 = R1 + R2
    72             *  nums3 = 1  2  3  5  7  |  8  9  10  11  12
    73             * */
    74             if ((len % 2)  === 0) {
    75                 L1 = (L1 > L2) ? L1 : L2;
    76                 R1 = (R1 < R2) ? R1 : R2;
    77                 return (L1+R1) / 2;
    78             } else{
    79                 //  假如 nums3 為奇數
    80                 /*  nums1 = 3  5  8  |  9   15
    81                  *  nums2 = 1  2  7  |  10  11  12
    82                  *  如果是奇數選小的
    83                  *  nums3 = 1  2  3  5  7  8  | 9  10  11  12  15
    84                  * */
    85                 console.log(R1, R2);
    86                 R1 = (R1 < R2) ? R1 : R2;
    87                 return R1;
    88             }
    89         }
    90     }
    91     return -1;
    92 };
    93 console.log(findMedianSortedArrays([3, 5, 8, 9], [1 , 2, 7, 10, 11, 12]))

    我在二刷的时候看到另一个讲解,也非常好。思路和时间复杂度是一样的。

    Java实现

     1 class Solution {
     2     public double findMedianSortedArrays(int[] nums1, int[] nums2) {
     3         // corner case
     4         if (nums1.length > nums2.length) {
     5             int[] temp = nums1;
     6             nums1 = nums2;
     7             nums2 = temp;
     8         }
     9 
    10         // normal case
    11         int m = nums1.length;
    12         int n = nums2.length;
    13         // 在短的数组里面进行二分法
    14         int left = 0;
    15         int right = m;
    16         while (left <= right) {
    17             int i = left + (right - left) / 2;
    18             int j = (m + n + 1) / 2 - i;
    19             int nums1LeftMax = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
    20             int nums1RightMin = i == m ? Integer.MAX_VALUE : nums1[i];
    21             int nums2LeftMax = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
    22             int nums2RightMin = j == n ? Integer.MAX_VALUE : nums2[j];
    23             if (nums1LeftMax <= nums2RightMin && nums2LeftMax <= nums1RightMin) {
    24                 if (((m + n) & 1) == 1) {
    25                     return Math.max(nums1LeftMax, nums2LeftMax);
    26                 } else {
    27                     return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin)))
    28                             / 2;
    29                 }
    30             } else if (nums2LeftMax > nums1RightMin) {
    31                 left = i + 1;
    32             } else {
    33                 right = i - 1;
    34             }
    35         }
    36         return -1;
    37     }
    38 }

    LeetCode 题目总结

  • 相关阅读:
    洛谷P1330 封锁阳光大学
    洛谷P1341 无序字母对
    Bzoj1059 [ZJOI2007]矩阵游戏
    POJ2337 Catenyms
    Bzoj2342 [Shoi2011]双倍回文
    Bzoj1009 [HNOI2008]GT考试
    Bzoj3670 [Noi2014]动物园
    POJ2406 Power Strings
    POJ 2752 Seek the Name, Seek the Fame
    POJ3522 Slim Span
  • 原文地址:https://www.cnblogs.com/cnoodle/p/12840302.html
Copyright © 2011-2022 走看看