二分查找题目:寻找两个正序数组的中位数

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 代码
    • 复杂度分析

题目

标题和出处

标题:寻找两个正序数组的中位数

出处:4. 寻找两个正序数组的中位数

难度

8 级

题目描述

要求

给定两个大小分别为 m \texttt{m} m 和 n \texttt{n} n 的升序数组 nums1 \texttt{nums1} nums1 和 nums2 \texttt{nums2} nums2,返回这两个升序数组的中位数。

要求时间复杂度是 O(log (m + n)) \texttt{O(log (m + n))} O(log (m + n))。

示例

示例 1:

输入: nums1 = [1,3], nums2 = [2] \texttt{nums1 = [1,3], nums2 = [2]} nums1 = [1,3], nums2 = [2]
输出: 2.00000 \texttt{2.00000} 2.00000
解释:合并数组是 [1,2,3] \texttt{[1,2,3]} [1,2,3],中位数是 2 \texttt{2} 2。

示例 2:

输入: nums1 = [1,2], nums2 = [3,4] \texttt{nums1 = [1,2], nums2 = [3,4]} nums1 = [1,2], nums2 = [3,4]
输出: 2.50000 \texttt{2.50000} 2.50000
解释:合并数组是 [1,2,3,4] \texttt{[1,2,3,4]} [1,2,3,4],中位数是 2 + 3 2 = 2.5 \dfrac{\texttt{2} + \texttt{3}}{\texttt{2}} = \texttt{2.5} 22+3​=2.5。

数据范围

  • nums1.length = m \texttt{nums1.length} = \texttt{m} nums1.length=m
  • nums2.length = n \texttt{nums2.length} = \texttt{n} nums2.length=n
  • 0 ≤ m ≤ 1000 \texttt{0} \le \texttt{m} \le \texttt{1000} 0≤m≤1000
  • 0 ≤ n ≤ 1000 \texttt{0} \le \texttt{n} \le \texttt{1000} 0≤n≤1000
  • 1 ≤ m + n ≤ 2000 \texttt{1} \le \texttt{m} + \texttt{n} \le \texttt{2000} 1≤m+n≤2000
  • -10 6 ≤ nums1[i], nums2[i] ≤ 10 6 \texttt{-10}^\texttt{6} \le \texttt{nums1[i], nums2[i]} \le \texttt{10}^\texttt{6} -106≤nums1[i], nums2[i]≤106

解法一

思路和算法

已知两个升序数组的长度分别是 m m m 和 n n n。计算两个升序数组的中位数可以转换成找到两个升序数组的所有元素中的第 k k k 小元素,其中 0 ≤ k < m + n 0 \le k < m + n 0≤k<m+n。用 total = m + n \textit{total} = m + n total=m+n 表示两个升序数组的长度之和。当 total \textit{total} total 是奇数时, k = total − 1 2 k = \dfrac{\textit{total} - 1}{2} k=2total−1​,第 k k k 小元素即为中位数;当 total \textit{total} total 是偶数时,分别取 k = total 2 − 1 k = \dfrac{\textit{total}}{2} - 1 k=2total​−1 和 k = total 2 k = \dfrac{\textit{total}}{2} k=2total​,两次第 k k k 小元素的平均数即为中位数。因此,根据两个升序数组的长度之和是奇数或偶数,执行一次或两次寻找第 k k k 小元素的操作,即可得到中位数。

由于题目要求时间复杂度是 O ( log ⁡ ( m + n ) ) O(\log (m + n)) O(log(m+n)),因此要求每次寻找第 k k k 小元素的操作的时间复杂度是 O ( log ⁡ ( m + n ) ) O(\log (m + n)) O(log(m+n))。需要使用二分查找实现。

用 k k k 表示目标值在剩余元素中的序号( k k k 从 0 0 0 开始,序号为 k k k 表示剩余元素中有 k k k 个元素小于等于目标值),用 index 1 \textit{index}_1 index1​ 和 index 2 \textit{index}_2 index2​ 分别表示数组 nums 1 \textit{nums}_1 nums1​ 和 nums 2 \textit{nums}_2 nums2​ 的首个剩余元素的下标,初始时 index 1 \textit{index}_1 index1​ 和 index 2 \textit{index}_2 index2​ 都等于 0 0 0。剩余元素表示可能是目标值的元素,查找过程中将不可能是目标值的元素排除。

