寻找两个正序数组的中位数

寻找两个正序数组的中位数
 

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

方法一
基本思路:归并排序,即将两个有序的数组归并成一个更大的有序数组。要将一个数组排序,可以先递归将它分成两个有序数组,然后将结果归并。
优点:实现简单,逻辑简单,类似对一个更大的数组重新排序
缺点:需要一个额外的空间,空间复杂度高
int main()
{
    int nums1[] = {1,2,4,9};
    int nums2[] = {1,2,3,4,5,6,7,8,9,10};
    int nums1Size = 4, nums2Size = 10;      //测试数组

    float mid = 0;
    int left = 0, right = nums1Size;      //左右两个有序数组
    int len = nums1Size + nums2Size;      
    int *aux = malloc(sizeof(int) * len);  //辅助数组,用于存储两个有序数组
    int *result = malloc(sizeof(int) * len); //保存归并结果

    for(int i = 0; i < nums1Size; i++)    //装入辅助数组
    {
        aux[i] = nums1[i];
    }

    for(int j = nums1Size , m = 0; j < len; j++, m++)
    {
        aux[j] = nums2[m];
    }

  /*
  进行归并
  进行4个判断条件
  左半边数组耗尽
  右半边数组耗尽
  右半边的当前数组小于左半边数组元素
  右半边的当前数组元素大于或等于左半边数组元素
              */
for(int k = 0; k < len; k++)      { if(left > nums1Size - 1) result[k] = aux[right++]; else if(right > len) result[k] = aux[left++]; else if(aux[right] < aux[left]) result[k] = aux[right++]; else result[k] = aux[left++]; } mid = len%2 == 0 ? (result[len/2] + result[len/2 - 1])/2.0 : result[len/2];return 0; }

 程序可以正常在Code::Block上运行,但无法通过leetcode编译(不懂)

方法二

基本思路:二分查找

根据中位数的定义,对于两个有序数组,若m+n为奇数, 中位数是第(m+n)/2个数;若m+n为偶数,中位数为第(m+n)/2 与第(m+n)/2 -1个数的平均值

因此,这道题可以转化成寻找两个有序数组中的第 k 小的数

其实在两个有序数组中,寻找中位数并不需要每一个元素都遍历到。

根据有序数组的性质,可以假设中位数为第k小的数,那么对于比第k/2小的数,其实就可以直接排除了

更一般的情况 A[1] ,A[2] ,A[3],A[k/2] ... ,B[1],B[2],B[3],B[k/2] ... ,如果 A[k/2]<B[k/2] ,那么A[1],A[2],A[3],A[k/2]都不可能是第 k 小的数字。

A 数组中比 A[k/2] 小的数有 k/2-1 个,B 数组中,B[k/2] 比 B[k/2] 小的数同样有k/2-1,假设 B[k/2] 前边的数字都比 A[k/2] 小,也只有 k/2-1 个,所以比 A[k/2] 小的数字最多有 k/1-1+k/2-1=k-2个,所以 A[k/2] 最多是第 k-1 小的数。而比 A[k/2] 小的数更不可能是第 k 小的数了,所以可以把它们排除。

因此,可以归纳出以下几点

  • 若A[k/2-1]<B[k/2-1], 则A[0]~A[k/2-1]的元素都可以排除
  • 若A[k/2-1]>B[k/2-1],则B[0]~B[k/2-1]的元素都可以排除
  • 若相等,当做第一种情况处理
  • k/2>len_A(数组A的长度),毫无疑问,第k小的数,只能在数组B中

采用递归实现

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int len1 = nums1Size ,len2 = nums2Size;
    int left = (len1 + len2 + 1)/2;
    int right = (len1 + len2 + 2)/2;
  //若为奇数,重复计算2遍;若为偶数,直接出结果
    return (getKth(nums1, 0, len1 - 1, nums2, 0, len2 - 1, left) + getKth(nums1, 0, len1 - 1, nums2, 0, len2 - 1, right))*0.5;

}

int getKth(int *nums1, int start1, int end1, int *nums2, int start2, int end2, int k)
{
    int len1 = end1 - start1 + 1;
    int len2 = end2 - start2 + 1;

    if(len1 > len2)          //确保len1的长度总为最短的,方便计算
        getKth(nums2, 0, len2 - 2, nums1, 0, len1 - 1,  k);
    if(len1 == 0)            //特殊情况的处理
        return nums2[start2 + k - 1];
    if(k == 1)
        return nums1[start1] < nums2[start2] ? nums1[start1] : nums2[start2];

    int min1 = (len1 < k/2) ? len1 : k/2;
    int min2 = (len2 < k/2) ? len2 : k/2;

    int i = start1 + min1 - 1;      //可以当成指针一样理解,操控指针移动
    int j = start2 + min2 - 1;
    if(nums1[i] > nums2[j])      //排除一次,更新第k小的数
    {
        return getKth(nums1 , start1, end1, nums2, j+1, end2, k - (j - start2 + 1));
    }
    else
    {
        return getKth(nums1, i+1, end1, nums2, start2, end2, k - (i - start1 + 1));
    }

            
}

程序可以正常在Code::Block上运行,但无法通过leetcode编译(不懂)

 

附:

 

 



posted @ 2023-03-10 20:36  哈啰世界  阅读(69)  评论(0编辑  收藏  举报