Leetcode(4):Median of Sorted Arrays

来源:互联网 发布:js修改input边框颜色 编辑:程序博客网 时间:2024/06/01 15:00

题目描述

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

Example 1:nums1 = [1, 3]nums2 = [2]

The median is 2.0

Example 2:nums1 = [1, 2]nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

分析

这题就是给定两个排好序的数组,求这两个数组的中位数。很自然的思路是:将两个数组合并然后按照中位数的定义来求。合并数组这一步不一定要全部做完,只用找到中间一个数(长度为奇数)或者两个数(长度为偶数)即可,也就是说可以只重排一半的数就可以了。直接套用合并数组的算法即可。但是我这种算法的复杂度为O((m+n)/2),虽然没有达到要求,但是过了。。。所以肯定有更好的方法。
反观上面的这种方法,我们要找到中位数必须得遍历前一半的数。如果我们每一步能跳过一半的数,不用遍历,自然复杂度就降低了。举个例子假设num1=[1,2,3,4],num2=[5,6,7,8],按之前的算法,我们必须得遍历1,2,3,4,如果我们以上帝模式知道1,2,3,4一定比中位数小,那么是不是可以一次过2个数?换句话说一次删去2个数。现在就是得实现这种上帝模式了。
其实这是一个找第k大元素的问题,现在假设中位数是第k大的元素,现在我们要实现的就是每次过k/2个元素,直到找到这个元素为止。首先考虑最极端的情况:(1)前k个元素都在num1或num2中,那么自然地我们直接去掉num1或num2的k/2个元素;(2)前k个元素一半在num1,一半在num2,至于去掉哪一部分,后面再说。考虑了这两种情况,你会发现如果其中一个数组包含大于或等于k/2个比中位数小的数,那么一定可以剔除这个数组的k/2个数。那么怎么判断这一条件呢?假设num1是满足上述条件的数组,我们在合并数组的时候,当我们已经把num1中的前k/2个数放进新数组的时候,我们会判断第k/2+1个数与num2中接下来要比较的一个数进行比较,如果仍比较小就将它继续加入新数组,是不是意味着这个数肯定也比num2中的第k/2个数小(如果存在的话)?所以我们发现只用比较两个数组的第k/2个数的大小就可以知道可以删除哪个数组的前k/2个数。这里只是给出我的思考过程,说的可能不是很清楚,不过希望对大家有用,具体的证明可以参考http://blog.csdn.net/yutianzuijin/article/details/11499917/.

代码

1.最初的代码O((m+n)/2)(写的好长,有点不好意思)
这里面num1,num2分别用来存储最中间的两个数,当数组长度为偶数的时候,中位数就是两个数的平均;当数组长度为奇数时,中位数就是num2。

class Solution {public:    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {        int size1 = nums1.size(), size2 = nums2.size(), count = 0;        int i = 0, j = 0, edge = (size1 + size2) / 2;        double num1 = 0, num2 = 0;        while(i < size1 && j < size2 && count < edge + 2){            int a = nums1[i], b = nums2[j];            if(a < b){                i++;                count++;                if(count == edge)                    num1 = a;                if(count == edge + 1)                {                    num2 = a;                    break;                }            }            else{                j++;                count++;                if(count == edge)                    num1 = b;                if(count == edge + 1)                {                    num2 = b;                    break;                }            }         }        while(i < size1 && count < edge + 2)        {            int a = nums1[i];            i++;count++;            if(count == edge)                    num1 = a;                if(count == edge + 1)                {                    num2 = a;                    break;                }        }        while(j < size2 && count < edge + 2)        {            int a = nums2[j];            j++;count++;            if(count == edge)                    num1 = a;                if(count == edge + 1)                {                    num2 = a;                    break;                }        }        if((size1+size2)%2==0)            return (num1 + num2)/2.0;        else            return num2;    }};

2.缩短后的代码
代码虽然缩短了,但是运行时间变长了(个人认为是多了函数调用)而且缩短的不多,果然还是路还是很长。

class Solution {public:    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {        int size1 = nums1.size(), size2 = nums2.size(), count = 0;        int i = 0, j = 0, edge = (size1 + size2) / 2;        double num1 = 0, num2 = 0;        while(i < size1 && j < size2 && count < edge + 1){            int a = nums1[i], b = nums2[j];            if(a < b){                i++;                getMedian(num1,num2,count,a,edge);            }            else{                j++;                getMedian(num1,num2,count,b,edge);            }         }        while(i < size1 && count < edge + 1)        {            int a = nums1[i];            i++;            getMedian(num1,num2,count,a,edge);        }        while(j < size2 && count < edge + 1)        {            int a = nums2[j];            j++;            getMedian(num1,num2,count,a,edge);        }        if((size1+size2)%2==0)            return (num1 + num2)/2.0;        else            return num2;    }        void getMedian(double &num1, double &num2, int &count, int value, int median)    {        count++;        if(count == median)            num1 = value;        if(count == median + 1)            num2 = value;    }};

3.第k大元素算法
下面这段代码应该比我之前写的看的要更清晰舒服,虽然花了挺多时间,但是我骄傲!有一点要注意的是这里面有一个vector转数组传入函数的过程,一定要注意,如果vector是空的是不能直接把向量首元素地址传进函数里的(因为它根本没有首元素)。其实笔者觉得还能优化,因为在总长度为偶数的情况下进行了两次findKth函数调用而这两次函数调用的过程大部分是相同的,如果两次变一次肯定能更快,更好,大致思路是动态规划,如果大家有什么更好的方法或是笔者写的有什么错误,欢迎评论交流!共同进步!

class Solution {public:    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {        int length = nums1.size() + nums2.size();        int *a = nums1.size()==0?new int:&nums1[0],*b = nums2.size()==0?new int:&nums2[0];        if(length%2)            return findKth(a,b,nums1.size(),nums2.size(),length/2+1);        else            return (findKth(a,b,nums1.size(),nums2.size(),length/2)+findKth(a,b,nums1.size(),nums2.size(),length/2+1))/2.0;    }        int findKth(int* a, int* b, int size1, int size2, int k)    {        if(k == 1)            if(size1 == 0) return b[0];            else if(size2 == 0) return a[0];            else return a[0]<b[0]?a[0]:b[0];        if(k/2 > size1)            return findKth(b,a,size2,size1,k);        if(k/2 > size2)            return findKth(a+k/2,b,size1-k/2,size2,k-k/2);        else if(a[k/2 - 1] <= b[k/2 - 1])            return findKth(a+k/2,b,size1-k/2,size2,k-k/2);        else             return findKth(a,b+k/2,size1,size2-k/2,k-k/2);    }};
原创粉丝点击