每次查找时,分别考虑两个数组的剩余元素中最小的 ⌈ k 2 ⌉ \Big\lceil \dfrac{k}{2} \Big\rceil ⌈2k​⌉ 个元素,共考虑 k + 1 k + 1 k+1 个元素(当 k k k 是奇数时)或 k k k 个元素(当 k k k 是偶数时),这些元素在两个数组中的下标范围分别是 nums 1 \textit{nums}_1 nums1​ 的下标范围 [ index 1 , endIndex 1 ] [\textit{index}_1, \textit{endIndex}_1] [index1​,endIndex1​] 和 nums 2 \textit{nums}_2 nums2​ 的下标范围 [ index 2 , endIndex 2 ] [\textit{index}_2, \textit{endIndex}_2] [index2​,endIndex2​],其中 endIndex 1 = index 1 + ⌊ k − 1 2 ⌋ \textit{endIndex}_1 = \textit{index}_1 + \Big\lfloor \dfrac{k - 1}{2} \Big\rfloor endIndex1​=index1​+⌊2k−1​⌋, endIndex 2 = index 2 + ⌊ k − 1 2 ⌋ \textit{endIndex}_2 = \textit{index}_2 + \Big\lfloor \dfrac{k - 1}{2} \Big\rfloor endIndex2​=index2​+⌊2k−1​⌋。考虑 nums 1 [ endIndex 1 ] \textit{nums}_1[\textit{endIndex}_1] nums1​[endIndex1​] 和 nums 2 [ endIndex 2 ] \textit{nums}_2[\textit{endIndex}_2] nums2​[endIndex2​],其中的较大值是第 k k k 小元素(当 k k k 是奇数时)或第 k − 1 k - 1 k−1 小元素(当 k k k 是偶数时),因此其中的较小值一定不是第 k k k 小元素。对于较小值所在的数组,可以将较小值以及较小值前面的元素全部排除。

需要注意的是, endIndex 1 \textit{endIndex}_1 endIndex1​ 和 endIndex 2 \textit{endIndex}_2 endIndex2​ 不能超出数组下标范围。如果一个数组的剩余元素个数少于 ⌈ k 2 ⌉ \Big\lceil \dfrac{k}{2} \Big\rceil ⌈2k​⌉,则该数组中考虑的元素是该数组中的全部剩余元素。因此有 endIndex 1 = min ⁡ ( index 1 + ⌊ k − 1 2 ⌋ , m − 1 ) \textit{endIndex}_1 = \min(\textit{index}_1 + \Big\lfloor \dfrac{k - 1}{2} \Big\rfloor, m - 1) endIndex1​=min(index1​+⌊2k−1​⌋,m−1), endIndex 2 = min ⁡ ( index 2 + ⌊ k − 1 2 ⌋ , n − 1 ) \textit{endIndex}_2 = \min(\textit{index}_2 + \Big\lfloor \dfrac{k - 1}{2} \Big\rfloor, n - 1) endIndex2​=min(index2​+⌊2k−1​⌋,n−1)。

由此可以根据三种情况分别做相应的处理,缩小查找范围。

  • 如果 nums 1 [ endIndex 1 ] < nums 2 [ endIndex 2 ] \textit{nums}_1[\textit{endIndex}_1] < \textit{nums}_2[\textit{endIndex}_2] nums1​[endIndex1​]<nums2​[endIndex2​],则将 nums 1 \textit{nums}_1 nums1​ 的下标范围 [ index 1 , endIndex 1 ] [\textit{index}_1, \textit{endIndex}_1] [index1​,endIndex1​] 中的元素全部排除,排除的元素个数是 endIndex 1 − index 1 + 1 \textit{endIndex}_1 - \textit{index}_1 + 1 endIndex1​−index1​+1。

  • 如果 nums 1 [ endIndex 1 ] > nums 2 [ endIndex 2 ] \textit{nums}_1[\textit{endIndex}_1] > \textit{nums}_2[\textit{endIndex}_2] nums1​[endIndex1​]>nums2​[endIndex2​],则将 nums 2 \textit{nums}_2 nums2​ 的下标范围 [ index 2 , endIndex 2 ] [\textit{index}_2, \textit{endIndex}_2] [index2​,endIndex2​] 中的元素全部排除,排除的元素个数是 endIndex 2 − index 2 + 1 \textit{endIndex}_2 - \textit{index}_2 + 1 endIndex2​−index2​+1。

  • 如果 nums 1 [ endIndex 1 ] = nums 2 [ endIndex 2 ] \textit{nums}_1[\textit{endIndex}_1] = \textit{nums}_2[\textit{endIndex}_2] nums1​[endIndex1​]=nums2​[endIndex2​],则处理方式和 nums 1 [ endIndex 1 ] < nums 2 [ endIndex 2 ] \textit{nums}_1[\textit{endIndex}_1] < \textit{nums}_2[\textit{endIndex}_2] nums1​[endIndex1​]<nums2​[endIndex2​] 相同。

