您的位置:首页 > 其它

LeetCode 56, Merge Intervals 从 TLE 到 AC

2015-07-30 22:15 369 查看

LeetCode 56, Merge Intervals 从 TLE 到 AC

这个题再次告诉我,光照着API或者java docs 写代码是靠不住的,还是要去看底层的源代码。前人封装好的黑盒子给我们用,并不是照着说明书做就可以了,有时候还需要知道黑盒子里大概装的是什么。

https://leetcode.com/problems/merge-intervals/

Given a collection of intervals, merge all overlapping intervals.

For example,

Given [1,3],[2,6],[8,10],[15,18],

return [1,6],[8,10],[15,18].

[code]/**
 * Definition for an interval.
 * public class Interval {
 *     int start;
 *     int end;
 *     Interval() { start = 0; end = 0; }
 *     Interval(int s, int e) { start = s; end = e; }
 * }
 */
public class Solution {
    public List<Interval> merge(List<Interval> intervals) {

    }
}


这道题本身的思路很明确。先按照Interval.start 排序,然后合并重叠的部分。排序的时间复杂度是O(n*log(n)), 合并是O(N)。 List类下面有一个 sort 方法,既然是做leetCode, 我觉的这题的难点也就是排序了,于是就像自己再写一遍quickSort算法。另外,出于节省空间的目的,我的想法是,在传入的List〈Intervals〉上进行sort 和 merge 而返回新的List对象。

于是,我写了sort 方法,并在 sort 之后对 传入的List参数进行merge 操作,代码如下:

[code]    public void quickSort(List<Interval> intervals,int h, int t){
        //print(intervals);
        int i=h+1;
        int ref=intervals.get(h).start;     
        for(int j=h+1;j<t;j++){
            //System.out.println(j);    
            if(intervals.get(j).start < ref){
                swap(intervals,j,i);
                i++;
            }   
        }
        swap(intervals,h,i-1);
        if(i>h+1) quickSort(intervals,h,i);
        if(t>i+1) quickSort(intervals,i,t);

    }


然后,我对sort 后的代码,进行merge 操作:

[code]        quickSort(intervals,0,size);        
        //merge
        Interval cur=intervals.get(0);
        int start=cur.start, end=cur.end;

            for(int i=1;i<size;i++){
                Interval it=intervals.get(i);
                //System.out.println("size:"+size+" i:"+i);
                //print(intervals);
                if(end>=it.start){
                    intervals.remove(i-1);
                    intervals.remove(i-1);
                    end=Integer.max(end,it.end);
                    Interval tmp=new Interval(start,end);
                    intervals.add(i-1,tmp);
                    i--;
                    size--;
                }else{
                    start=it.start;
                    end=it.end;
                }
            }


以上代码虽然不够clean,草草写出来的,但是我想O(n*log(n))的算法,肯定是可以通过所有test cases 的,结果恰恰出乎我意料,没有AC,而是TLE了。这题既然要排序,就肯定是这个O(n*log(n))的复杂度,一定是我的算法复杂度计算错了。那么哪里不对呢?

很快便想到,是java.util.List 下的add 方法和 remove 方法本身的复杂度并不为O(1)。

util.ArrayList 相关源码如下:

[code]    public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,size - index);
        elementData[index] = element;
        size++;
    }


这里可以看到,引用了lang.System.arraycopy

[code] public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);


很遗憾,arraycopy是一个native方法,应该是C/C++ 实现的,看不到进一步的源码了。但是没关系,我在注释里找到了这句话:

[code]A subsequence of array components are copied from the source
     * array referenced by <code>src</code> to the destination array
     * referenced by <code>dest</code>. The number of components copied is
     * equal to the <code>length</code> argument.


这说明,arraycopy其实是把每一个element 往前copy一位,也就是说,这个操作的复杂度是O(n)而不是常数,这就印证了我的想法。

这个题再次告诉我,光照着API或者java docs 写代码是靠不住的,还是要去看底层的源代码。前人封装好的黑盒子给我们用,并不是照着说明书做就可以了,有时候还需要知道黑盒子里大概装的是什么。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: