LeetCode-Median of Two Sorted Arrays

算法分析与设计,第一周博客

Median of Two Sorted Arrays

Description:

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
本题的大致意思就是说,有两个已经排好序的数组,要求找到两个数组的中位数,并且要求算法的时间复杂度为  O(log (m+n))。

首先不考虑考虑算法的复杂度,那么最简单的方法就是把这两个有序的数组合并成一个有序数组,然后在去找这个合并后的数组的中位数。

 public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length, m = nums2.length;
        int sum = n+m;
        int[] merge = new int[sum];
        int i = 0, j = 0, k = 0;
        while (i < n && j < m) {
            if (nums1[i] < nums2[j]) {
                merge[k] = nums1[i];
                ++i;
            } else {
                merge[k] = nums2[j];
                ++j;
            }
            ++k;
        }
        while (i < n) {
            merge[k++] = nums1[i++];
        }
        while (j < m) {
            merge[k++] = nums2[j++];
        }
        if (sum%2 == 0) {
        	double one = merge[sum/2-1];
        	double two = merge[sum/2];
        	return (one+two)/2;
        } else {
        	return (double)merge[sum/2];
        }
    }
可以看出来,因为需要把两个数组合并,所以它的时间复杂度为O(n+m),并不符合题目的要求。

现在,考虑O(log(m+n))的算法。在常用的算法里面,能够达到O(log(n))数量级的算法最常见的就是二分法了:每次把大问题分成两部分,通过判断,把其中的一部分删去,留下另一部分进行递归求解,直到问题规模小到可以直接解决。

那么,对于这一题,应该如何应用二分法呢。我的解题思路是:因为有两个数组,需要找到的是中位数,也就是处于数组中间的那个数。设中位数在整个数组中的位置是第k位,那么就去分别找这两个数组的第k/2位的数字(设为a和b,分别对应数组A和数组B),并比较它们的大小,情况无非有以下两种:

  1. a > b:那么表明第k位的数在A的前半部分和B的后半部分之间。
  2. a <= b:那么表明第k位的数在A的后半部分和B的前半部分之间。

有了这些结论,就可以递归调用不断的减小数组的范围。

                int half = k/2;
		int mid1 = i+half >= n ? n-1 : i+half;
		int mid2 = j+(half) >= m ? m-1 : j+(half);
		int key1 = nums1[mid1];
		int key2 = nums2[mid2];
		if (key1 < key2) {
			// 数组2指向第1个元素,并且数组1指向的元素恰好是第k个元素,则返回数组1所指的元素,下同
			if (mid2 == j && mid1-i == k) {
				return key1;
			} 
			// 如果key1是数组1的第一个元素,那么key1也排除,下同
			if (mid1 <= i)
				++mid1;
			// 把搜索范围缩小到数组1的[mid1,n),数组2的[j,m),并且搜索元素变为第(k-(mid1-i))位,下同
			return search(nums1, mid1, n, nums2, j, m, k-(mid1-i));
		} else {
			if (mid1 == i && mid2-j == k) {
				return key2;
			}
			if (mid2 <= j)
				++mid2;
			return search(nums1, i, n, nums2, mid2, m, k-(mid2-j));
		} 

接下来考虑这个算法的时间复杂性。两个数组的和为n+m,需要寻找的数为第k位。第i次递归可以减少k/(2^i))的搜索范围:

  1. 第零次的搜索范围是[0,n)和[0,m),第k位。
  2. 第一次的搜索范围是[k/2,n)和[0,k/2),第k/2位。
  3. ...
  4. 第i次的搜索范围是[k/2+k/4+...+k/(2^i),n), [0, k/(2^i)),第k/(2^i)位。
那么搜索将在i=log(k)时停止,而每次搜索的花销是若干次取值与比较,时间复杂度是O(1),那么这个算法的总体复杂度是: O(log(k)) = O(log(n+m)),符合题目的要求。

整个程序如下:

public class Solution {
	public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int n = nums1.length, m = nums2.length;
        int sum = n+m;
        if (sum%2 == 0) {
            // 总个数为偶数时,需要取中间两个元素的平均数
        	double one = search(nums1, 0, n, nums2, 0, m, sum/2-1);
        	double two = search(nums1, 0, n, nums2, 0, m, sum/2);
        	return (one+two)/2;
        } else {
        	return search(nums1, 0, n, nums2, 0, m, sum/2);
        }
    }
    // 搜索数组1的[i,n),数组2的[j,m),搜索元素为第k位
	private double search(int[] nums1, int i, int n, int[] nums2, int j, int m, int k) {
		// 如果数组nums1已经检查完毕,那么就直接去数组nums2里面直接找,下同
		if (i >= n && j+k < m) {
			return nums2[j+k];
		}
		if (j >= m && i+k < n) {
			return nums1[i+k];
		}
        // 已经只剩一个位置了,只需要返回两个数组中更小的那个
		if (k <= 0) {
			return nums1[i] < nums2[j] ? nums1[i] : nums2[j];
		}
		int half = k/2;
		int mid1 = i+half >= n ? n-1 : i+half;
		int mid2 = j+(half) >= m ? m-1 : j+(half);
		int key1 = nums1[mid1];
		int key2 = nums2[mid2];
		if (key1 < key2) {
			// 数组2指向第1个元素,并且数组1指向的元素恰好是第k个元素,则返回数组1所指的元素,下同
			if (mid2 == j && mid1-i == k) {
				return key1;
			} 
			// 如果key1是数组1的第一个元素,那么key1也排除,下同
			if (mid1 <= i)
				++mid1;
			// 把搜索范围缩小到数组1的[mid1,n),数组2的[j,m),并且搜索元素变为第(k-(mid1-i))位,下同
			return search(nums1, mid1, n, nums2, j, m, k-(mid1-i));
		} else {
			if (mid1 == i && mid2-j == k) {
				return key2;
			}
			if (mid2 <= j)
				++mid2;
			return search(nums1, i, n, nums2, mid2, m, k-(mid2-j));
		} 
	}
    
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值