每次查找之后,将 k k k 的值减去排除的元素个数,并将排除元素的数组的相应下标更新为该数组首个剩余元素的下标,具体做法如下:如果排除的是 nums 1 \textit{nums}_1 nums1​ 中的元素,则将 index 1 \textit{index}_1 index1​ 更新为 endIndex 1 + 1 \textit{endIndex}_1 + 1 endIndex1​+1;如果排除的是 nums 2 \textit{nums}_2 nums2​ 中的元素,则将 index 2 \textit{index}_2 index2​ 更新为 endIndex 2 + 1 \textit{endIndex}_2 + 1 endIndex2​+1。

二分查找的条件是 index 1 < m \textit{index}_1 < m index1​<m, index 2 < n \textit{index}_2 < n index2​<n 和 k > 0 k > 0 k>0。如果三个条件之一不满足,则二分查找结束,得到目标值。

  • 如果 index 1 = m \textit{index}_1 = m index1​=m,则剩余元素都在 nums 2 \textit{nums}_2 nums2​ 中,目标值是 nums 2 [ index 2 + k ] \textit{nums}_2[\textit{index}_2 + k] nums2​[index2​+k]。

  • 如果 index 2 = n \textit{index}_2 = n index2​=n,则剩余元素都在 nums 1 \textit{nums}_1 nums1​ 中,目标值是 nums 1 [ index 1 + k ] \textit{nums}_1[\textit{index}_1 + k] nums1​[index1​+k]。

  • 如果 k = 0 k = 0 k=0,则剩余元素中的最小元素是目标值,目标值是 min ⁡ ( nums 1 [ index 1 ] , nums 2 [ index 2 ] ) \min(\textit{nums}_1[\textit{index}_1], \textit{nums}_2[\textit{index}_2]) min(nums1​[index1​],nums2​[index2​])。

以下用一个例子说明该解法。

两个数组是 nums 1 = [ 1 , 2 , 3 , 4 , 5 ] \textit{nums}_1 = [1, 2, 3, 4, 5] nums1​=[1,2,3,4,5], nums 2 = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] \textit{nums}_2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] nums2​=[1,2,3,4,5,6,7,8,9,10],两个数组的长度分别是 m = 5 m = 5 m=5, n = 10 n = 10 n=10,长度之和是 15 15 15, k = 7 k = 7 k=7。初始时, index 1 = 0 \textit{index}_1 = 0 index1​=0, index 2 = 0 \textit{index}_2 = 0 index2​=0。

  1. 根据 index 1 = 0 \textit{index}_1 = 0 index1​=0, index 2 = 0 \textit{index}_2 = 0 index2​=0 和 k = 7 k = 7 k=7 计算得到 endIndex 1 = 3 \textit{endIndex}_1 = 3 endIndex1​=3, endIndex 2 = 3 \textit{endIndex}_2 = 3 endIndex2​=3。由于 nums 1 [ 3 ] = nums 2 [ 3 ] \textit{nums}_1[3] = \textit{nums}_2[3] nums1​[3]=nums2​[3],因此将 nums 1 \textit{nums}_1 nums1​ 的下标范围 [ 0 , 3 ] [0, 3] [0,3] 排除,排除 4 4 4 个元素,更新得到 k = 3 k = 3 k=3, index 1 = 4 \textit{index}_1 = 4 index1​=4。

  2. 根据 index 1 = 4 \textit{index}_1 = 4 index1​=4, index 2 = 0 \textit{index}_2 = 0 index2​=0 和 k = 3 k = 3 k=3 计算得到 endIndex 1 = 4 \textit{endIndex}_1 = 4 endIndex1​=4, endIndex 2 = 1 \textit{endIndex}_2 = 1 endIndex2​=1。由于 nums 1 [ 4 ] > nums 2 [ 1 ] \textit{nums}_1[4] > \textit{nums}_2[1] nums1​[4]>nums2​[1],因此将 nums 2 \textit{nums}_2 nums2​ 的下标范围 [ 0 , 1 ] [0, 1] [0,1] 排除,排除 2 2 2 个元素,更新得到 k = 1 k = 1 k=1, index 2 = 2 \textit{index}_2 = 2 index2​=2。

  3. 根据 index 1 = 4 \textit{index}_1 = 4 index1​=4, index 2 = 2 \textit{index}_2 = 2 index2​=2 和 k = 1 k = 1 k=1 计算得到 endIndex 1 = 4 \textit{endIndex}_1 = 4 endIndex1​=4, endIndex 2 = 2 \textit{endIndex}_2 = 2 endIndex2​=2。由于 nums 1 [ 4 ] > nums 2 [ 2 ] \textit{nums}_1[4] > \textit{nums}_2[2] nums1​[4]>nums2​[2],因此将 nums 2 \textit{nums}_2 nums2​ 的下标范围 [ 2 , 2 ] [2, 2] [2,2] 排除,排除 1 1 1 个元素,更新得到 k = 0 k = 0 k=0, index 2 = 3 \textit{index}_2 = 3 index2​=3。

  4. 此时 k = 0 k = 0 k=0,二分查找结束, nums 1 [ 4 ] \textit{nums}_1[4] nums1​[4] 和 nums 2 [ 3 ] \textit{nums}_2[3] nums2​[3] 中的较小值 4 4 4 即为目标值。

