4. Median of Two Sorted Arrays【Hard】

11 篇文章 0 订阅

Desciption

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)).

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

思路1

参考leetcode解题链接

该方法的时间复杂度为 O(log(min(m,n))) ,优于最常见的二分解法——转化为寻找两个排序数组的第k个最小值的时间复杂度 O(log(m+n))

循环条件

对于寻找中位数的问题,我们可以把两个数组看做均一分为二为left和right两个部分,设数组A的分界坐标为i,数组B的分界坐标为j:

 left_part          |        right_part
A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

对于left_part和right_part有如下性质:

1) len(left_part) == len(right_part)
    * i+j=m-i+n-j (or: m - i + n - j + 1)
2) max(left_part) <= min(right_part)
    * A[i-1]<=B[j]
    * A[i]>=B[j-1]

对于约束条件 i+j=mi+nj ,设 i 已知,则j=(m+n)/2i,为保证 j 的非负性,应有条件:

(m+n)/2i>=0   m>=n

同时由于计算机整式除法的向下取整,为保证 j 的精度不受损害,j应该变形为
j=(m+n+1)/2

则合并数组的中位数为

(max(A[i1],B[j1])+min(A[i]+B[j]))/2

边界条件

从上面的分析可知,在确保条件 max(left_part)<=min(right_part) 时,存在边界条件:

i=0,j=0,i=m,j=nA[i1],B[j1],A[i],B[j]

仔细思考,其实在这种情况下,说明,已经找到了我们所要求的 i j,此时可以退出循环。

二分法找中位数

根据以上分析,我们只要遍历一遍数组A,就一定能找到所需的 i 及其对应的j,此时的复杂度为 O(m) 。但是显然,我们可以利用中位数的特性,利用二分法对i进行查找:

init:
    imin=0;
    imax=m;
    i=(imin+imax)/2;
while(imin<=imax){
    j=(m+n+1)/2-i;
    if(A[i-1]>B[j]){
        //i太大,应该变小->imax=i-1
    }else if(A[i]<B[j-1]){
        //i太小,应该变大 -> imin=m+1
    }else{
        //找到了所需的i
    }
}

代码

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.empty()&&nums2.empty()){
            return 0;
        }
        int len1,len2;
        int i=0,j=0;

        //保证len1<=len2
        if(nums1.size()>nums2.size()){
            vector<int> temp = nums2;
            nums2=nums1;
            nums1=temp;
        }
        len1=nums1.size();
        len2=nums2.size();

        int imin=0,imax=len1;//注意此处imax应该等于len1,而不是len1-1
        int left=0,right=0;
        int half_len=(len1+len2+1)/2;
        while(imin<=imax){
            i=(imin+imax)/2;
            int j=half_len-i;
            if(i>0 && nums1[i-1]>nums2[j]){
                imax = i-1;
            }else if(i<len1 && nums1[i]<nums2[j-1]){
                imin = i+1;
            }else{
                //找到了正确的i和j
                if(i==0){
                    left = nums2[j-1];
                }else if(j==0){
                    left = nums1[i-1];
                }else{
                    left = max(nums1[i-1],nums2[j-1]);
                }
                //如果两个数组长度和为奇数
                if((len1+len2)%2==1){
                    return left;
                }

                if(i==len1){
                    right=nums2[j];
                }else if(j==len2){
                    right=nums1[i];
                }else{
                    right=min(nums1[i],nums2[j]);
                }
                return (left+right)/2.0;
            }
        }
        //这一句根本不会执行,只是为了给一个返回
        //正确结果一定会在while循环中被找到
        return (left+right)/2.0;

    }
};

思路2

按照常规思路,把题目变为:查找两个排序数组合并后的第k小的值,k=1,2,…,m+n,则应用二分法,其复杂度为 O(log(m+n))

