方法一、暴力解法
简单说一下思路: 即先将两个数组合并为一个数组,然后对合并后的数组排序,最后求出排好序的数组的中位数。这种方法很暴力,没有利用两个子数组是有序的这一条件。合并两个数组时间复杂度O(m+n),排序一般使用快速排序,时间复杂度O((m+n)lg(m+n)),最终的时间复杂度取决于排序算法的时间复杂度,即最终时间复杂度是O((m+n)lg(m+n))。空间复杂度是O(m+n),因为要开辟额外空间存储两个数组。
方法二、借鉴归并排序的归并阶段,将两个有序数组归并为一个有序数组,然后根据数组长度奇偶性,求得相应的中位数。算法时间复杂度较优秀(O(m+n)),空间复杂度为O(m+n),和暴力解法一样。但时间复杂度还没有达到题目所要求的对数级别。
具体思路如下:
先将两个有序数组合并为一个有序数组,然后求得此有序数组的中位数。因此算法的核心是归并两个有序数组,可以借鉴归并排序里归并两个有序数组的方法。此法时间复杂度为O(m+n),因为需要遍历两个数组所有元素,空间复杂度为O(m+n),因为需要开辟新数组存储这两个数组的所有元素。

#include <stdio.h>
#include <stdlib.h>

double findMedianSortedArr(int *arr1, int arr1Size, int *arr2, int arr2Size)
{

  //merge array
  int size = arr1Size + arr2Size;
  int *arr = (int *)malloc(size * sizeof(int));
  int arr1Index = 0;
  int arr2Index = 0;
  double median;
  for (int index = 0; index < size; index++) {
    if (arr1Index < arr1Size && arr2Index < arr2Size)
    {
      if (arr1[arr1Index] <= arr2[arr2Index])
      {
        arr[index] = arr1[arr1Index];
        arr1Index++;
      }
      else
      {
        arr[index] = arr2[arr2Index];
        arr2Index++;
      }
    }
    else if (arr1Index < arr1Size && arr2Index >= arr2Size)
    {
      arr[index] = arr1[arr1Index];
      arr1Index++;
    }
    else if (arr1Index >= arr1Size && arr2Index < arr2Size)
    {
      arr[index] = arr2[arr2Index];
      arr2Index++;
    }
  }
  // 打印合并后的数组,测试的代码,可以注释掉
  //for (int j = 0; j < size; j++) {
  //  printf("%d ", arr[j]);
  //}
  
  if (size % 2 == 0)
  {
    median = (double)(arr[size / 2 - 1] + arr[size / 2]) / 2;
  }
  else
  {
    median = arr[size / 2];
  }
  return median;
}
int main(void)
{
  int arr1[] = {2, 4, 5, 7, 9, 29, 67};
  int arr2[] = {3, 6, 8, 9, 10, 13, 18};
  int size1 = sizeof(arr1) / sizeof(*arr1);
  int size2 = sizeof(arr2) / sizeof(*arr2);
  printf("arr length: %d %d\n", size1, size2);
  double median = findMedianSortedArr(arr1, size1, arr2, size2);
  printf("\n");
  for (int i = 0; i < size1; i++)
  {
    printf("%d ", arr1[i]);
  }
  printf("\n");
  for (int j = 0; j < size2; j++)
  {
    printf("%d ", arr2[j]);
  }
  printf("\n");
  printf("median is %f", median);
  printf("\n");
}
方法一和方法二思路一样,都是先合并数组为一个有序数组,再求出中位数。

方法三:二分法查找数组划分线。我们不必合并数组,而是去寻找数组的划分线,使得划分之后的数组满足如下性质:

  1. 划分线左边的所有数值都小于划分线右边的所有数值
  2. 划分线两边数字相等(两个数组长度之和为偶数)或者左边比右边多1个数字(两个数组长度之和为奇数)

那么,两个数组长度之和为偶数时:划分线左边的最大值与划分线右边的最小值的平均数就是我们要找的中位数。
两个数组长度之和为奇数时,划分线左边的最大值就是我们要找的中位数。
因此,我们的主要任务是寻找划分线,再寻找划分线时使用二分查找。
需要注意的是,我们只需要确定一个数组上的划分线,然后根据上述性质,即可确定另一个数组上的划分线。

#include <stdio.h>
#include <limits.h>
double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    if (nums1Size > nums2Size) {
        int* temp = nums1;
        nums1 = nums2;
        nums2 = temp;
        int tempNum = nums1Size;
        nums1Size = nums2Size;
        nums2Size = tempNum;
    }
    int left = 0;
    int right = nums1Size;
    int bothLeftLen = (nums1Size + nums2Size + 1) / 2;
    int i, j;
    while (left < right) {
        i = left + (right - left + 1) / 2;
        j = bothLeftLen - i;
        if (nums1[i - 1] > nums2[j]) {
            right = i - 1;
        } else {
            left = i;
        }
    }
    i = left;
    j = bothLeftLen - i;
    int leftNums1Max = i == 0 ? INT_MIN : nums1[i - 1];
    int rightNums1Min = i == nums1Size ? INT_MAX : nums1[i];
    int leftNums2Max = j == 0 ? INT_MIN : nums2[j - 1];
    int rightNums2Min = j == nums2Size ? INT_MAX : nums2[j];
    int leftMax = leftNums1Max > leftNums2Max ? leftNums1Max : leftNums2Max;
    int rightMin = rightNums1Min < rightNums2Min ? rightNums1Min : rightNums2Min;
    if ((nums1Size + nums2Size) % 2 == 0)
    {
      return (double)(leftMax + rightMin) / 2;
    }
    else
    {
      return leftMax;
    }
}

int main () {
  int arr1[] = {2, 4, 5};
  int arr2[] = {1, 3};
  int len1 = sizeof(arr1) / sizeof(*arr1);
  int len2 = sizeof(arr2) / sizeof(*arr2);
  double median = findMedianSortedArrays(arr1, len1, arr2, len2);
  printf("%f", median);
}

阿料
1 声望4 粉丝

一个平凡的coder