记LeetCode_4: Median of Two Sorted Arrays

问题描述:

  如果题目没有时间复杂度的要求的话,让人很容易就想到合并排序,遍历两个数组,然后比较两元素的大小,在第三个数组C上做插入排序,最后直接取数组C的中位数即可。时间复杂度为O(n),空间复杂度为O(m+n)。

  题目要求的时间复杂度是O(log(m+n)),关于log,嗯,与分治的思想有关。首先,想到类似折半查找的思路去解决,但是自己折腾了好久,还是没写出来。不得已,只能看看大神是怎么解的。浏览大神们的博客,自己也理解了这其中的问题。

思路

  找出两个有序数组的中位数问题可以看成找出两个数组的第K位数问题。题目中两个数组时有序的,我们可以在数组A中遍历前k/2个元素(如果数组A的数组长度大于k/2的话),在数组B中遍历k-k/2个元素即可找到第k大的元素。如果A[p]<B[q],说明数组A的前p个数可以舍弃遍历,第k大的数在剩余数组里面,转而在剩余的数组里面寻找第k-p大的数。如果A[p]>B[q],说明数组B的前q个数可以舍弃遍历,第k大的数在剩余数组里面,转而在剩余的数组里面寻找第k-q大的数。将原问题分解成更小的问题,递归下去进行查找。findKth函数的形参包含了每个数组开始遍历的起始点,是为了方便更新下一步遍历的位置。

  

   基于Java语言的代码如下:

   

public double findKth(int[] nums1,int[] nums2,int start1, int start2,int len1,int len2,int k)
	{
		//先对nums1长度进行判断
		if(len1>len2)
		{
			return findKth(nums2, nums1, start2, start1, len1, len2, k);
		}
		//判断数组nums1为空的情况,直接返回nums2数组的第k个值
		if(len1==0)
		{
			return nums2[start2+k-1];
		}
		if(k==1)
		{
			return Math.min(nums1[start1],nums2[start2]);
		}
		//nums1数组遍历下标
		int p = Math.min(k/2,len1);
		//nums2数组遍历下标
		int q = k-p;
		//针对<,>,=三种情况进行判断
		if(nums1[start1+p-1]<nums2[start2+q-1])
		{
			return findKth(nums1, nums2, start1+p, start2, len1-p, len2, k-p);//更新数组起始位置和k的大小
		}
		else if(nums1[start1+p-1]>nums2[start2+q-1])
		{
			return findKth(nums1, nums2, start1, start2+q, len1, len2-q, k-q);
		}
		else
		{
			return nums1[start1+p-1];//正好找到第k个数
		}

	}
	public double findMedianSortedArrays(int[] A, int[] B)
	{
		int Alen = A.length;
		int Blen = B.length;
		int total = Alen + Blen;
		if((total&1) >0)//这里用按位与,速度是比%2==0的判断快的
			return findKth(A, B, 0, 0, Alen, Blen, total / 2 + 1);
		else
		{
			return (findKth(A, B, 0, 0, Alen, Blen, total / 2)
						 + findKth(A, B, 0, 0, Alen, Blen, total / 2 + 1)) / 2;
		}
	}

算法的时间复杂度为O(log(m+n)),空间复杂度为O(1)。

  在看完解释以后,由于还没理解清楚,在写findKth函数上犯了错误。导致算法在空间复杂度上出了问题,代码贴出来,以当经验。

public double findKth(int[] a, int m, int[] b, int n, int k)
	{
		if(m > n)
		{
			return findKth( b, n, a, m, k);
		}
		if(m == 0)
		{
			return b[k-1];
		}
		if (k == 1)
		{
			return Math.min(a[0], b[0])
		};
		//分治的思想
		int pa = Math.min(k/2, m), pb = k - pa;//把数组a,b虚拟看成一个数组
		if(a[pa-1] < b[pb-1])//如果数组a的第pa个的值小于数组b的第pb个值
		{//舍弃以a[pa-1]为支点的左半部分
			int[] str = new int[a.length-pa+1];
			for(int i=pa;i<a.length;i++)//其实这里不必要的,既增加了代码量,又增加了复杂度
			{
				str[i-pa] = a[i];
			}
			//更新需要遍历数组的当前位置
			return findKth(str, m - pa, b, n, k - pa);
		}
		else if(a[pa-1] > b[pb-1])
		{
			int[]str2=new int[b.length-pb+1];
			for(int i=pb;i<b.length;i++)
		        {
				  str2[i-pb]=b[i];
			}
			return findKth(a, m, str2, n - pb, k - pb);
		}
		else
		{
			return a[pa-1];
		}
	}

学习参考链接:

点击打开链接


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值