代码

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int m = nums1.length, n = nums2.length;int total = m + n;if (total % 2 == 1) {int medianIndex = (total - 1) / 2;return findKthSmallest(medianIndex, nums1, nums2);} else {int medianIndex1 = total / 2 - 1, medianIndex2 = total / 2;return (findKthSmallest(medianIndex1, nums1, nums2) + findKthSmallest(medianIndex2, nums1, nums2)) / 2.0;}}public int findKthSmallest(int k, int[] nums1, int[] nums2) {int m = nums1.length, n = nums2.length;int index1 = 0, index2 = 0;while (index1 < m && index2 < n && k > 0) {int endIndex1 = Math.min(index1 + (k - 1) / 2, m - 1);int endIndex2 = Math.min(index2 + (k - 1) / 2, n - 1);int num1 = nums1[endIndex1], num2 = nums2[endIndex2];if (num1 <= num2) {k -= endIndex1 - index1 + 1;index1 = endIndex1 + 1;} else {k -= endIndex2 - index2 + 1;index2 = endIndex2 + 1;}}if (index1 == m) {return nums2[index2 + k];} else if (index2 == n) {return nums1[index1 + k];} else {return Math.min(nums1[index1], nums2[index2]);}}
}

复杂度分析

  • 时间复杂度: O ( log ⁡ ( m + n ) ) O(\log (m + n)) O(log(m+n)),其中 m m m 和 n n n 分别是数组 nums 1 \textit{nums}_1 nums1​ 和 nums 2 \textit{nums}_2 nums2​ 的长度。每次寻找第 k k k 小元素时, k k k 的初始值是 m + n m + n m+n 的一半附近的整数,每次查找将 k k k 的值减小一半,因此时间复杂度是 O ( log ⁡ ( m + n ) ) O(\log (m + n)) O(log(m+n))。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

解法二

思路和算法

解法一的时间复杂度是 O ( log ⁡ ( m + n ) ) O(\log (m + n)) O(log(m+n)),该时间复杂度已经很低,但是这道题还存在时间复杂度更低的解法。

为了找到中位数,需要在数组 nums 1 \textit{nums}_1 nums1​ 和 nums 2 \textit{nums}_2 nums2​ 中分别找到分割点 cut 1 \textit{cut}_1 cut1​ 和 cut 2 \textit{cut}_2 cut2​,将每个数组分割成两个部分。

  • 数组 nums 1 \textit{nums}_1 nums1​ 被分割成下标范围 [ 0 , cut 1 − 1 ] [0, \textit{cut}_1 - 1] [0,cut1​−1] 和下标范围 [ cut 1 , m − 1 ] [\textit{cut}_1, m - 1] [cut1​,m−1] 两部分,左边部分的长度是 cut 1 \textit{cut}_1 cut1​。

  • 数组 nums 2 \textit{nums}_2 nums2​ 被分割成下标范围 [ 0 , cut 2 − 1 ] [0, \textit{cut}_2 - 1] [0,cut2​−1] 和下标范围 [ cut 2 , n − 1 ] [\textit{cut}_2, n - 1] [cut2​,n−1] 两部分,左边部分的长度是 cut 2 \textit{cut}_2 cut2​。

其中, 0 ≤ cut 1 ≤ m 0 \le \textit{cut}_1 \le m 0≤cut1​≤m, 0 ≤ cut 2 ≤ n 0 \le \textit{cut}_2 \le n 0≤cut2​≤n,即每个数组分割成的两个部分中可以有一个部分为空。

假设 nums 1 [ − 1 ] = nums 2 [ − 1 ] = − ∞ \textit{nums}_1[-1] = \textit{nums}_2[-1] = -\infty nums1​[−1]=nums2​[−1]=−∞, nums 1 [ m ] = nums 2 [ n ] = + ∞ \textit{nums}_1[m] = \textit{nums}_2[n] = +\infty nums1​[m]=nums2​[n]=+∞,分割应满足以下两个条件。

  • 两个数组的左边部分的最大值小于等于两个数组的右边部分的最小值, max ⁡ ( nums 1 [ cut 1 − 1 ] , nums 2 [ cut 2 − 1 ] ) ≤ min ⁡ ( nums 1 [ cut 1 ] , nums 2 [ cut 2 ] ) \max(\textit{nums}_1[\textit{cut}_1 - 1], \textit{nums}_2[\textit{cut}_2 - 1]) \le \min(\textit{nums}_1[\textit{cut}_1], \textit{nums}_2[\textit{cut}_2]) max(nums1​[cut1​−1],nums2​[cut2​−1])≤min(nums1​[cut1​],nums2​[cut2​])。

  • 两个数组的左边部分的长度之和为两个数组的长度之和的一半向上取整, cut 1 + cut 2 = ⌈ m + n 2 ⌉ \textit{cut}_1 + \textit{cut}_2 = \Big\lceil \dfrac{m + n}{2} \Big\rceil cut1​+cut2​=⌈2m+n​⌉。