在这种情况下,要注意的是两个数组的奇偶数情况的处理。

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.empty()&&nums2.empty()){
            return 0;
        }
        int len1,len2;
        len1=nums1.size();
        len2=nums2.size();
        if(len1==0&&len2==0){
            return 0;
        }
        //k=1,2...,len1+len2
        int k1=(len1+len2+1)/2;
        int k2=(len1+len2+2)/2;

        //传入有效下标
        return (getKth(nums1,0,len1-1,nums2,0,len2-1,k1)+getKth(nums1,0,len1-1,nums2,0,len2-1,k2))/2.0;
    }

    static double getKth(vector<int> &A,int al,int ar,vector<int>& B, int bl,int br, int k){
        //保证长度小的数组在前面,长度大的数组在后面
        if(ar-al>br-bl){ // 其实是ar-al+1>br-bl+1
            return getKth(B,bl,br,A,al,ar,k);
        }
        //终止条件
        if(ar<al){
            return B[bl+k-1];
        }
        if(k==1){
            return min(A[al],B[bl]);
        }

        //使用二分法,两个个数组的步长均为k/2
        int mid = k/2;
        //注意此处是i-1和j-1!
        //同时注意条件ar-al+1<k/2 :如果A的长度小于k/2,那么B中的前k/2位一定小于要找的resmid
        if(ar-al+1<mid || A[al+mid-1]>B[bl+mid-1]){
            return getKth(A,al,ar,B,bl+mid,br,k-mid);
        }else{
            return getKth(A,al+mid,ar,B,bl,br,k-mid);
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 一个 Go 程序,用来查找两个有序数组的中位数,可能如下:func FindMedian(arr1, arr2 []int) float64 { n1 := len(arr1) n2 := len(arr2) if n1 == 0 { return float64(arr2[n2/2]) } if n2 == 0 { return float64(arr1[n1/2]) } if n1 == 1 && n2 == 1 { return float64(arr1[0] + arr2[0]) / 2.0 } if n1 == 1 { if n2 % 2 == 0 { return float64(arr2[n2/2 - 1] + arr2[n2/2]) / 2.0 } else { return float64(arr2[n2/2]) } } if n2 == 1 { if n1 % 2 == 0 { return float64(arr1[n1/2 - 1] + arr1[n1/2]) / 2.0 } else { return float64(arr1[n1/2]) } } i := n1 - 1 j := n2 - 1 var median1, median2 int for i >= 0 && j >= 0 { if arr1[i] > arr2[j] { median1 = arr1[i] i-- } else { median1 = arr2[j] j-- } } if i == -1 { median2 = arr2[j-1] } else { median2 = arr1[i-1] } return float64(median1 + median2) / 2.0 } ### 回答2: 生成一个Go程序来查找两个有序数组的中位数。 首先,我们需要明确中位数的定义。对于一个有序数组,中位数是指位于数组中间位置的元素。如果数组长度为奇数,中位数就是唯一那个中间元素;如果数组长度为偶数,中位数是中间的两个元素的平均值。 接下来,我们可以使用以下步骤生成一个Go程序来找到两个有序数组的中位数: 1. 声明一个函数,例如findMedian,该函数接受两个有序数组作为输入参数。 2. 初始化变量n,用于存储两个有序数组的总长度。 3. 通过将两个有序数组合并为一个新的有序数组,找到中位数所在的位置。可以使用合并排序或使用两个指针的方法来实现这一步骤。 4. 根据总长度n的奇偶性,分别处理中位数的情况: - 如果n为奇数,返回新数组中间位置的元素作为中位数。 - 如果n为偶数,返回新数组中间两个元素的平均值作为中位数。 5. 在主函数中调用findMedian函数,传递两个有序数组作为参数,并打印出中位数结果。 下面是一个示例的Go程序,通过使用合并排序的方法找到两个有序数组的中位数: ```go package main import "fmt" func merge(nums1 []int, nums2 []int) []int { merged := make([]int, len(nums1)+len(nums2)) i, j, k := 0, 0, 0 for i < len(nums1) && j < len(nums2) { if nums1[i] < nums2[j] { merged[k] = nums1[i] i++ } else { merged[k] = nums2[j] j++ } k++ } for i < len(nums1) { merged[k] = nums1[i] i++ k++ } for j < len(nums2) { merged[k] = nums2[j] j++ k++ } return merged } func findMedian(nums1 []int, nums2 []int) float64 { merged := merge(nums1, nums2) n := len(merged) if n%2 == 0 { return float64(merged[n/2-1]+merged[n/2]) / 2.0 } else { return float64(merged[n/2]) } } func main() { nums1 := []int{1, 3} nums2 := []int{2} median := findMedian(nums1, nums2) fmt.Printf("Median is: %.2f", median) } ``` 以上示例展示了如何使用合并排序的方法找到两个有序数组的中位数。你可以根据自己的需求进行扩展和修改,以适应其他情况和更复杂的需求。 ### 回答3: 下面是一个生成 Go 程序以找到两个有序数组的中位数的示例: ```go package main import ( "fmt" ) func findMedianSortedArrays(nums1 []int, nums2 []int) float64 { m, n := len(nums1), len(nums2) if m > n { nums1, nums2, m, n = nums2, nums1, n, m } imin, imax, half_len := 0, m, (m+n+1)/2 var max_of_left, min_of_right int for imin <= imax { i := (imin + imax) / 2 j := half_len - i if i < m && nums2[j-1] > nums1[i] { imin = i + 1 } else if i > 0 && nums1[i-1] > nums2[j] { imax = i - 1 } else { if i == 0 { max_of_left = nums2[j-1] } else if j == 0 { max_of_left = nums1[i-1] } else { max_of_left = max(nums1[i-1], nums2[j-1]) } break } } if (m+n)%2 == 1 { return float64(max_of_left) } if i == m { min_of_right = nums2[j] } else if j == n { min_of_right = nums1[i] } else { min_of_right = min(nums1[i], nums2[j]) } return float64(max_of_left+min_of_right) / 2.0 } func max(a, b int) int { if a > b { return a } return b } func min(a, b int) int { if a < b { return a } return b } func main() { nums1 := []int{1, 3} nums2 := []int{2} median := findMedianSortedArrays(nums1, nums2) fmt.Printf("The median is: %.2f\n", median) } ``` 这个程序实现了在合并后的有序数组中找到中位数的逻辑。它首先判断数组的大小,然后使用二分查找的方法从较小的数组开始分割,保证左边的元素数量等于右边(或比右边多1)。然后通过比较左侧的最大值和右侧的最小值来确定中位数。如果数组长度是奇数,则直接返回中位数的值;如果是偶数,则计算左侧最大值和右侧最小值的平均值作为中位数的值。 在 main 函数中,我们定义了两个示例的有序数组 `nums1` 和 `nums2`,然后调用 `findMedianSortedArrays` 函数计算它们的中位数,并打印结果。 请注意,这只是一个示例程序,您可以根据自己的需求进行修改和扩展。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值