LeetCode第[56]题(Java):Merge Intervals

题目:合并区间

难度:Medium

题目内容

 

Given a collection of intervals, merge all overlapping intervals.

翻译

给定一个区间的集合,合并所有重叠的区间。

Example 1:

Input: [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]

Example 2:

Input: [[1,4],[4,5]]
Output: [[1,5]]

我的思路:因为可能是乱序的,不好逐个比较,所以需要排序

1、将List内的元素进行排序(利用比较器),然后从前往后比较 ;

2、当前的end和后一个的start,看是否需要合并

3、如果需要合并则当前的end等于它自己与后一个的end中取大的,然后将后一个remove()掉 

我的代码

 1 class Solution {
 2     public List<Interval> merge(List<Interval> intervals) {
 3         Collections.sort(intervals, new MyComparator());
 4         
 5         for (int i = 0; i < intervals.size() - 1; i++) {
 6             if (intervals.get(i).end >= intervals.get(i+1).start) {
 7                 intervals.get(i).end = intervals.get(i).end > intervals.get(i+1).end ? intervals.get(i).end : intervals.get(i+1).end;
 8                 intervals.remove(i+1);
 9                 i--;
10             }    
11         }
12         return intervals;
13     }
14 }
15 
16 class MyComparator implements Comparator<Interval> {  
17   
18     public int compare(Interval one, Interval two) {
19         return one.start - two.start;
20     }  
21       
22 }  

我的复杂度:O(NlogN)    主要是排序的那里用了nlogn    剩下的只有n

编程过程中的问题

1、忘记比较器怎么实现

      方法一:将List中的类实现Comparable<E>接口(本题的E就是Interval),并重写compareTo方法(一个传入对象)  用自己的值和传入对象的值做比较即可。

【比较规则:自己比传入对象大就返回零,等于就等于0,小于就小于0,所以一般直接   return   自己 - 传入对象】如下:

    public int compareTo(Interval o) {
        return start - o.start;
    }

        但是,本题的Interval是写死的不能在编辑中修改,但是不要急,还有另外一种办法:

  方法二:自己写一个类实现Comparator<E>接口,并重写compare方法(两个传入对象)

【比较规则:与上面类似  一般直接 return   前面一个对象 - 后面一个对象】如下:

    public int compare(Interval one, Interval two) {
        return one.start - two.start;
    }  

   扩展:那如果要比较两个属性呢,比如先比较名字,再比较年龄————代码如下

    public int compare(Person one, Person two) {  
        int i = one.name.compareTo(two.name); //比较名字字符串  
        if (i == 0) { //如果名字一样,则继续比较年龄  
            return one.age - two.age;  
        } else { //首先比较名字,名字不一样,则返回比较结果  
            return i;  
        }  
    } 

  使用此方法需要在sort()方法中传入作为第二个参数,(所以一般可直接在sort方法里使用匿名类)如下:

        Collections.sort(intervals, new Comparator<Interval>(){
            @Override
            public int compare(Interval interval1, Interval interval2){
                return interval1.start - interval2.start;
            }
        });

2、发现需要合并后,是不能直接将end取后一个的end的,因为后一个的end也是有可能比当前的小的,比如【【1,6】,【3,5】】,所以需要判断一下end的大小;

3、因为是顺序向后循环的,List调用remove方法去掉后一个之后,i 会指向后一个继续进行循环,但是此时应该仍然指向自己,继续判断后一个是否需要合并,所以需要 i - -。

  扩展:如果是从后往前循环的呢?需要 i ++?

    此时删去了下标i - 1,然后 i -- ,i 移到 下标 i -1 , 而此时的下标 i -1 就是remove之前的 下标 i 所以不再需要做操作。

    例如:下标 0,1,2  此时 i 等于2 ,将1remove(此时的下标2自动变为1),然后 i -- ,i还是指向原来的2的位置。

  所以,当在循环中调用List的remove方法的时候,一定要考虑下标的变化,而调整指针 i 的值

答案代码

 1 class Solution {
 2     private class IntervalComparator implements Comparator<Interval> {
 3         @Override
 4         public int compare(Interval a, Interval b) {
 5             return a.start < b.start ? -1 : a.start == b.start ? 0 : 1;
 6         }
 7     }
 8 
 9     public List<Interval> merge(List<Interval> intervals) {
10         Collections.sort(intervals, new IntervalComparator());
11 
12         LinkedList<Interval> merged = new LinkedList<Interval>();
13         for (Interval interval : intervals) {
14             if (merged.isEmpty() || merged.getLast().end < interval.start) {
15                 merged.add(interval);
16             }
17             else {
18                 merged.getLast().end = Math.max(merged.getLast().end, interval.end);
19             }
20         }
21 
22         return merged;
23     }
24 }

答案思路:和我的思路是一样的,先排序然后从头到位判断是否需要合并,

不过他使用了队列,空间复杂度提高了,但是这样避免了remove方法带来的多余的时间复杂度,二者各有好处。

原文地址:https://www.cnblogs.com/Xieyang-blog/p/9034145.html