LeetCode-4-寻找两个正序数组的中位数

题目

来源:LeetCode.

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

    看到这个问题我首先想到的方法就是将两个有序数组合并,然后找中位数。

接下来看一下解题思路:

思路一(归并求解):

    首先将两个有序数组按序合并存放在一个数组里
    然后查找中位数,若有奇数个数据,直接返回中间的数据,
    若有偶数个数据,返回中间两个数的平均数
代码示例:

public double findMedianSortedArrays1(int[] nums1, int[] nums2) {
        int count1 = nums1.length;
        int count2 = nums2.length;
        int n = count1 + count2;
        //需要申请m + n空间
        int[] merge = new int[n];

        int i = 0;
        int j = 0;
        int k = 0;
        //有序合并的过程
        while(i < count1 && j < count2) {
            if(nums1[i] > nums2[j]) {
                merge[k++] = nums2[j++];
            } else {
                merge[k++] = nums1[i++];
            }
        }
        while(i < count1) {
            merge[k++] = nums1[i++];
        }
        while(j < count2) {
            merge[k++] = nums2[j++];
        }
        double result = 0.0;
        if((n & 1) == 0){
            //有偶数个元素,返回中间两个元素的平均数
            result = (merge[n / 2] + merge[(n / 2) - 1]) / 2.0;
        } else {
            //有奇数个元素,返回中间元素
            result = merge[n / 2];
        }
        return result;
    }
总结

    这样做时间复杂度 O(m + n),m为nums1的长度;n为nums2的长度;
    因为申请了合并后数组的存放空间,所以空间复杂度 O(m + n);
    其实不需要申请空间,因为知道数组元素个数 ,所以可以直接定位到中位数下标,这样就不需要申请额外的空间了。

思路二

这里看到了一位大佬的解法:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/.
解题思路:
    用 l e n len len 表示合并后数组的长度,如果数组的元素个数是奇数个,只需要知道第 l e n + 1 2 \frac{len + 1}{2} 2len+1 个数就可以了,如果遍历的话需要遍历 l e n 2 \frac{len}{2} 2len + 1 次。
    如果是偶数,我们需要知道第 l e n 2 \frac{len}{2} 2len l e n 2 \frac{len}{2} 2len + 1 个数,也是需要遍历 l e n 2 \frac{len}{2} 2len + 1 次。所以遍历的话,奇数和偶数都是 l e n 2 \frac{len}{2} 2len + 1 次。

    返回中位数的话,奇数需要最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历的结果。
    所以这里用两个变量 left 和 right,right 保存当前循环的结果,在每次循环前将 right 的值赋给 left。这样在最后一次循环的时候,left 将得到 right 的值,也就是上一次循环的结果,接下来 right 更新为最后一次的结果。

    循环中用 nums1Start 和 nums2Start 分别表示当前指向 nums1 数组和 nums2 数组的位置。如果 nums1Start 还没有到最后并且此时 nums1 位置的数字小于 nums2 位置的数组,那么就可以后移了。也就是 nums1Start<m && nums1[nums1Start]< nums2[nums2Start]。

    但如果 nums2 数组此刻已经没有数字了,继续取数字 nums2[ nums2Start ],则会越界,所以判断下 nums2Start 是否大于数组长度了,这样 || 后边的就不会执行了,也就不会导致错误了,所以增加为 nums1Start<m && (nums2Start) >= n || nums1[nums1Start] < nums2[nums2Start]) 。

public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        int len = m + n;
        int left = 0;
        int right = 0;
        int nums1Start = 0;
        int nums2Start = 0;
		//无论是奇数还是偶数,只需要遍历 len / 2 + 1 次
        for(int i = 0; i <= len / 2; ++i) {
            //这里用于保存上一个值
            left = right;
            //这里利用 || 的短路运算,防止nums2下标越界
            if(nums1Start < m && (nums2Start >= n 
                || (nums1[nums1Start] < nums2[nums2Start]))) {
                right = nums1[nums1Start++];
            } else {
                right = nums2[nums2Start++];
            }
        }

        if((len & 1) == 0){
            return (left + right) / 2.0;
        } else {
            return right;
        }
    }
总结

    时间复杂度:遍历 l e n 2 \frac{len}{2} 2len + 1 次,len=m+n,所以时间复杂度依旧是 O(m+n)。

