跳转至

4. 寻找两个正序数组的中位数#

问题描述#

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

进阶:你能设计一个时间复杂度为 O(log (m+n)) 的算法解决此问题吗?

 

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2

示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5

示例 3:

输入:nums1 = [0,0], nums2 = [0,0]
输出:0.00000

示例 4:

输入:nums1 = [], nums2 = [1]
输出:1.00000

示例 5:

输入:nums1 = [2], nums2 = []
输出:2.00000

 

提示:

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

解题思路#

方法一:排序(通过)#

最简单的方法就是合并两个数组后排序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        if len(nums1) < len(nums2):
            nums1, nums2 = nums2, nums1

        nums1 += nums2
        nums1.sort()
        n = len(nums1)

        return (nums1[n // 2] + nums1[(n - 1) // 2]) / 2 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if (nums1.size() < nums2.size()) swap(nums1, nums2);
        for (auto x : nums2) nums1.emplace_back(x);

        int n = nums1.size();

        sort(nums1.begin(), nums1.end());

        return (nums1[n / 2] + nums1[(n - 1) / 2]) / 2.0;
    }
};

时间复杂度\(\mathcal{O}((m+n)\log(m+n))\)
空间复杂度\(\mathcal{O}(\min(m,n))\)

方法二:合并两个有序数组(通过)#

方法一中没有用到两个数组是有序的这个条件,可以在合并的过程中判断是否是中位数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        n1, n2 = len(nums1), len(nums2)
        L, R = (n1 + n2 - 1) // 2, (n1 + n2) // 2
        x = y = k = 0

        while nums1 and nums2:
            if nums1[-1] >= nums2[-1]:
                mx = nums1.pop()
            else:
                mx = nums2.pop()

            if k == L: 
                x = mx
            if k == R: 
                y = mx

            k += 1

        while nums1:
            if k == L:
                x = nums1[-1]
            if k == R:
                y = nums1[-1]
            nums1.pop()
            k += 1

        while nums2:
            if k == L:
                x = nums2[-1]
            if k == R:
                y = nums2[-1]
            nums2.pop()
            k += 1

        return (x + y) / 2
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n1 = nums1.size(), n2 = nums2.size();
        int L = (n1 + n2 - 1) / 2, R = (n1 + n2) / 2;
        int x, y;
        int i = 0, j = 0, k, mn;

        while (i < n1 && j < n2) {
            k = i + j;
            if (nums1[i] <= nums2[j]) {
                mn = nums1[i++];
            } else {
                mn = nums2[j++];
            }
            if (k == L) x = mn;
            if (k == R) y = mn;
        }

        while (i < n1) {
            if (i + j == L) x = nums1[i];
            if (i + j == R) y = nums1[i];
            ++i;
        }

        while (j < n2) {
            if (i + j == L) x = nums2[j];
            if (i + j == R) y = nums2[j];
            ++j;
        }

        return (x + y) / 2.0;
    }
};

时间复杂度\(\mathcal{O}(n)\)
空间复杂度\(\mathcal{O}(1)\)

方法三:二分搜索(通过)#

关键在于确定每个数在两个数组中合并后的位置范围。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        def findKth(s1: List[int], s2: List[int], k: int) -> int:
            L, R = 0, len(s2) - 1
            while L <= R:
                M = L + ((R - L) >> 1)
                a, b = bisect_left(s1, s2[M]) + M, bisect_right(s1, s2[M]) + M
                if k < a: R = M - 1
                elif k > b: L = M + 1
                else: return s2[M]

            return findKth(s2, s1, k)

        n = len(nums1) + len(nums2)
        return (findKth(nums1, nums2, (n - 1) // 2) + findKth(nums1, nums2, n // 2)) / 2.0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
    int findKth(vector<int> &s1, vector<int> &s2, int k) {
        int L = 0, R = s2.size() - 1;
        while (L <= R) {
            auto M = L + ((R - L) >> 1);
            auto range = equal_range(s1.begin(), s1.end(), s2[M]);
            int a = range.first - s1.begin() + M, b = range.second - s1.begin() + M;
            if (k < a) R = M - 1;
            else if (k > b) L = M + 1;
            else return s2[M];
        }
        return findKth(s2, s1, k);
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size() + nums2.size();
        return (findKth(nums1, nums2, (n - 1) / 2) + findKth(nums1, nums2, n / 2)) / 2.0;
    }
};

时间复杂度\(\mathcal{O}(\log(m)\log(n))\)
空间复杂度\(\mathcal{O}(1)\)

方法四:二分搜索(通过)#

从方法三中可以看出,可以直接判断 \(\texttt{s2}[M]\) 能否作为第 \(k\) 个数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
    int findKth(vector<int> &s1, vector<int> &s2, int k) {
        // 查找合并后的第 k 个数(从 1 开始)
        int n1 = s1.size(), n2 = s2.size();
        if (n1 == 0) return s2[k - 1];
        if (n2 == 0) return s1[k - 1];

        int L = 0, R = min(k - 1, n2 - 1);
        while (L <= R) {
            auto M = L + ((R - L) >> 1); // 在 s2 中包含 M + 1 个数
            if (M + 1 == k) {
                if (s1[0] >= s2[M]) return s2[M]; // s2[M] 刚好是 s2 中的第 k 个数,而且可以将 s1 中的数全部放在 s2[M] 之后,即 s2[M] 可以是合并后的第 k 个数 
                break;
            }
            int pos = k - M - 1 - 1; // 在 s1 中包含 k - (M + 1) 个数,所以下标为 k - (M + 1) - 1
            if (pos >= n1 || s1[pos] > s2[M]) L = M + 1; // 不够 k 个数
            else if (s1[pos] <= s2[M] && (pos + 1 >= n1 || s1[pos + 1] >= s2[M])) return s2[M]; // s1[pos] 及之前的数可以放在 s2[M] 之前,而且 s1[pos] 之后的数可以放在 s2[M] 之后,所以 s2[M] 可以作为合并后的第 k 个数
            else R = M - 1; // s1[pos + 1] < s2[M],超过 k 个数 
        }
        return findKth(s2, s1, k); // 第 k 个数不在 s2 中
    }

    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size() + nums2.size();
        return (findKth(nums1, nums2, (n - 1) / 2 + 1) + findKth(nums1, nums2, n / 2 + 1)) / 2.0;
    }
};

时间复杂度\(\mathcal{O}(\log(m) + \log(n))\)
空间复杂度\(\mathcal{O}(1)\)

返回顶部

在手机上阅读