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

387 阅读2分钟

题目

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

题解

  • 这题更通用的形式是,给定两个排序好的数组,找到两者所有元素中第k小的元素,且所有元素是允许重复的;
  • 方法1:直接merge两个数组,然后求第k小的元素,算法复杂度O(m+n);
  • 方法2:类似于merge sort,分别使用两个指针pA和pB,如果数组A当前元素小,那么pA++,同时m++;如果数组B当前元素小,那么pB++,同时m++。当m等于k时,就找到了第k小的元素;
  • 方法3:找第k小的数,如果每次都能够删除一个一定比k小的数,那么只需要删除k次即可。如何利用有序数组呢,第一次删k/2个,第二次删k/4个,……,需要注意的是输出的个数不能长于当前数组的长度,具体如下:假设A和B的元素个数都大于k/2,将A数组的A[k/2-1]和B数组的B[k/2-1]进行比较,存在3种情况。
  1. A[k/2-1]<B[k/2-1],说明A[k/2-1]不可能是第k小数,因为即使B[0]、……、B[k/2-2]都小于A[k/2-1],也只能说A[k/2-1]是第k-1小数,即A[0]、……、A[k/2-2]这些数都必定小于第k小数,可以被删除。此外,此时也不能说明B[k/2-1]是第k小数,因为可能存在A[k/2]<B[k/2-1]的情形;
  2. A[k/2-1]==B[k/2-1],由于允许重复,故此时找到了第k小数,为A[k/2-1]或B[k/2-1];
  3. A[k/2-1]>B[k/2-1],与情况1类似。
  • 删除了k/2个元素后,现在在剩余的元素里找第k/2小数,可以用递归进行实现,但注意递归退出条件。
  1. 当A或B是空时,返回B[k-1]或A[k-1];
  2. 当k==1时,返回min(A[0], B[0]);
  3. 当A[k/2-1]==B[k/2-1]时,返回A[k/2-1]或B[k/2-1]。

源码

int find_kth(int* nums1, int nums1Size, int* nums2, int nums2Size, int k);

double findMedianSortedArrays(int* nums1, int nums1Size, int* nums2, int nums2Size){
    int total = nums1Size + nums2Size;
	if (total & 0x1)
	{
		return find_kth(nums1, nums1Size, nums2, nums2Size, total / 2 + 1) / 1.0;
	}
	else
	{
		return (find_kth(nums1, nums1Size, nums2, nums2Size, total / 2) + find_kth(nums1, nums1Size, nums2, nums2Size, total / 2 + 1)) / 2.0;
	}
}

int find_kth(int* nums1, int nums1Size, int* nums2, int nums2Size, int k)
{
	if (nums1Size > nums2Size)
	{
		return find_kth(nums2, nums2Size, nums1, nums1Size, k);
	}
	if (nums1Size == 0)
	{
		return nums2[k - 1];
	}
	if (k == 1)
	{
		return nums1[k - 1] < nums2[k - 1] ? nums1[k - 1] : nums2[k - 1];
	}
	int ia = k/2 <nums1Size ? k / 2 : nums1Size;
	int ib = k - ia;
	if (nums1[ia - 1] > nums2[ib - 1])
	{
		return find_kth(nums1, nums1Size, nums2 + ib, nums2Size - ib, k - ib);
	}
	else if (nums1[ia - 1] < nums2[ib - 1])
	{
		return find_kth(nums1 + ia, nums1Size - ia, nums2, nums2Size, k - ia);
	}
	else
	{
		return nums1[ia - 1];
	}
}