寻找两个正序数组的中位数(C语言)

题目:

        给定两个大小分别为 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

方法:二分查找

当两个数组总长度为奇数时,中位数表示为第 numsSize/2+1 个元素,当数组长度为偶数时,中位数则为第 numsSize/2 个元素和第 numsSize/2+1 个元素的平均数。因此,我们可以理解这道题是查找第 k 小的数,k 则表示 numsSize/2 或 numsSize/2+1 。

以一个例子来解释一波

A :  1 3 4 9

B :  1 2 3 4 5 6 7 8 9

两数组长度为13,所以 k 为 7,有两个数组,所以我们可以把 k 二分,分别在两个数组里排除缩短时间,最多也只会有 A[0...k/2-1] 和 B[0...k/2-1] 个元素被排除,进而缩小范围,找到答案。(词穷了有点,不知道咋描述,可以看官方解析)

所以,第一次二分,我们需要找到两个数组中下标为 k/2-1=2 的数,即 A[2] ,B[2]

A :  1 3 4 9

B :  1 2 3 4 5 6 7 8 9

显然,A[2] >B[2] ,所以,B[0] 到 B[2] 一定不会是我们要找的第 k 个数,将其排除

同时,更新 k 值,k = k- k/2 =4。

第二次二分,k/2 -1=1, 即 A[1] B[4] 

A :  1 3 4 9

B :  1 2 3 4 5 6 7 8 9

A[1] < B[4] ,同理,排除 A[0] 和 A[1],k = 2

第三次二分,k/2 -1 =0, 即 A[2] B[3]

A :  1 3 4 9

B :  1 2 3 4 5 6 7 8 9

相等了,随意排除一个就可以,假设排除 A

k = k - k/2 =1

k =1 了,这个时候,我们就只需要找两个数组中较小的那个,就是我们要找的第 k 个数。

A :  1 3 4 9

B :  1 2 3 4 5 6 7 8 9

所以,B[3] = 4 就是我们要找的答案。

代码:

int erfen(int *nums1,int *nums2, int nums1Size,int nums2Size, int k){
    int i=0,j=0; //i,j 记住 k/2-1 的老位置,方便下一次改变
    while(true){
        //边界情况,只有一边存在数字,直接返回另一个数组的中值即可
        if(nums1Size==i){
            return nums2[j+k-1];
        }
        if(nums2Size==j){
            return nums1[i+k-1];
        }
        //最后一步就要找到结果了,返回两个值中较小的一个,就是结果
        if(k==1){
            return nums1[i]>nums2[j]?nums2[j]:nums1[i];
        }
        //中间不断判断更新k值的过程
        int newi =(i + k / 2 - 1)>(nums1Size - 1)?nums1Size - 1:(i + k / 2 - 1);
        int newj =(j + k / 2 - 1)>(nums2Size - 1)?nums2Size - 1:(j + k / 2 - 1);
        int pivot1 = nums1[newi];
        int pivot2 = nums2[newj];
        //比较这两个数 
        if (pivot1 <= pivot2) {
            k -= newi - i + 1;
            i = newi + 1;
        }
        else {
            k -= newj - j + 1;
            j = newj + 1;
        }
    }
}
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int numsSize=nums1Size+nums2Size;
    if(numsSize%2==1){
        return(erfen(nums1,nums2,nums1Size,nums2Size,(numsSize+1)/2));
    }
    else{
        return (erfen(nums1, nums2,nums1Size, nums2Size,numsSize / 2) + erfen(nums1,nums2,nums1Size,nums2Size, numsSize / 2 + 1)) / 2.0;  //PS:2.0很重要,要注意小数
    }
}

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
解法:二分查找 题目要求时间复杂度为 O(log(m+n)),可以考虑使用二分查找。 假设我们要在两个有序数组 nums1 和 nums2 中找到第 k 小的数,我们可以分别在 nums1 和 nums2 中查找第 k/2 小的数,设其为 mid1 和 mid2,比较这两个数的大小: - 如果 mid1 小于 mid2,则说明 nums1 中的前 k/2 个数都不是第 k 小的数,因为这些数加起来也不到 k,所以我们可以将 nums1 中的前 k/2 个数全部排除。更新 k 的值,让 k 减去 nums1 中排除的数的个数,同时让 nums1 的起始位置向后移动 k/2 个位置。 - 如果 mid1 大于 mid2,则说明 nums2 中的前 k/2 个数都不是第 k 小的数,因为这些数加起来也不到 k,所以我们可以将 nums2 中的前 k/2 个数全部排除。更新 k 的值,让 k 减去 nums2 中排除的数的个数,同时让 nums2 的起始位置向后移动 k/2 个位置。 - 如果 mid1 等于 mid2,则说明找到了第 k 小的数,直接返回 mid1 或 mid2 即可。 注意边界情况: - 当其中一个数组的起始位置超过数组长度,说明该数组已经全部排除,直接返回另一个数组的第 k 小的数。 - 如果 k=1,则两个数组中的最小值即为第一小的数,直接返回两个数组的起始位置指向的数中的最小值。 具体实现见下面的代码: double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){ int totalSize = nums1Size + nums2Size; if (totalSize % 2 == 1) { // 总数为奇数 return findKthSmallest(nums1, nums1Size, nums2, nums2Size, totalSize / 2 + 1); } else { // 总数为偶数 return (findKthSmallest(nums1, nums1Size, nums2, nums2Size, totalSize / 2) + findKthSmallest(nums1, nums1Size, nums2, nums2Size, totalSize / 2 + 1)) / 2.0; } } // 在 nums1 和 nums2 中查找第 k 小的数 int findKthSmallest(int* nums1, int nums1Size, int* nums2, int nums2Size, int k) { if (nums1Size > nums2Size) { // 保证 nums1 的长度不大于 nums2 的长度 return findKthSmallest(nums2, nums2Size, nums1, nums1Size, k); } if (nums1Size == 0) { // nums1 数组为空 return nums2[k - 1]; } if (k == 1) { // 找第一小的数,即两个数组中的最小值 return fmin(nums1[0], nums2[0]); } int mid1 = fmin(nums1Size, k / 2); // 在 nums1 中查找第 k/2 小的数 int mid2 = k - mid1; // 在 nums2 中查找第 k-mid1 小的数 if (nums1[mid1 - 1] < nums2[mid2 - 1]) { // nums1 的前 mid1 个数都不是第 k 小的数 return findKthSmallest(nums1 + mid1, nums1Size - mid1, nums2, nums2Size, k - mid1); } else if (nums1[mid1 - 1] > nums2[mid2 - 1]) { // nums2 的前 mid2 个数都不是第 k 小的数 return findKthSmallest(nums1, nums1Size, nums2 + mid2, nums2Size - mid2, k - mid2); } else { // 找到了第 k 小的数 return nums1[mid1 - 1]; } } 时间复杂度分析:每次查找时,我们都可以排除掉 k/2 个数,因此最多需要查找 log(k) 次,即时间复杂度为 O(log(k))。由于 k 最大为 m+n,因此时间复杂度为 O(log(m+n))。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值