将两个数组的左边部分统称为前半部分,将两个数组的右边部分统称为后半部分,则前半部分的最大值小于等于后半部分的最小值,前半部分的元素个数为两个数组的长度之和的一半向上取整。

用 total = m + n \textit{total} = m + n total=m+n 表示两个升序数组的长度之和,用 lowerSize = ⌈ total 2 ⌉ \textit{lowerSize} = \Big\lceil \dfrac{\textit{total}}{2} \Big\rceil lowerSize=⌈2total​⌉ 表示前半部分的元素个数。当 total \textit{total} total 是奇数时,中位数是前半部分的最大值;当 total \textit{total} total 是偶数时,中位数是前半部分的最大值与后半部分的最小值的平均数。

由于已知 cut 1 + cut 2 = lowerSize \textit{cut}_1 + \textit{cut}_2 = \textit{lowerSize} cut1​+cut2​=lowerSize,因此可以在 nums 1 \textit{nums}_1 nums1​ 中寻找 cut 1 \textit{cut}_1 cut1​,当 cut 1 \textit{cut}_1 cut1​ 确定之后 cut 2 \textit{cut}_2 cut2​ 也可以确定。

寻找 cut 1 \textit{cut}_1 cut1​ 可以使用二分查找实现。由于两个数组都是升序数组, nums 1 [ cut 1 − 1 ] ≤ nums 1 [ cut 1 ] \textit{nums}_1[\textit{cut}_1 - 1] \le \textit{nums}_1[\textit{cut}_1] nums1​[cut1​−1]≤nums1​[cut1​] 和 nums 2 [ cut 2 − 1 ] ≤ nums 2 [ cut 2 ] \textit{nums}_2[\textit{cut}_2 - 1] \le \textit{nums}_2[\textit{cut}_2] nums2​[cut2​−1]≤nums2​[cut2​] 都满足,因此只需要满足 nums 1 [ cut 1 − 1 ] ≤ nums 2 [ cut 2 ] \textit{nums}_1[\textit{cut}_1 - 1] \le \textit{nums}_2[\textit{cut}_2] nums1​[cut1​−1]≤nums2​[cut2​] 和 nums 2 [ cut 2 − 1 ] ≤ nums 1 [ cut 1 ] \textit{nums}_2[\textit{cut}_2 - 1] \le \textit{nums}_1[\textit{cut}_1] nums2​[cut2​−1]≤nums1​[cut1​] 即可。二分查找需要查找满足 nums 1 [ cut 1 − 1 ] ≤ nums 2 [ cut 2 ] \textit{nums}_1[\textit{cut}_1 - 1] \le \textit{nums}_2[\textit{cut}_2] nums1​[cut1​−1]≤nums2​[cut2​] 的最大下标 cut 1 \textit{cut}_1 cut1​。

用 low \textit{low} low 和 high \textit{high} high 分别表示二分查找的下标范围的下界和上界,初始时 low = 0 \textit{low} = 0 low=0, high = m \textit{high} = m high=m。每次查找时,取 index 1 \textit{index}_1 index1​ 为 low \textit{low} low 和 high \textit{high} high 的平均数向上取整,并得到 index 2 = lowerSize − index 1 \textit{index}_2 = \textit{lowerSize} - \textit{index}_1 index2​=lowerSize−index1​,比较 nums 1 [ index 1 − 1 ] \textit{nums}_1[\textit{index}_1 - 1] nums1​[index1​−1] 和 nums 2 [ index 2 ] \textit{nums}_2[\textit{index}_2] nums2​[index2​] 的大小关系,调整查找的下标范围。

  • 如果 nums 1 [ index 1 − 1 ] ≤ nums 2 [ index 2 ] \textit{nums}_1[\textit{index}_1 - 1] \le \textit{nums}_2[\textit{index}_2] nums1​[index1​−1]≤nums2​[index2​],则 cut 1 ≥ index 1 \textit{cut}_1 \ge \textit{index}_1 cut1​≥index1​,因此在下标范围 [ index 1 , high ] [\textit{index}_1, \textit{high}] [index1​,high] 中继续查找。

  • 如果 nums 1 [ index 1 − 1 ] > nums 2 [ index 2 ] \textit{nums}_1[\textit{index}_1 - 1] > \textit{nums}_2[\textit{index}_2] nums1​[index1​−1]>nums2​[index2​],则 cut 1 < index 1 \textit{cut}_1 < \textit{index}_1 cut1​<index1​,因此在下标范围 [ low , index 1 − 1 ] [\textit{low}, \textit{index}_1 - 1] [low,index1​−1] 中继续查找。

