leetcode352. Data Stream as Disjoint Intervals

Keywords: Java less

Subject requirements

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?

A concept of disjoint interval is mentioned here, which refers to disjoint intervals. If the new data coincides with the current interval set, the current interval set needs to be merged. This ensures that the resulting data sets do not intersect each time.

Ideas and Codes

There are two ways to solve this problem. One is to merge the intersecting intervals when inserting data, the other is to update the range of intervals only when inserting data, and not merge the intervals.

These two schemes have their own advantages under different data sets. The first method is suitable for scenarios with large amount of data and few sets of intervals. Because there are few interval sets, the data movement cost of interval sets caused by each merge is lower. The latter, on the contrary, is suitable for scenarios with more interval sets and only triggers merge operations when reading interval sets.

This is achieved by scheme one. If we maintain an ordered set of intervals and a new number comes from the data stream, the number and the intervals in the interval flow have the following situations:

  1. Just hit an interval and do nothing.
  2. If the left-most value of the interval is less than 1, a new left-hand interval will be added.
  3. If it is equal to the leftmost value of the interval minus 1, then the leftmost value of the interval is modified to val.
  4. If it is greater than the maximum value of the interval plus 1, then the right interval is added.
  5. If it is equal to the rightmost value of the interval plus 1, then the rightmost value of the interval is modified to val.
  6. If the right value of an interval is added to 1 and the left value of an interval is subtracted from 1, the two intervals are merged.
  7. If the right value of an interval is greater than one and equals the left value of an interval minus one, then an interval is added to the two intervals.
  8. If it is equal to the right value of an interval plus 1, then the right value of an interval is modified to val.
  9. If the left value of an interval is reduced by 1, then the left value of an interval is modified to val.

The code is as follows:

    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;
        }
    }

The above code creates the Pair class as an object of the interval. Then use List to store the interval Pair from small to large. Each time addNum is called to pass in val, the nearest interval with a larger left boundary than Val is found by dichotomy. Find the area and process it one by one according to the above judgment. Assuming that the size of data stream is n and the average size of interval is m, the time complexity of this method is O(lgM*n). But because of the more logic of judgment, the code is messy and the readability is poor.

TreeMap can be used for optimization. TreeMap is the implementation of the smallest heap by default. It saves us a lot of dichotomy logic and reduces the time complexity to O(NlgN) when inserting intervals. Only the boundary scene needs to be judged. The code is as follows:

    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;
    }

Posted by Zmodem on Sat, 12 Oct 2019 03:25:14 -0700