leetcode 4——Median of Two Sorted Arrays

来源:互联网 发布:中小学创新设计软件 编辑:程序博客网 时间:2024/05/29 04:09

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

        记录一下解题思路。

        首先要区分中位数,不是平均数!该题是要找中位数,如果序列是奇数个,那么直接取中间的,如果是偶数个,那么取中间的两个数的平均数,所以,核心问题是,如何把两个排好的序列整合成一个序列,并且时间复杂度要求低于: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++;}elsebreak;}}double findKth(int a[], int m, int b[], int n, int k){//如果a更长,那么交换一下参数位置,保证m<=nif (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 - paint 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];}}};        
原创粉丝点击