当 low = high \textit{low} = \textit{high} low=high 时,查找结束,此时 low \textit{low} low 即为 cut 1 \textit{cut}_1 cut1​。

得到 cut 1 \textit{cut}_1 cut1​ 之后即可得到 cut 2 \textit{cut}_2 cut2​, nums 1 [ cut 1 − 1 ] \textit{nums}_1[\textit{cut}_1 - 1] nums1​[cut1​−1] 和 nums 2 [ cut 2 − 1 ] \textit{nums}_2[\textit{cut}_2 - 1] nums2​[cut2​−1] 中的最大值是前半部分的最大值, nums 1 [ cut 1 ] \textit{nums}_1[\textit{cut}_1] nums1​[cut1​] 和 nums 2 [ cut 2 ] \textit{nums}_2[\textit{cut}_2] nums2​[cut2​] 中的最小值是后半部分的最小值。根据前半部分的最大值和后半部分的最小值即可计算中位数。

  • 当 total \textit{total} total 是奇数时,中位数是前半部分的最大值。

  • 当 total \textit{total} total 是偶数时,中位数是前半部分的最大值与后半部分的最小值的平均数。

该解法的时间复杂度是 O ( log ⁡ m ) O(\log m) O(logm),优于解法一的 O ( log ⁡ ( m + n ) ) O(\log (m + n)) O(log(m+n))。

实现方面,由于只需要在一个数组中二分查找,因此可以选择较短的数组二分查找,时间复杂度是 O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m, n)) O(logmin(m,n))。

以下用一个例子说明上述过程。

两个数组是 nums 1 = [ 1 , 2 , 3 , 4 , 5 ] \textit{nums}_1 = [1, 2, 3, 4, 5] nums1​=[1,2,3,4,5], nums 2 = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ] \textit{nums}_2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] nums2​=[1,2,3,4,5,6,7,8,9,10],两个数组的长度分别是 m = 5 m = 5 m=5, n = 10 n = 10 n=10,长度之和是 15 15 15,前半部分的元素个数是 8 8 8。初始时, low = 0 \textit{low} = 0 low=0, high = 5 \textit{high} = 5 high=5。

  1. 根据 low = 0 \textit{low} = 0 low=0 和 high = 5 \textit{high} = 5 high=5 计算得到 index 1 = 3 \textit{index}_1 = 3 index1​=3, index 2 = 5 \textit{index}_2 = 5 index2​=5。由于 nums 1 [ 2 ] ≤ nums 2 [ 5 ] \textit{nums}_1[2] \le \textit{nums}_2[5] nums1​[2]≤nums2​[5],因此将 low \textit{low} low 更新为 3 3 3。

  2. 根据 low = 3 \textit{low} = 3 low=3 和 high = 5 \textit{high} = 5 high=5 计算得到 index 1 = 4 \textit{index}_1 = 4 index1​=4, index 2 = 4 \textit{index}_2 = 4 index2​=4。由于 nums 1 [ 3 ] ≤ nums 2 [ 4 ] \textit{nums}_1[3] \le \textit{nums}_2[4] nums1​[3]≤nums2​[4],因此将 low \textit{low} low 更新为 4 4 4。

  3. 根据 low = 4 \textit{low} = 4 low=4 和 high = 5 \textit{high} = 5 high=5 计算得到 index 1 = 5 \textit{index}_1 = 5 index1​=5, index 2 = 3 \textit{index}_2 = 3 index2​=3。由于 nums 1 [ 4 ] > nums 2 [ 3 ] \textit{nums}_1[4] > \textit{nums}_2[3] nums1​[4]>nums2​[3],因此将 high \textit{high} high 更新为 4 4 4。

  4. 此时 low = high \textit{low} = \textit{high} low=high,二分查找结束。根据 low = 4 \textit{low} = 4 low=4 计算得到 cut 1 = 4 \textit{cut}_1 = 4 cut1​=4, cut 2 = 4 \textit{cut}_2 = 4 cut2​=4,前半部分的最大值是 4 4 4,后半部分的最小值是 5 5 5。由于两个数组的长度之和是奇数,因此中位数是前半部分的最大值,中位数是 4 4 4。

代码

