LeetCode(4):寻找两个有序数组的中位数 Median of Two Sorted Arrays(Java)

234 篇文章 1 订阅
177 篇文章 0 订阅

2019.4.11 #程序员笔试必备# LeetCode 从零单刷个人笔记整理(持续更新)

最近做了不少笔试,很多公司笔试都有在LeetCode原题上修改过来,而且最近各个公司出hard题的比例越来越高了。最近有做了腾讯、华为、百度、网易、招行、携程、心动网络、趋势科技。比如百度开发就出了三道hard,腾讯网易会有一道hard,华为最后一题根本没有思路。期待近期阿里的笔试。

这道题有两个思路:

1.两个数组归并,复杂度o(m+n)

2.快排思想,思路为从两个数组中找第K小的数,复杂度o(log(m+n))

快排函数具体思路如下,对于输入的数组nums1与nums2,求第k小数:

递归返回条件:

1.区分奇偶情况进行快排

2.保持元素较少的数组为nums1,当前一个数组为空时,直接返回后一个数组第k-1个数

3.当k = 1时返回两个数组头元素的较小值

递归函数体:

1.在任意位置(如k/2)将nums1和nums2分别按loc1和loc2划分成两份,使得loc1为k/2与length1的较小值,loc1与loc2的和为k(划分如下图)

            left_part        |        right_part
    A[0], A[1], ..., A[i-1]  |  A[i], A[i+1], ..., A[m-1]
    B[0], B[1], ..., B[j-1]  |  B[j], B[j+1], ..., B[n-1]

2.若nums1第loc1小值比nums2第loc2小值要小,说明nums1的前loc1小值中不存在第k小值,其出现在nums1的后半数组与nums2中,截取后半数组进行递归求解。反之同理。

3.当nums1第loc1小值与nums2第loc2小值相等时,返回该值。


传送门:寻找两个有序数组的中位数

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)).

You may assume nums1 and nums2 cannot be both empty.

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。

请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

你可以假设 nums1 和 nums2 不会同时为空。

示例 1:
nums1 = [1, 3]
nums2 = [2]
则中位数是 2.0

示例 2:
nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5


/**
 * 
 * @author ChopinXBP
 * 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)).
 * You may assume nums1 and nums2 cannot be both empty.
 * 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
 * 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。
 * 你可以假设 nums1 和 nums2 不会同时为空。
 * 延伸:求有序数组中第K小的数
 * 
 */

import java.util.ArrayList;
import java.util.Arrays;

