LeetCode第4题思悟——两个排序数组的中位数

第四题

题目要求

给定两个大小为 m 和 n 的有序数组 nums1nums2

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

你可以假设 nums1nums2 不会同时为空。

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

示例

示例一:
nums1 = [1, 3]
nums2 = [2]
The median is 2.0

示例二:
nums1 = [1, 2]
nums2 = [3, 4]
The median is (2 + 3)/2 = 2.5

我的思路

类比归并排序的思路,首先计算数组长度,然后得到计算中位数的法则;之后使用双指针分别遍历两个数组,记录移动指针的次数,当达到中位数要求时,计算出相应的值。总的来说中规中矩;

这里的遍历过程需要分以下几种情况:

  1. 两个指针都还在数组之内,就已经找到中位数;
  2. 一个数组已经遍历结束,此时需要在另一个数组当中寻找中位数;
//71 ms
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int totalLength=nums1.length+nums2.length;//计算总的数目
    boolean isEven=totalLength%2==0;
    int firstPointer=0,secondPointer=0,medianPosition,step=-1;
    int total;
    if(isEven){//计算中位数的第一个数的“下标”
        medianPosition=totalLength/2-1;//偶数个数字
    }else{
        medianPosition=totalLength/2;//奇数个数字
    }
    while(firstPointer< nums1.length&&secondPointer<nums2.length){//尚无数组遍历结束
        if(nums1[firstPointer]<=nums2[secondPointer]){
            total=nums1[firstPointer];
            firstPointer++;
        }else{
            total=nums2[secondPointer];
            secondPointer++;
        }
        step++;
        if(step==medianPosition){//已经找到中位数的第一个数
            if(isEven){
                if(firstPointer<nums1.length&&secondPointer<nums2.length){//尚无数组遍历结束,第二个数需要比较得出
                    if(nums1[firstPointer]<=nums2[secondPointer]){
                        total+=nums1[firstPointer];
                    }else{
                        total+=nums2[secondPointer];
                    }
                }else{//已经有数组遍历结束啦
                    if(firstPointer>=nums1.length){//第一个数组遍历结束
                        total+=nums2[secondPointer];
                    }else {
                        total += nums1[firstPointer];//第二个数组遍历结束
                    }
                }
                return total/2.0;
            }else{
                return total;
            }
        }
    }//尚未找到中位数的第一个数,接下来要在一个数组里寻找;
    if(firstPointer==nums1.length){
        return finishFind(step,medianPosition,secondPointer,isEven,nums2);
    }else{
        return finishFind(step,medianPosition,firstPointer,isEven,nums1);
    }
}
/**
* step:当前已经检查过的数字
* medianPosition:中位数所在的下标
* startPointer:开始寻找的下标
* isEven:是否是偶数
* nums: 中位数所在的数组
*/
public double finishFind(int step,int medianPosition,int startPointer,boolean isEven,int[] nums){
    int total;
    while(true){
        total=nums[startPointer];
        startPointer++;
        step++;
        if(step==medianPosition){
            if(isEven){
                total+=nums[startPointer];
                return total/2.0;
            }else{
                return total+0.0;
            }
        }
    }
}

优秀解法

//42ms
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
    int m = nums1.length, n = nums2.length;
    if (m < n) return findMedianSortedArrays(nums2, nums1);
    if (n == 0) return (nums1[(m - 1) / 2] + nums1[m / 2]) / 2.0;
    int left = 0, right = 2 * n;
    while (left <= right) {
        int mid2 = (left + right) / 2;
        int mid1 = m + n - mid2;
        double L1 = mid1 == 0 ? Double.MIN_VALUE : nums1[(mid1 - 1) / 2];
        double L2 = mid2 == 0 ? Double.MIN_VALUE : nums2[(mid2 - 1) / 2];
        double R1 = mid1 == m * 2 ? Double.MAX_VALUE : nums1[mid1 / 2];
        double R2 = mid2 == n * 2 ? Double.MAX_VALUE : nums2[mid2 / 2];
        if (L1 > R2) left = mid2 + 1;
        else if (L2 > R1) right = mid2 - 1;
        else return (Math.max(L1, L2) + Math.min(R1, R2)) / 2;
    }
}
//33ms
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
	int nums2length = nums2.length;
	int[] r = new int[nums1.length + nums2length];
	int index = 0;
	int j = 0;
	for (int num : nums1) {
		while (j < nums2length && num > nums2[j]) {
			r[index] = nums2[j];
			j++;
			index++;
		}
		r[index] = num;
		index++;
	}
	for (; j < nums2length; j++) {
		r[index] = nums2[j];
		index++;
	}
	int mi = r.length / 2;
	return r.length % 2 == 1 ? r[mi] : (r[mi - 1] + r[mi]) / 2.0;
}

差别在哪里

第一种方法,是真没看懂;还需要斟酌斟酌,思考思考;

第二种方法的原理则是归并排序,归并的方式是先将数组A的所有元素先放入结果数组中,然后将数组B的剩余元素放入结果中;

我的方法则显得多此一举和笨拙;

多此一举表现在缓存了用来中位数的第一个数的值,引入该变量的目的就是处理总量为偶数的情况;但是,记录该变量实际上是一次数组访问,要达到同样的效果实际上只需要记录当前遇到的最大值是来自nums1还是nums2,这样的话,就可以使用boolean来记录,并且避免访问数组元素;

笨拙表现在finishFind函数里,我选择了遍历数组而不是通过变量计算出来:还需要移动的次数为medianPosition减去step;起点是startPointer,所以中位数的第一个值的下标为startPointer+medianPosition-step;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值