空间复杂度:我们申请了常数个变量,所以空间复杂度是 O(1)。

思路三(二分法:找第K小的数)

大佬的解法:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-2/.
对于寻找第K小的数的代码实现很巧妙。
解题思路:
    假设数组 A A A 的长度为 m m m ,数组 B B B的长度为 n n n
    根据中位数的定义:
    当 m + n m+n m+n 是奇数时,中位数是两个有序数组中的第 m + n 2 \frac{m + n}{2} 2m+n 个元素;
    当 m + n m+n m+n 是偶数时,中位数是两个有序数组中的第 m + n 2 \frac{m + n}{2} 2m+n 个元素和第 m + n 2 + 1 \frac{m + n}{2}+1 2m+n+1 个元素的平均值。
因此,这道题可以转化成寻找两个有序数组中的第 k k k 小的数,其中 k k k m + n 2 \frac{m + n}{2} 2m+n m + n 2 + 1 \frac{m + n}{2}+1 2m+n+1

    要想找到第 k k k 个元素,我们可以比较 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21]
    由于 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21] 的前面分别有 k 2 − 1 \frac{k}{2} - 1 2k1 个元素,对于 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21] 中的较小值,最多只会有 ( k / 2 − 1 ) + ( k / 2 − 1 ) ≤ k − 2 (k/2-1)+(k/2-1) \leq k-2 (k/21)+(k/21)k2 个元素比它小,那么它就不能是第 k k k 小的数了。

因此可以归纳出三种情况:

  • 如果 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] < B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21],则比 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] 小的数最多只有 A \text{A} A 的前 k / 2 − 1 k/2-1 k/21 个数和 B \text{B} B 的前 k / 2 − 1 k/2-1 k/21 个数,即比 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] 小的数最多只有 k / 2 − 1 k/2-1 k/21 + k / 2 − 1 k/2-1 k/21 = k − 2 k−2 k2 个,因此 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] 不可能是第 k k k个数, A [ 0 ] \text{A}[0] A[0] A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] 也都不可能是第 k k k 个数,可以全部排除。
  • 如果 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] > B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21],则可以排除 B [ 0 ] \text{B}[0] B[0] B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21]
  • 如果 A [ k / 2 − 1 ] \text{A}[k/2-1] A[k/21] = B [ k / 2 − 1 ] \text{B}[k/2-1] B[k/21],则可以归入第一种情况处理。

下面举个例子来描述上述算法:
在这里插入图片描述
    假设给定如上两个数组,且数组长度分别为 3 和 6,长度之和是 9,中位数是两个有序数组中的第 m + n 2 + 1 \frac{m + n}{2} + 1 2m+n+1 = 5 个元素,因此需要找到第 k=5 个元素。

    比较两个有序数组中下标为 k / 2 - 1 = 1 的数,即 A [ 1 ] \text{A}[1] A[1] B [ 1 ] \text{B}[1] B[1],如下面所示:
在这里插入图片描述

    由于 A [ 1 ] < B [ 1 ] \text{A}[1] < \text{B}[1] A[1]<B[1],因此排除 A [ 0 ] \text{A}[0] A[0] A [ 1 ] \text{A}[1] A[1],即数组 A \text{A} A 的下标偏移(offset)变为 2,同时更新 k k k 的值: k = k − k / 2 = 3 k = k - k / 2 = 3 k=kk/2=3

    下一步寻找,比较两个有序数组中下标为 k / 2 − 1 = 0 k / 2 - 1 = 0 k/21=0 的数,即 A [ 2 ] \text{A}[2] A[2] 和 $\text{B}[0],如下面所示,其中蓝色部分表示已经被排除的数。
在这里插入图片描述
    由于 B [ 0 ] < A [ 2 ] \text{B}[0] < \text{A}[2] B[0]<A[2],因此排除 B [ 0 ] \text{B}[0] B[0],即数组 B \text{B} B 的下标偏移变为 1,同时更新 k k k 的值: k = k − k / 2 = 2 k = k - k / 2 = 2 k=kk/2=2

    下一步寻找,比较两个有序数组中下标为 k / 2 − 1 = 0 k / 2 - 1 = 0 k/21=0 的数,即比较 A [ 2 ] \text{A}[2] A[2] B [ 1 ] \text{B}[1] B[1],如下面所示,其中蓝色部分表示已经被排除的数。