public class MedianofTwoSortedArrays {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] nums1 = {3};
		int[] nums2 = {1, 2};
		System.out.println(findMedianSortedArrays(nums1, nums2));
		System.out.println(findMedianSortedArrays1(nums1, nums2));
		System.out.println(findMedianSortedArrays2(nums1, nums2));
		System.out.println(findMedianSortedArrays3(nums1, nums2));
	}
	
	//在线处理,复杂度o(m+n)
    public static double findMedianSortedArrays(int[] nums1, int[] nums2) {
        if(nums1.length == 0) {
        	int length = nums2.length;
        	if((length & 0x01) == 1) {
        		return (double)nums2[length / 2];
        	}else {
        		return (double)(nums2[length / 2 - 1] + nums2[length / 2]) / 2; 
        	}
        }
        else if(nums2.length == 0) {
        	int length = nums1.length;
        	if((length & 0x01) == 1) {
        		return (double)nums1[length / 2];
        	}else {
        		return (double)(nums1[length / 2 - 1] + nums1[length / 2]) / 2; 
        	}
        }
        
        int length1 = nums1.length;
        int length2 = nums2.length;
        int idx = (length1 + length2) / 2;

        int num1 = 0;	//记录数组1遍历数字个数
        int num2 = 0;	//记录数组2遍历数字个数
        int smaller;
        int bigger;
        if(nums1[0] < nums2[0]) {
        	bigger = nums1[0];
        	smaller = nums1[0];
        	num1 = 1;
        }else {
        	bigger = nums2[0];
        	smaller = nums2[0];
        	num2 = 1;
        }
        
        //区分奇偶情况,循环运算次数不同
        int total;
        boolean flag;
        if(((length1 + length2) & 0x01) == 1) {
        	total = idx + 1;
        	flag = true;
        }else {
        	total = idx;
        	flag = false;
        }
        
    	//每次推进并更新smaller和bigger,令smaller位于中位数idx,bigger位于idx+1
    	while(num1 + num2 <= total) {
    		if(num1 < length1 && num2 < length2) {
    			smaller = bigger;
    			if(nums1[num1] < nums2[num2]) {
    				bigger = nums1[num1];	//nums1[num1+1-1];
    				num1++;
    			}else {
    				bigger = nums2[num2];
    				num2++;
    			}
    		}
    		else if(num1 == length1) {
    			smaller = bigger;
    			bigger = nums2[num2];
    			num2++;
    		}
    		else if(num2 == length2) {
    			smaller = bigger;
    			bigger = nums1[num1];
    			num1++;
    		}
    	}
    	
        if(flag) {
        	return smaller;
        }else {
        	return (double)(smaller + bigger) / 2;
        }
        
        
    }

    //归并,复杂度o(m+n)
	public static double findMedianSortedArrays1(int[] nums1, int[] nums2) {
        if(nums1.length == 0) {
        	int length = nums2.length;
        	if((length & 0x01) == 1) {
        		return (double)nums2[length / 2];
        	}else {
        		return (double)(nums2[length / 2 - 1] + nums2[length / 2]) / 2; 
        	}
        }
        else if(nums2.length == 0) {
        	int length = nums1.length;
        	if((length & 0x01) == 1) {
        		return (double)nums1[length / 2];
        	}else {
        		return (double)(nums1[length / 2 - 1] + nums1[length / 2]) / 2; 
        	}
        }
        
        int idx = (nums1.length + nums2.length) / 2;
        ArrayList<Integer> nums = new ArrayList<>();
        int loc1 = 0;
        int loc2 = 0;
        
        while(nums.size() <= idx) {
        	if(loc1 == nums1.length) {
        		nums.add(nums2[loc2]);
        		loc2++;
        	}
        	else if(loc2 == nums2.length) {
        		nums.add(nums1[loc1]);
        		loc1++;
        	}
        	else if(nums1[loc1] < nums2[loc2]){
        		nums.add(nums1[loc1]);
        		loc1++;
        	}
        	else {
        		nums.add(nums2[loc2]);
        		loc2++;
        	}
        }
        
        if(((nums1.length + nums2.length) & 0x01) == 1) {
        	return nums.get(idx);
        }else {
        	return (double)(nums.get(idx - 1) + nums.get(idx)) / 2;
        }
	}
    
    //循环二分,思路为从两个数组中找第K小的数,复杂度o(log(m+n))
	public static double findMedianSortedArrays2(int[] nums1, int[] nums2) {
		int m = nums1.length;
		int n = nums2.length;
		if (m > n) { // to ensure m<=n
			int[] temp = nums1;
			nums1 = nums2;
			nums2 = temp;
			int tmp = m;
			m = n;
			n = tmp;
		}
		int iMin = 0, iMax = m, halfLen = (m + n + 1) / 2;
		while (iMin <= iMax) {
			int i = (iMin + iMax) / 2;
			int j = halfLen - i;
			if (i < iMax && nums2[j - 1] > nums1[i]) {
				iMin = i + 1; // i is too small
			} else if (i > iMin && nums1[i - 1] > nums2[j]) {
				iMax = i - 1; // i is too big
			} else { // i is perfect
				int maxLeft = 0;
				if (i == 0) {
					maxLeft = nums2[j - 1];
				} else if (j == 0) {
					maxLeft = nums1[i - 1];
				} else {
					maxLeft = Math.max(nums1[i - 1], nums2[j - 1]);
				}
				if ((m + n) % 2 == 1) {
					return maxLeft;
				}

				int minRight = 0;
				if (i == m) {
					minRight = nums2[j];
				} else if (j == n) {
					minRight = nums1[i];
				} else {
					minRight = Math.min(nums2[j], nums1[i]);
				}

				return (maxLeft + minRight) / 2.0;
			}
		}
		return 0.0;
	}
	
	//递归二分,思路为从两个数组中找第K小的数,复杂度o(log(m+n))
	public static double findMedianSortedArrays3(int[] nums1, int[] nums2) {
		int idx = (nums1.length + nums2.length) / 2;
		
		if(((nums1.length + nums2.length) & 0x01) == 1) {
			return findKth(nums1, nums2, idx + 1);
		}else {
			return (findKth(nums1, nums2, idx) + findKth(nums1, nums2, idx + 1)) / 2.0;
		}
	}
	
	//求两排序数组第k小数
	public static double findKth(int[] nums1, int[] nums2, int k) {
		int length1 = nums1.length;
		int length2 = nums2.length;		
		//始终保持元素较少数组位于前面的位置
		if(length1 > length2) {
			return findKth(nums2, nums1, k);
		}
		//若前一个数组为空,则直接返回后一个数组第k小数
		if(length1 == 0) {
			return nums2[k - 1];
		}
		//若k为1,则返回两个数组头元素的较小值
		if(k == 1) {
			return nums1[0] < nums2[0] ? nums1[0] : nums2[0];
		}
		
		//将nums1和nums2分别按loc1和loc2划分成两份,使得loc1与loc2的和为k
		int loc1 = k / 2 < length1 ? k / 2 : length1;	//loc1取length1与k/2中的较小值
		int loc2 = k  - loc1;
		
		//若nums1第loc1小值比nums2第loc2小值要小,说明前loc1小值不存在第k小值,其出现在nums1的后半数组与nums2中,截取数组递归求解
		if(nums1[loc1 - 1] < nums2[loc2 - 1]) {
			int[] newarray = Arrays.copyOfRange(nums1, loc1, length1);
			return findKth(newarray, nums2, k - loc1);
		}
		//反之同理
		else if(nums1[loc1 - 1] > nums2[loc2 - 1]) {
			int[] newarray = Arrays.copyOfRange(nums2, loc2, length2);
			return findKth(nums1, newarray, k - loc2);
		}
		//当nums1第loc1小值与nums2第loc2小值相等时,返回该值
		else{
			return nums1[loc1 - 1];
		}
	}
}


#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值