leetcode352. Data Stream as Disjoint Intervals

題目要求

Given a data stream input of non-negative integers a1, a2, ..., an, ..., summarize the numbers seen so far as a list of disjoint intervals.

For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, ..., then the summary will be:

[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]

**Follow up:**

What if there are lots of merges and the number of disjoint intervals are small compared to the data stream's size?

這裏面提到了一個disjoint interval的概念,它是指不相交的區間。如果新來的數據與當前的區間集產生了重合,則需要將當前的區間集進行合併。從而確保每次得到的數據集都是不相交的。

思路和代碼

這道題目整體來說有兩種思路,一種就是每次插入數據的時候將出現交集的區間進行合併,另一種就是在插入數據時只更新區間的範圍,並不將區間合併。

這兩種方案在不同特點的數據集下各有優勢。第一種方法適用於數據量大區間集少的場景。因爲區間集少,而每次合併帶來的區間集的數據移動成本較低。後者則剛好相反,適用於區間集較多的場景,只有在讀取區間集的時候,去觸發合併操作。

這裏是採用方案一來實現的。如果我們維護了一組有序的區間集,這時數據流中傳來一個新的數字,則該數字和區間流中的區間一共有如下幾種情況:

  1. 正好命中某個區間,則不做任何處理
  2. 小於區間最左值減1,則新增左側區間
  3. 等於區間最左值減1,則修改區間最左值爲val
  4. 大於區間最有值加1, 則新增右側區間
  5. 等於區間最右值加1,則修改區間最右值爲val
  6. 等於某個區間的右值加1且等於某個區間的左值減1,則合併兩個區間
  7. 大於某個區間的右值加一且等於某個區間的左值減一,則在兩個區間中新增一個區間
  8. 等於某個區間的右值加1,則修改區間右值爲val
  9. 等於某個區間的左值減1,則修改區間左值爲val

代碼如下:

    List<Pair> pairs = new ArrayList<>();
    public void addNum(int val) {
        if (pairs.size() == 0) {
            pairs.add(new Pair(val, val));
            return;
        }
        int left = 0, right = pairs.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (pairs.get(mid).left < val) {
                left = mid + 1;
            }else if (pairs.get(mid).left > val) {
                right = mid - 1;
            }else {
                return;
            }
        }


        if (left == 0) {
            if (pairs.get(left).left == val + 1) {
                pairs.get(left).left = val;
            }else {
                pairs.add(0, new Pair(val, val));
            }
        } else if (left == pairs.size()) {
            if (pairs.get(left-1).right == val - 1) {
                pairs.get(left-1).right = val;
            }else if (pairs.get(left-1).right < val - 1) {
                pairs.add(new Pair(val, val));
            }
        }else if (pairs.get(left-1).right == val - 1 && pairs.get(left).left == val + 1) {
            pairs.get(left-1).right = pairs.get(left).right;
            pairs.remove(left);
        }else if (pairs.get(left-1).right == val - 1) {
            pairs.get(left - 1).right = val;
        }else if (pairs.get(left).left == val + 1) {
            pairs.get(left).left = val;
        }else if (pairs.get(left-1).right < val){
            pairs.add(left, new Pair(val, val));
        }
    }

    public int[][] getIntervals() {
        int[][] result = new int[pairs.size()][2];
        for (int i = 0 ; i < pairs.size() ; i++) {
            result[i][0] = pairs.get(i).left;
            result[i][1] = pairs.get(i).right;
        }
        return result;
    }

    public static class Pair{
        int left;
        int right;

        public Pair(int left, int right) {
            this.left = left;
            this.right = right;
        }
    }

上面的代碼新建了類Pair作爲區間的對象。然後使用List對區間Pair按照從小到大存儲。每次調用addNum傳入val時,都會利用二分法找到左邊界比val大的最近的區間。找到該區間後再按照上文的判斷方式逐個處理。假設數據流大小爲n,區間的平均大小爲m,則這種方法的時間複雜度爲O(lgM*n)。但是因爲判斷邏輯較多,因此代碼顯得凌亂,可讀性很差。

這裏可以使用TreeMap進行優化,TreeMap默認上是最小堆的實現,它幫我們省去了大量二分法的邏輯,同時在插入區間的時候,時間複雜度也降低到O(NlgN)。只需要對邊界場景進行判斷即可。代碼如下:

    TreeMap<Integer, Integer> treeMap = new TreeMap<>();

    public void addNum(int val) {
        if (treeMap.containsKey(val)) {
            return;
        }
        Integer lowerKey = treeMap.lowerKey(val);
        Integer higherKey = treeMap.higherKey(val);
        if (lowerKey != null && higherKey != null && treeMap.get(lowerKey) == val - 1 && higherKey == val + 1) {
            treeMap.put(lowerKey, treeMap.get(higherKey));
            treeMap.remove(higherKey);
        }else if (lowerKey != null && treeMap.get(lowerKey) >= val - 1) {
            treeMap.put(lowerKey, Math.max(val, treeMap.get(lowerKey)));
        }else if (higherKey != null && higherKey == val + 1) {
            treeMap.put(val, treeMap.get(higherKey));
            treeMap.remove(higherKey);
        }else {
            treeMap.put(val, val);
        }
    }

    public int[][] getIntervals() {
        int[][] result = new int[treeMap.size()][2];
        int index = 0;
        for (int key : treeMap.keySet()){
            result[index][0] = key;
            result[index][1] = treeMap.get(key);
        }
        return result;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章