在这里插入图片描述
    由于 B [ 1 ] < A [ 2 ] \text{B}[1] < \text{A}[2] B[1]<A[2],因此排除 B [ 1 ] \text{B}[1] B[1],即数组 B \text{B} B 的下标偏移变为 2,同时更新 k k k 的值: k = k − k / 2 = 1 k = k - k / 2 = 1 k=kk/2=1

    由于 k k k 的值变成 1,因此比较两个有序数组中的未排除下标范围内的第一个数,如下面所示,其中蓝色部分表示已经被排除的数。
在这里插入图片描述

其中较小的数即为第 k 个数,因此第 k k k 个数是 B [ 2 ] = A [ 2 ] = 4 \text{B}[2] = A[2] = 4 B[2]=A[2]=4

    过程中每次选取的下标值为 k / 2 − 1 k / 2 - 1 k/21 的数,可能会出现下面这种情况:其中蓝色部分表示已经被排除的数。
在这里插入图片描述
可能会遇到数组长度小于 k / 2 k / 2 k/2 的时候。此时将箭头指向它的末尾就可以了。而且在算法进行中, k k k 的值都有可能从奇数变为偶数,最终都会变为 1 或者由于一个数组空了,直接返回结果。
    采用递归的思路,为了防止数组长度小于 k/2,所以每次比较 min(k/2,len(数组) )对应的数字,把小的那个对应的数组的数字排除,将两个新数组进入递归,并且 k 要减去排除的数字的个数。递归出口就是当 k=1 或者其中一个数组长度是 0 了。

public double findMedianSortedArrays4(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        //数组中第K个数,也就是(m+n)/2 或 (m+n)/2 + 1
        int left = (m + n + 1) / 2;
        int right = (m + n + 2) / 2;;
        //将偶数和奇数的情况合并,如果是奇数,会求两次同样的 k 。
        return (findMinK(nums1, 0, m - 1, nums2, 0, n - 1, left) + findMinK(nums1, 0, m - 1, nums2, 0, n - 1, right)) / 2.0;
    }

    private int findMinK(int[] nums1, int start1, int end1, int[] nums2, int start2, int end2, int k) {
        int len1 = end1 - start1 + 1;
        int len2 = end2 - start2 + 1;
        //让 len1 的长度小于 len2,这样就能保证如果有数组空了,一定是 len1
        if(len1 > len2) {
            return findMinK(nums2, start2, end2, nums1, start1, end1, k);
        }
        //如果某一数组为空,直接返回另一数组的中位数
        if(len1 == 0) {
            return nums2[start2 + k - 1];
        }
        //k = 1,代表当前未排除数组的第一个小的元素,所以返回相对小的就是中位数
        if(k == 1) {
            return Math.min(nums1[start1], nums2[start2]);
        }

        //为了防止k / 2 大于数组长度,也就是数组下标越界,所以选取较小的,
        // 也就是说如果数组的长度比 k / 2小,那么直接定位到数组末尾就可
        int i = start1 + Math.min(len1, k / 2) - 1;
        int j = start2 + Math.min(len2, k / 2) - 1;

        if(nums1[i] > nums2[j]) {
            //j + 1,从下一个位置访问,也就是排除nums2[j]之前的元素
            //k - (j - start2 + 1)参数,下一个第K小的元素,也就是 k = k - k / 2;
            return findMinK(nums1, start1, end1, nums2, j + 1, end2, k - (j - start2 + 1));
        } else {
            return findMinK(nums1, i + 1, end1, nums2, start2, end2, k - (i - start1 + 1));
        }
    }
 }