class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {return nums1.length <= nums2.length ? findMedian(nums1, nums2) : findMedian(nums2, nums1);}public double findMedian(int[] shorter, int[] longer) {int length1 = shorter.length, length2 = longer.length;int total = length1 + length2;int lowerSize = (total + 1) / 2;int low = 0, high = length1;while (low < high) {int index1 = low + (high - low + 1) / 2;int index2 = lowerSize - index1;int left1 = shorter[index1 - 1];int right2 = longer[index2];if (left1 <= right2) {low = index1;} else {high = index1 - 1;}}int cut1 = low, cut2 = lowerSize - low;int lower1 = cut1 == 0 ? Integer.MIN_VALUE : shorter[cut1 - 1];int lower2 = cut2 == 0 ? Integer.MIN_VALUE : longer[cut2 - 1];int higher1 = cut1 == length1 ? Integer.MAX_VALUE : shorter[cut1];int higher2 = cut2 == length2 ? Integer.MAX_VALUE : longer[cut2];int lowerMax = Math.max(lower1, lower2), higherMin = Math.min(higher1, higher2);if (total % 2 == 1) {return lowerMax;} else {return (lowerMax + higherMin) / 2.0;}}
}

复杂度分析

  • 时间复杂度: O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m, n)) O(logmin(m,n)),其中 m m m 和 n n n 分别是数组 nums 1 \textit{nums}_1 nums1​ 和 nums 2 \textit{nums}_2 nums2​ 的长度。在较短的数组中二分查找,范围是 [ 0 , min ⁡ ( m , n ) ] [0, \min(m, n)] [0,min(m,n)],二分查找的次数是 O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m, n)) O(logmin(m,n)),每次查找的时间是 O ( 1 ) O(1) O(1),因此时间复杂度是 O ( log ⁡ min ⁡ ( m , n ) ) O(\log \min(m, n)) O(logmin(m,n))。

  • 空间复杂度: O ( 1 ) O(1) O(1)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/pingmian/68458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【unity游戏开发之InputSystem——07】InputSystem+UGUI配合使用(基于unity6开发介绍)

文章目录 一、InputSystem+UGUI配合使用1、官方文档参考2、切换到新的输入模块二、UGUI中的新输入系统输入模块参数相关1、Send Pointer Hover To Parent2、Move Repeat Delay3、Move Repeat Rate4、XR Tracking Origin5、Deselect On Background CLick6、Pointer Behavior7、S…

uniapp使用uni.navigateBack返回页面时携带参数到上个页面

我们平时开发中也经常遇到这种场景&#xff0c;跳转一个页面会进行一些操作&#xff0c;操作完成后再返回上个页面同时要携带着一些参数 其实也很简单&#xff0c;也来记录一下吧 假设从A页面 跳转到 B页面 A页面 直接上完整代码了哈&#xff0c;很简单&#xff1a; <t…

国内优秀的FPGA设计公司主要分布在哪些城市?

近年来&#xff0c;国内FPGA行业发展迅速&#xff0c;随着5G通信、人工智能、大数据等新兴技术的崛起&#xff0c;FPGA设计企业的需求也迎来了爆发式增长。很多技术人才在求职时都会考虑城市的行业分布和发展潜力。因此&#xff0c;国内优秀的FPGA设计公司主要分布在哪些城市&a…

汇编基础语法及其示例

1.汇编指令 1.1汇编指令的基本格式 <opcode>{<cond>}{s} <Rd> , <Rn> , <shifter_operand> <功能码>{<条件码>}{cpsr影响位} <目标寄存器> , <第一操作寄存器> , <第二操作数> 注&#xff1a;第一操作寄存器…

Direct2D 极速教程(1) —— 画图形

极速导航 Direct2D 简介创建新项目&#xff1a;001-DrawGraphics弄一个白窗口在窗口上画图 Direct2D 简介 大家在学 WINAPI 的时候的时候有没有想过&#xff0c;怎么在一副窗口上画图呢&#xff1f;大家知道 Windows 系统是 GUI 图形用户界面 系统&#xff0c;以 Graphics 图形…

Android13源码下载和编译过程详解

前言 作为Android开发者人人都应该有一份自己Android源码,这样我们就可以随时对自己有疑惑的地方通过亲手调试来加强理解 一 源码下载 1.1 配置要求 官方推荐配置请参考&#xff1a;AOSP使用入门文档&#xff0c;重点有如下几项&#xff1a; 1.1.1 硬件配置要求 至少需要…

Linux之详谈——权限管理

目录 小 峰 编 程 ​编辑 一、权限概述 1、什么是权限 2、为什么要设置权限 3、Linux中的权限类别- 4、Linux中文件所有者 1&#xff09;所有者分类&#xff08;谁&#xff09; 2&#xff09;所有者的表示方法 ① u(the user who owns it)&#xff08;属主权限&…

python Flask-Redis 连接远程redis

当使用Flask-Redis连接远程Redis时&#xff0c;首先需要安装Flask-Redis库。可以通过以下命令进行安装&#xff1a; pip install Flask-Redis然后&#xff0c;你可以使用以下示例代码连接远程Redis&#xff1a; from flask import Flask from flask_redis import FlaskRedisa…

Go Fx 框架使用指南:深入理解 Provide 和 Invoke 的区别

