[LeetCode] 4. Median of Two Sorted Arrays

 

Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays.

The overall run time complexity should be O(log (m+n)).

Example 1:

Input: nums1 = [1,3], nums2 = [2]
Output: 2.00000
Explanation: merged array = [1,2,3] and median is 2.

Example 2:

Input: nums1 = [1,2], nums2 = [3,4]
Output: 2.50000
Explanation: merged array = [1,2,3,4] and median is (2 + 3) / 2 = 2.5.

Constraints:

  • nums1.length == m
  • nums2.length == n
  • 0 <= m <= 1000
  • 0 <= n <= 1000
  • 1 <= m + n <= 2000
  • -106 <= nums1[i], nums2[i] <= 106

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/median-of-two-sorted-arrays
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

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

让我们先回到比较直观的做法。我们可以先将两个数组合并,根据合并后的数组长度 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             return findMedianSortedArrays(nums2, nums1);
 6         }
 7 
 8         // normal case
 9         int m = nums1.length;
10         int n = nums2.length;
11         // 在短的数组里面进行二分法
12         int left = 0;
13         int right = m;
14         while (left <= right) {
15             int i = left + (right - left) / 2;        // nums1分割点
16             int j = (m + n + 1) / 2 - i;            // nums2分割点
17             int L1 = i == 0 ? Integer.MIN_VALUE : nums1[i - 1];
18             int R1 = i == m ? Integer.MAX_VALUE : nums1[i];
19             int L2 = j == 0 ? Integer.MIN_VALUE : nums2[j - 1];
20             int R2 = j == n ? Integer.MAX_VALUE : nums2[j];
21             if (L1 <= R2 && L2 <= R1) {
22                 // 如果数字总个数是奇数,中位数就是这样算
23                 if (((m + n) % 2) == 1) {
24                     return Math.max(L1, L2);
25                 }
26                 // 否则中位数就是这样算
27                 else {
28                     return (double) (Math.max(L1, L2) + Math.min(R1, R2)) / 2;
29                 }
30             } else if (L2 > R1) {
31                 left = i + 1;
32             } else {
33                 right = i - 1;
34             }
35         }
36         return -1;
37     }
38 }

 

LeetCode 题目总结

posted @ 2020-05-07 02:34  CNoodle  阅读(555)  评论(0编辑  收藏  举报