总结

    时间复杂度:每一轮循环可以将查找范围减少一半,所以是O(log(m+n)。
    空间复杂度:O(1)。

思路四(二分法:分割数组)

    这个是官方的解题思路,看了这个思路我才直到这道题为什么是hard了。下面来看一下官方的解题思路。
     大致思路是: 先将nums1和nums2两个数组分割,满足nums1[i - 1] < nums2[j] && nums2[j - 1] < nums1[i];条件;
    并且如果两个数组的元素个数是偶数个,则分割后使左右两边的元素个数相等;如果两个数组的元素个数是奇数个,则分割后使左元素比右边个数多1;
    这个算法的核心是如何分割,从而满足nums1[i - 1] < nums2[j] && nums2[j - 1] < nums1[i];条件。
    思路:
    1.这里需要理解中位数的作用是什么?。在统计中,中位数被用来:

将一个集合划分为两个长度相等的子集,其中一个子集中的元素总是大于另一个子集中的元素。

如果理解了中位数的划分作用,就很接近答案了。
    首先,在任意位置 i ,j将 nums1和nums2数组划分成两个部分:
在这里插入图片描述
由于 nums1 中有 m 个元素, 所以有m+1 种划分的方法( i∈[0,m])。

len(leftNums1) = i, len(rightNums1) = m − i.

注意:当i = 0 时,leftNums1为空集, 而当 i = m 时, rightNums1为空集。
    采用同样的方式,在任意位置 j 将 nums2 划分成两个部分.

将 leftNums1和leftNums2放入一个集合,并将rightNums1 和 rightNums2 放入另一个集合。 再把这两个新的集合分别命名为 leftPart 和 rightPart
在这里插入图片描述
当nums1和nums2 的总长度是偶数时,如果可以确认:

len(leftPart) = len(rightPart);
max(leftPart) <= min(rightPart);

那么,{nums1, nums2} 中的所有元素已经被划分为相同长度的两个部分,且前一部分中的元素总是小于或等于后一部分中的元素。中位数就是前一部分的最大值和后一部分的最小值的平均值:

            m e d i a n median median= ( m a x ( l e f t P a r t ) + m i n ( r i g h t P a r t ) ) 2.0 \frac{(max(leftPart) + min(rightPart))}{2.0} 2.0(max(leftPart)+min(rightPart));
当 nums1和 nums2 的总长度是奇数时,如果可以确认:

len(leftPart) = len(rightPart) + 1;
max(leftPart) <= min(rightPart);

那么,{nums1, nums2}中的所有元素已经被划分为两个部分,前一部分比后一部分多一个元素,且前一部分中的元素总是小于或等于后一部分中的元素。中位数就是前一部分的最大值:
                 m e d i a n median median= m a x ( l e f t P a r t ) max(leftPart) max(leftPart)
    第一个条件对于总长度是偶数和奇数的情况有所不同,但是可以将两种情况合并。第二个条件对于总长度是偶数和奇数的情况是一样的。

要确保这两个条件,只需要保证:

  1. i + j = m − i + n − j i + j = m - i + n - j i+j=mi+nj(当 m + n m+n m+n 为偶数)或 i + j = m − i + n − j + 1 i + j = m - i + n - j + 1 i+j=mi+nj+1(当 m + n m+n m+n 为奇数)。等号左侧为前一部分的元素个数,等号右侧为后一部分的元素个数。将 i i i j j j 全部移到等号左侧,我们就可以得到 i + j = m + n + 1 2 i+j = \frac{m + n + 1}{2} i+j=2m+n+1。这里的分数结果只保留整数部分。

  2. 0 ≤ i ≤ m 0 \leq i \leq m 0im 0 ≤ j ≤ n 0 \leq j \leq n 0jn。如果我们规定 nums1 的长度小于等于 nums2 的长度,即 m ≤ n m \leq n mn。这样对于任意的 i ∈ [ 0 , m ] i \in [0, m] i[0,m],都有 j = m + n + 1 2 − i ∈ [ 0 , n ] j = \frac{m + n + 1}{2} - i \in [0, n] j=2m+n+1i[0,n],那么我们在 [0, m]的范围内枚举 i 并得到 j,就不需要额外的性质了。

  • 如果 nums1 的长度较大,那么我们只要交换 nums1 和 nums2 即可。
  • 如果 m > n m > n m>n ,那么得出的 j 有可能是负数。
  1. nums2 [ j − 1 ] ≤ nums1 [ i ] \text{nums2}[j-1] \leq \text{nums1}[i] nums2[j1]nums1[i] 以及 nums1 [ i − 1 ] ≤ nums2 [ j ] \text{nums1}[i-1] \leq \text{nums2}[j] nums1[i1]nums2[j],即前一部分的最大值小于等于后一部分的最小值。

    为了简化分析,假设 nums1 [ i − 1 ] \text{nums1}[i-1] nums1[i1], nums2 [ j − 1 ] \text{nums2}[j-1] nums2[j1], nums1 [ i ] \text{nums1}[i] nums1[i], nums2 [ j ] \text{nums2}[j] nums2[j] 总是存在。对于 i = 0 i=0 i=0 i = m i=m i=m j = 0 j=0 j=0 j = n j=n j=n 这样的临界条件,我们只需要规定 nums1 [ − 1 ] = nums2 [ − 1 ] = − ∞ \text{nums1}[-1]=\text{nums2}[-1]=-\infty nums1[1]=nums2[1]= A [ m ] = B [ n ] = ∞ \text{A}[m]=\text{B}[n]=\infty A[m]=B[n]=即可。
    这也是比较直观的:当一个数组不出现在前一部分时,对应的值为负无穷,就不会对前一部分的最大值产生影响;当一个数组不出现在后一部分时,对应的值为正无穷,就不会对后一部分的最小值产生影响。
分割数组可能会出现下面四种情况:
在这里插入图片描述
在这里插入图片描述

所以需要做的是:

在 [0, m]中找到 i,使得:
nums2 [ j − 1 ] ≤ nums1 [ i ] \text{nums2}[j-1] \leq \text{nums1}[i] nums2[j1]nums1[i] nums1 [ i − 1 ] ≤ nums2 [ j ] \text{nums1}[i-1] \leq \text{nums2}[j] nums1[i1]nums2[j]
其中 j = m + n + 1 2 − i j = \frac{m + n + 1}{2} - i j=2m+n+1i

证明它等价于:

在 [0, m] 中找到最大的 i,使得:
nums1 [ i − 1 ] ≤ nums2 [ j ] \text{nums1}[i-1] \leq \text{nums2}[j] nums1[i1]nums2[j],其中 j = m + n + 1 2 − i j = \frac{m + n + 1}{2} - i j=2m+n+1i

这是因为:

  • i i i 0 ∼ m 0 \sim m 0m 递增时, nums1 [ i − 1 ] \text{nums1}[i-1] nums1[i1] 递增, nums2 [ j ] \text{nums2}[j] nums2[j]
    递减,所以一定存在一个最大的 i i i 满足 nums1 [ i − 1 ] ≤ nums2 [ j ] \text{nums1}[i-1] \leq \text{nums2}[j] nums1[i1]nums2[j]
  • 如果 i i i 是最大的,那么说明 i + 1 i+1 i+1 不满足。将 i + 1 i+1 i+1 带入可以得到 nums1 [ i ] > nums2 [ j − 1 ] \text{nums1}[i] > \text{nums2}[j-1] nums1[i]>nums2[j1],也就是 nums2 [ j − 1 ] < nums1 [ i ] \text{nums2}[j - 1] < \text{nums1}[i] nums2[j1]<nums1[i],就和进行等价变换前 i i i 的性质一致了。

因此可以对 i i i 在 [0, m] 的区间上进行二分搜索,找到最大的满足 nums1 [ i − 1 ] ≤ nums2 [ j ] \text{nums1}[i-1] \leq \text{nums2}[j] nums1[i1]nums2[j] i i i 值,就得到了划分的方法。此时,划分前一部分元素中的最大值,以及划分后一部分元素中的最小值,才可能作为就是这两个数组的中位数。

来源:力扣(LeetCode).

代码示例:

public double findMedianSortedArrays2(int[] nums1, int[] nums2) {
        int m = nums1.length;
        int n = nums2.length;
        //确保nums1总是最短的,方便后面的计算
        if(m > n) {
            return findMedianSortedArrays2(nums2, nums1);
        }
        //分割后两个数组左边的所有元素和
        //m + (n - m + 1) / 2 等价于 m + n + 1 / 2;是为了防止n + m溢出
        //这也是为什么需要上面保证nums1元素个数最少的原因
        int totalLeft = m + (n - m + 1) / 2;
        int left = 0;
        int right = m;
        //采用二分法在[0, m]区间找
        //满足nums1[i - 1] < nums2[j] && nums2[j - 1] < nums1[i];
        //条件的过程
        while(left < right) {
            //寻找nums1数组中间下标,
            int i = left + (right - left + 1) / 2;
            //nums2数组中间下标
            int j = totalLeft - i;
            //满足这个条件,说明nums1数组找到位置偏后了,
            //需要在前面找,否则在后面找
            if(nums1[i - 1] > nums2[j]) {
                //下一轮寻找区间[left, i - 1]
                right = i - 1;
            } else {
                //下一轮寻找区间[i, right]
                left = i;
            }
        }
        //int i = left + (right - left + 1) / 2; 这里分子为什么 + 1,
        //有以下几个原因:
        //1.不用考虑数组元素个数有奇数/偶数个,因为若是奇数个,
        //分割后左边多一个元素,若是偶数个,因为是向下取整,不影响
        //2.判断nums1[i - 1] > nums2[j]条件时[i - 1]不会出现下标越界错误
        //3.执行left = i;这个时,若数组只剩下两个元素[left, right],
        //就会陷入死循环,若是+1的话,就可以跳出while循环了
        
        //语句执行跳出while循环,说明找到了满足条件的分割点
        int i = left;
        int j = totalLeft - i;
        //这里对数组下标越界的情况加以判断,为什么会出现这种情况呢?
        //就是我们找到的分割点可能在数组的最左侧或者最右侧
        //这个时候给它一个最大值或最小值,
        //保证在判断最大值和最小值的时候不被选中
        int leftNum1Max = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]);
        int rightNum1Min = (i == m ? Integer.MAX_VALUE : nums1[i]);
        int leftNum2Max = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]);
        int rightNum2Min = (j == n ? Integer.MAX_VALUE : nums2[j]);

        if(((m + n) & 1) == 0){
            //若数组有偶数个元素,返回左边最大值和右边最小值的平均数
            return ((Math.max(leftNum1Max, leftNum2Max) + 
                   Math.min(rightNum1Min, rightNum2Min)) / 2.0);
        } else {
            //若数组有奇数个元素,返回左边最大值即可
            return (double)Math.max(leftNum1Max, leftNum2Max);
        }
    }