1. 什么是 Fx 框架&#xff1f; Fx 是一个基于 Go 语言的依赖注入框架&#xff0c;专注于简化应用程序的生命周期管理和依赖的构建。在复杂的应用程序中&#xff0c;Fx 通过模块化的设计方式将组件连接起来&#xff0c;使开发者能够更高效地管理依赖关系。 Fx 的核心理念是&a…

基于金融新闻的大型语言模型强化学习在投资组合管理中的应用

“Financial News-Driven LLM Reinforcement Learning for Portfolio Management” 论文地址&#xff1a;https://arxiv.org/pdf/2411.11059 摘要 本研究探索了如何通过将大语言模型&#xff08;LLM&#xff09;支持的情感分析融入强化学习&#xff08;RL&#xff09;中&#…

K8s运维管理平台 - KubeSphere 3.x 和4.x 使用分析:功能较强,UI美观

目录标题 Lic使用感受优点&#xff1a;优化点&#xff1a; 实操首页项目 | 应用负载 | 配置 | 定制资源定义存储监控告警集群设置 **KubeSphere 3.x** 和 **4.x**1. **架构变化**&#xff1a;2. **多集群管理**&#xff1a;3. **增强的 DevOps 功能**&#xff1a;4. **监控与日…

当AI学会“顿悟”:DeepSeek-R1如何用强化学习突破推理边界?

开篇&#xff1a;一场AI的“青春期叛逆” 你有没有想过&#xff0c;AI模型在学会“推理”之前&#xff0c;可能也经历过一段“中二时期”&#xff1f;比如&#xff0c;解题时乱写一通、语言混搭、答案藏在火星文里……最近&#xff0c;一支名为DeepSeek-AI的团队&#xff0c;就…

【llm对话系统】 LLM 大模型推理python实现:vLLM 框架

在 LLM 的应用中&#xff0c;推理 (Inference) 阶段至关重要。它指的是利用训练好的 LLM 模型&#xff0c;根据输入 (Prompt) 生成文本的过程。然而&#xff0c;LLM 的推理速度往往较慢&#xff0c;尤其是在处理长序列或高并发请求时&#xff0c;效率瓶颈尤为突出。 为了解决这…

Ollama+DeepSeek本地大模型部署

1、Ollama 官网&#xff1a;https://ollama.com/ Ollama可以干什么&#xff1f; 可以快速在本地部署和管理各种大语言模型&#xff0c;操作命令和dokcer类似。 mac安装ollama&#xff1a; # 安装ollama brew install ollama# 启动ollama服务&#xff08;默认11434端口&#xf…

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(三)

Understanding Diffusion Models: A Unified Perspective&#xff08;三&#xff09; 文章概括 文章概括 引用&#xff1a; article{luo2022understanding,title{Understanding diffusion models: A unified perspective},author{Luo, Calvin},journal{arXiv preprint arXiv:…

mybatis(104/134)

动态sql标签&#xff0c;用于选择查询 if标签 where标签 &#xff1a;自动生成where&#xff0c;取决于后面有没有条件&#xff0c;会自动去除条件前面的and和or&#xff0c;不会去除语句后面的 trim标签&#xff1a;自动生成where&#xff0c;在语句后自动去除后缀and和or for…

【数据结构】动态内存管理函数

动态内存管理 为什么存在动态内存管理动态内存函数的介绍&#x1f38a;malloc补充&#xff1a;perror函数&#x1f38a;free&#x1f38a;calloc&#x1f38a;realloc 常见动态内存错误对空指针的解引用操作对动态开辟空间的越界访问对非动态开辟内存使用free释放使用free释放一…

在FreeBSD下安装Ollama并体验DeepSeek r1大模型

在FreeBSD下安装Ollama并体验DeepSeek r1大模型 在FreeBSD下安装Ollama 直接使用pkg安装即可&#xff1a; sudo pkg install ollama 安装完成后&#xff0c;提示&#xff1a; You installed ollama: the AI model runner. To run ollama, plese open 2 terminals. 1. In t…

C++类和对象下详细指南

C类和对象下详细指南 1. 初始化列表与构造函数 1.1 初始化列表概述 初始化列表在C中用于初始化对象的成员变量&#xff0c;特别是当你需要在对象构造时就明确成员变量的值时。通过初始化列表&#xff0c;成员变量的初始化可以在进入构造函数体之前完成。这不仅可以提升性能&…

文档智能扫描,提升无纸化办公效率

随着无纸化办公的推广和移动设备的普及&#xff0c;用户迫切需要将纸质文档快速、准确地转换成电子格式&#xff0c;以提高工作效率和信息管理的便捷性。同时&#xff0c;用户将文档扫描成电子版后&#xff0c;可以自行通过加密和访问控制提高电子文档的安全性&#xff0c;以满…