LeetCode 56, Merge Intervals 从 TLE 到 AC

本文通过解决LeetCode中MergeIntervals问题,揭示了使用API和文档编写代码时可能遇到的问题,强调了深入阅读源代码的重要性。文章详细分析了排序算法(快速排序)的应用及Java List类操作的复杂度,特别是add和remove方法的实际实现,从而避免了不必要的TLE错误。
摘要由CSDN通过智能技术生成

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

/**
 * 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 操作,代码如下:

    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 操作:

        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 相关源码如下:

    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

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

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

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 写代码是靠不住的,还是要去看底层的源代码。前人封装好的黑盒子给我们用,并不是照着说明书做就可以了,有时候还需要知道黑盒子里大概装的是什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值