LeetCode300题计划——4. 寻找两个正序数组的中位数

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

package ListNode;

public class findMedianSortedArrays {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

	}

}

class Solution4 {

	public double findMedianSortedArrays(int[] nums1, int[] nums2) {

		// 1.先比较两个数组的长度,让nums1存放较短的数组
		// 为了保证需要查找对比的分界线两边的数的下标不越界,我们必须从较短的数组上寻找分割线的位置
		if (nums1.length > nums2.length) {
			int[] temp = nums1;
			nums1 = nums2;
			nums2 = temp;
		}

		int m = nums1.length;
		int n = nums2.length;

		// 2.分割线左边的所有元素需要满足的个数 m + (n - m + 1) / 2;
		// 如果是总元素个数是偶数个那么左右两边相等,如果是奇数个,那个左边多一个元素
		// m+n 为奇数,分割线要求左侧有 (m+n)/2 + 1 个元素
        // m+n 为偶数,分割线要求左侧有 (m+n)/2     个元素
        // 两种情况其实可以统一写作 (m+n+1)/2,表示对(m+n)/2向上取整
		int totalLeft = (m + n + 1) / 2;

		// 3.在 nums1 的区间 [0, m] 里查找恰当的分割线,保证分割线左边的数始终比右边的小
		// 使得 nums1[i - 1] <= nums2[j] && nums2[j - 1] <= nums1[i]
		int left = 0;
		int right = m;

		// 二分查找法
		/*
		 * 分割线左侧元素小于等于分割线右侧元素。由于两个数组均为正序数组,则只需要要求: 
		 * nums1[i-1] <= nums2[j] && nums2[j-1] <= nums1[i]; 
		 * 由于该条件等价于在[0, m]中找到最大的i使得nums1[i-1] <= nums2[j],
		 * 因此可以使用二分查找。(证明:假设我们已经找到了满足条件的最大i, 使得nums1[i-1] <= nums2[j],
		 * 那么此时必有nums[i] > nums2[j],进而有nums[i] > nums2[j-1])。 
		 * 当 i 从 0 ∼m 递增时,A[i−1] 递增,B[j]递减,所以一定存在一个最大的i满足A[i−1]≤B[j];
		 * 如果i是最大的,那么说明 i+1 不满足。将 i+1 带入可以得到A[i]>B[j−1],也就是B[j−1]<A[i],
		 * 就和我们进行等价变换前 i 的性质一致了(甚至还要更强)。
		 * 
		 */
		while (left < right) {
			// i就是nums1数组分割线右边第一个数的下标
			int i = left + (right - left + 1) / 2;  
			// j就是nums2数组分割线右边第一个数的下标
			int j = totalLeft - i;

			if (nums1[i - 1] > nums2[j]) {
				// 下一轮搜索的区间 [left, i - 1]
				right = i - 1;
			} else {
				// 下一轮搜索的区间 [i, right]
				left = i;
			}
		}

		// 退出循环时left=right,表示最终nums1中分割线的位置
		int i = left;
		int j = totalLeft - i;

		//判断极端情况
        int nums1LeftMax = (i == 0) ? Integer.MIN_VALUE : nums1[i - 1];  //nums1分割线左边没有元素
        int nums2LeftMax = (j == 0) ? Integer.MIN_VALUE : nums2[j - 1];  //nums2分割线左边没有元素
        int nums1RightMin = (i == m) ? Integer.MAX_VALUE : nums1[i];     //nums1分割线右边没有元素
        int nums2RightMin = (j == n) ? Integer.MAX_VALUE : nums2[j];     //nums2分割线右边没有元素

		if (((m + n) % 2) == 1) {
			return Math.max(nums1LeftMax, nums2LeftMax);
		} else {
			//返回左边数组中最大的值和右边数组中最小的值的和
			return (double) ((Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin))) / 2;
		}
	}
}
上一篇:LeetCode(力扣) 496题:下一个更大元素 I----栈+HashMap求解附带详细注释


下一篇:leetcode 1855. 下标对中的最大距离