总结

    通过 i i i j j j 的关系 j = m + n + 1 2 − i j = \frac{m + n + 1}{2} - i j=2m+n+1i,所以只需要在相对较短的数组nums1的区间[0, m]找就可以了。
    时间复杂度: O ( log ⁡ min ⁡ ( m , n ) ) ) O(\log\min(m,n))) O(logmin(m,n))),其中 m m m n n n 分别是数组 nums1 \textit{nums1} nums1 nums2 \textit{nums2} nums2的长度。查找的区间是 [0, m],而该区间的长度在每次循环之后都会减少为原来的一半。所以,只需要执行 log ⁡ m \log m logm 次循环。
    由于我们可能需要交换 nums1 \textit{nums1} nums1 nums2 \textit{nums2} nums2 使得 m ≤ n m \leq n mn,因此时间复杂度是 O ( log ⁡ min ⁡ ( m , n ) ) ) O(\log\min(m,n))) O(logmin(m,n)))

    空间复杂度:O(1)。

    上面代码的while循环里是基于 nums1 [ i − 1 ] ≤ nums2 [ j ] \text{nums1}[i-1] \leq \text{nums2}[j] nums1[i1]nums2[j]表达式分割数组的,其实也可以根据 nums2 [ j − 1 ] ≤ nums1 [ i ] \text{nums2}[j-1] \leq \text{nums1}[i] nums2[j1]nums1[i] 分割数组。

	  while(left < right) {
            //寻找nums1数组中间下标,
            int i = left + (right - left) / 2;
            //nums2数组中间下标
            int j = totalLeft - i;
            //满足这个条件,说明nums1数组找到位置偏后了,
            //需要在前面找,否则在后面找
            //由于取中位数 i 没有 +1向上取整,
            //所以取中位数 i 在[0, m]就不会取到 m;
            //那么 j 就一定会在左边取到数值,因此nums2[j - 1]
            //就不会出现下标越界错误
            if(nums2[j - 1] > nums1[i]) {
                //下一轮寻找区间[i + 1, right]
                //这里 i + 1了,所以 i 在取中位数的时候就不用 +1 
                //向上取整了 
                left = i + 1;
            } else {
                //下一轮寻找区间[left, i]
                right = i;
            }
        }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值