leetcode 4——Median of Two Sorted Arrays

    这道题很难,看了别人的代码都还搞了一天阿。。。

        记录一下解题思路。

        首先要区分中位数,不是平均数!该题是要找中位数,如果序列是奇数个,那么直接取中间的,如果是偶数个,那么取中间的两个数的平均数,所以,核心问题是,如何把两个排好的序列整合成一个序列,并且时间复杂度要求低于:o(log(m+n))。

        既然要求算法时间复杂度在log级别,那肯定和二分或者二叉树之类的带二分思想的东西有关。

        慢慢分析,假设这里有两个数组:

        nums1 [1,3*,4,5];

        nums2 [2,3,6,6,8,9],我们要找的是这两个数组合起来的中位数,这里一共有10个数,所以中位数一定是中间两个数的平均,即中位数把序列划分成两部分:{小于中位数的部分}(A),中位数,{大于中位数的部分}(B)

        假设nums1中的3*是B集合中的第一个,即排列后的10个数为:

        x x x x x | 3* x x x x

        也就是说3*的前面有5个比它小的数,但是昕仔nums1中3*前面只有一个数,不够,因此,要从nums2中从前往后再取4个,因此:nums2 = [ 2,3,6,6,8*,9],即要把8*前面的数字也放到合并后的A集合中,合并后得到的整个序列为:1,2,3,5,5,3*,4,5,8,9。但是很明显,3*不能比A集合中最大的数大,因此3*不可能是B集合中的第一个。(其实如果nums1序列中星号数字的位置确定 i,那么nums2中星数字的位置 j 也能确定了,j = half - i)。接着刚才的分析,因为 8* 的前一个数字(5)大于3*(只需要看前一个数字即可),所以不成立,表明3小了,因此 i 往后移动,那么 j 就只能往前移动了,y因为 j = half - i . 

       i 后移 ,j 前移的结果:

       nums1 : [1,3,4*,5]

       nums2:[2,3,6,6*,8,9]

       nums2中6*的前一个数字6大于nums1中的4*,因此还不对,还要 i 往前移动,     

       nums1 : [1,3,4,5*]

       nums2:[2,3,6*,6,8,9]

       nums1中5*的前一个数字4 < nums2中6*

       nums2中6*的前一个数字3 < nums1中5*

       OK。

       因此,前5个数字找到:1,2,3,3,4,后5个数字:5*,6*,6,8,9

       中位数:[max(nums1中5*的前一个数字,nums2中6*的前一个数字)+ min(5*,6*)] / 2。

       但是这个算法描述出来, 虽然可以写了,但是i 从 0 到 n,写出来时间复杂度是o(n),高于o(log(n)),因此,还要优化。

       这道题其实就是在找满足条件的一个数,只是这个规则比较复杂,本质找数依然不变,因此可以用二分查找,之前i是从0到n一个个找,现在 i 从中间开始,使用二分的思想。要让 i 增加,就把 i 以前的全部砍掉。加了二分查找,就可以将算法的复杂度降低到log级别。

       下面是代码:

        

class Solution
{
public:
	double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) 
	{
		int total = nums1.size() + nums2.size();
		//0x1是16进制写法,0x116进制对应的二进制是00000001,如果
		//toal与0x1相与等于1(即total是奇数),那么执行...
		int* a = new int[nums1.size()];
		for (int i = 0; i < nums1.size(); i++)
			a[i] = nums1.at(i);
		int* b = new int[nums2.size()];
		for (int i = 0; i < nums2.size(); i++)
			b[i] = nums2.at(i);
		if (total & 0x1)
			return findKth(a, nums1.size(), b,nums2.size(),total / 2 + 1);//total / 2 + 1是合并后中间的位置
		else//total是偶数
			return (findKth(a, nums1.size(), b, nums2.size(), total / 2)
				+ findKth(a, nums1.size(), b, nums2.size(), total / 2 + 1)) / 2;
	}
private:
	void earseVector(vector<int>& arr,int time)
	{
		vector<int>::iterator it;
		int m = 0;
		for (it = arr.begin(); it != arr.end();)
		{
			if (m < time)
			{
				
				it = arr.erase(it);    //删除元素,返回值指向已删除元素的下一个位置  
				++it;    //指向下一个位置
				m++;
			}
			else
				break;
		}
	}
	double findKth(int a[], int m, int b[], int n, int k)
	{
		//如果a更长,那么交换一下参数位置,保证m<=n
		if (m > n)
			return findKth(b, n, a, m, k);
		if (m == 0)//特列,如果a数组长度为0,直接返回b的k-1位置元素
		{
			cout << "return.." << endl;
			return b[k - 1];
		}
		if (k == 1)//特列,如果a,b长度有只有1,那么返回a,b中的最小值
		{
			cout << "k==1\n";
			return min(a[0], b[0]);
		}

		//divide k into two parts
		//二分查找,直接从中间找,因为第一次传过来的k就是二分之total,如果k再除以2,其实就是较短数组的中间位置了
		//k就是half,所以pb = half - pa
		int pa = min(k / 2, m);//取min表示如果超出m,取m,否则取k/2,边界处理
		int pb = k - pa;
		if (a[pa - 1] < b[pb - 1])//表明小了,i往前移动
		{
			cout << "e1\n";
			return findKth(a + pa, m - pa, b, n, k - pa);//a+pa其实就是移动数组的首指针,到a+pa位置,也就是说,pa之前的元素不考虑了,cut掉,因此长度变为m-pa,k也减pa
		}

		else if (a[pa - 1] > b[pb - 1])//表明大了,j往后移动
		{
			cout << "n - pb = " << n - pb << endl;
			return findKth(a, m, b + pb, n - pb, k - pb);
		}

		else
		{
			cout << " xx= :" << a[pa - 1] << endl;
			return a[pa - 1];
		}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值