Структура данных для обработки интервалов

У меня есть ряд временных интервалов(t_start,t_end), которые не могут перекрываться, т. е.: t_end(i) > t_start (i+1). Я хочу сделать следующие операции:

1) добавить новые (объединение) интервалов [ {(1,4),(8,10)} U (3,7) = {(1,7),(8,10)} ]
2) Принять интервалов из [ (1,7) - (3,5) = {(1,3),(5,7)}
3) Проверка того, перекрывается ли точка или интервал с интервалом в моем ряду (пересечении)
4) нахождения первого "без интервала" минимальная длина после какой-то момент [ {(1,4),(7,8)}: есть "не-интервал" длины 3 между 4 и 7 ].

Я хочу знать хорошие способы реализации этого, с низкими сложностями (log n для всех операций сделал бы это).

вопрос: структура данных для быстрого временного интервала искать

5 ответов


похоже, вы могли бы просто использовать сбалансированное бинарное дерево всех граничных времен.

например, представлять {(1,4), (8,10), (12,15)} как дерево, содержащее 1, 4, 8, 10, 12, и 15.

каждый узел должен сказать, является ли это началом или концом интервала. Итак:

                          8 (start)
                         /        \
                1 (start)         12 (start)
                      \             /      \
                     4 (end)   10 (end)   15 (end)

(здесь все" конечные " узлы оказались внизу по совпадению.)

тогда я думаю, что вы можете иметь все свои операции в O(log n) время. в добавить интервал:

  • найти время начала. Если он уже находится в дереве в качестве начального времени, вы можете оставить его там. Если он уже находится в дереве в качестве конечного времени, вы захотите его удалить. Если его нет на дереве и он не падает во время существующего интервала, вы хотите добавить его. Иначе вы не захотите его добавлять.

  • найдите время остановки, используя тот же метод, чтобы узнать, нужно ли его добавить, удалить или ни.

  • теперь вы просто хотите добавить или удалить вышеупомянутые узлы запуска и остановки и в то же время удалить все существующие узлы между ними. Для этого вам нужно только перестроить узлы дерева или непосредственно над эти два места на дереве. Если высота дерева равна O(log n), что можно гарантировать с помощью сбалансированного дерева, это занимает O (log n) времени.

(отказ от ответственности: если вы находитесь в C++ и делаете явное управление памятью, вы можете в конечном итоге освободить больше, чем O(log n) частей памяти, когда вы это делаете, но на самом деле время, необходимое для освобождения узла, должно быть указано тому, кто его добавил, Я думаю.)

удаление интервала в основном то же самое.

проверка точки или интервала - это просто.

поиск первого разрыва по крайней мере заданного размера после заданного времени можно сделать в O (log n) тоже, если вы также кэшируйте еще две части информации на узел:

  • в каждом начальном узле (кроме самого левого) размер разрыва непосредственно слева.

  • в каждом узле размер наибольшего разрыва, который появляется в этом поддереве.

чтобы найти первый пробел заданного размера, который появляется после заданного времени, сначала найдите это время в дереве. Затем поднимитесь до узла, который утверждает, что содержит достаточно большой пробел. Если вы подошли справа, вы знаете, что этот промежуток слева, поэтому вы игнорируете его и продолжаете идти вверх. В противном случае вы пришли слева. Если узел является начальным узлом, проверьте, достаточно ли большой зазор слева. Если так, то с тобой покончено. В противном случае достаточно большой промежуток должен быть где-то справа. Идите вниз направо и продолжайте вниз, пока не найдете промежуток. Опять же, поскольку высота дерева равна O(log n), ходить по нему три раза (вниз, вверх и, возможно, снова вниз) - O (log северный.)


не зная больше деталей, я бы предложил прочитать о Интервал Деревьев. Интервальные деревья-это специальный 1-мерный случай более общего KD-деревьев, и O(n log n) сроки строительства, и O(log n) типичное время работы. Точные реализации алгоритмов, которые вам нужно найти, но вы можете начать сCGAL.


Я знаю, что вы уже приняли ответ, но поскольку вы указали, что, вероятно, будете реализовывать на C++, вы также можете взглянуть на библиотеку контейнеров интервалов Boosts (http://www.boost.org/doc/libs/1_46_1/libs/icl/doc/html/index.html).


моя реализация дерева интервалов с деревом AVL.

public class IntervalTreeAVL<T>{
    private static class TreeNode<T>{
        private T low;
        private T high;
        private TreeNode<T> left;
        private TreeNode<T> right;
        private T max;
        private int height;
        private TreeNode(T l, T h){
            this.low=l;
            this.high=h;
            this.max=high;
            this.height=1;
        }
    }
    private TreeNode<T> root;
    public void insert(T l, T h){
        root=insert(root, l, h);
    }
    private TreeNode<T> insert(TreeNode<T> node, T l, T h){
        if(node==null){
            return new TreeNode<T>(l, h);
        }
        else{
            int k=((Comparable)node.low).compareTo(l);
            if(k>0){
                node.left=insert(node.left, l, h);
            }
            else{
                node.right=insert(node.right, l, h);
            }
            node.height=Math.max(height(node.left), height(node.right))+1;
            node.max=findMax(node);
            int hd = heightDiff(node);
            if(hd<-1){
                int kk=heightDiff(node.right);
                if(kk>0){
                    node.right=rightRotate(node.right);
                    return leftRotate(node);
                }
                else{
                    return leftRotate(node);
                }
            }
            else if(hd>1){
                if(heightDiff(node.left)<0){
                    node.left = leftRotate(node.left);
                    return rightRotate(node);
                }
                else{
                    return rightRotate(node);
                } 
            }
            else;
        }
        return node;
    }
    private TreeNode<T> leftRotate(TreeNode<T> n){
        TreeNode<T> r =  n.right;
        n.right = r.left;
        r.left=n;
        n.height=Math.max(height(n.left), height(n.right))+1;
        r.height=Math.max(height(r.left), height(r.right))+1;
        n.max=findMax(n);
        r.max=findMax(r);
        return r;
    }
    private TreeNode<T> rightRotate(TreeNode<T> n){
        TreeNode<T> r =  n.left;
        n.left = r.right;
        r.right=n;
        n.height=Math.max(height(n.left), height(n.right))+1;
        r.height=Math.max(height(r.left), height(r.right))+1;
        n.max=findMax(n);
        r.max=findMax(r);
        return r;
    }
    private int heightDiff(TreeNode<T> a){
        if(a==null){
            return 0;
        }
        return height(a.left)-height(a.right);
    }
    private int height(TreeNode<T> a){
        if(a==null){
            return 0;
        }
        return a.height;
    }
    private T findMax(TreeNode<T> n){
        if(n.left==null && n.right==null){
            return n.max;
        }
        if(n.left==null){
            if(((Comparable)n.right.max).compareTo(n.max)>0){
                return n.right.max;
            }
            else{
                return n.max;
            }
        }
        if(n.right==null){
           if(((Comparable)n.left.max).compareTo(n.max)>0){
                return n.left.max;
            }
            else{
                return n.max;
            } 
        }
        Comparable c1 = (Comparable)n.left.max;
        Comparable c2 = (Comparable)n.right.max;
        Comparable c3 = (Comparable)n.max;
        T max=null;
        if(c1.compareTo(c2)<0){
            max=n.right.max;
        }
        else{
            max=n.left.max;
        }
        if(c3.compareTo((Comparable)max)>0){
            max=n.max;
        }
        return max;
    }


TreeNode intervalSearch(T t1){
        TreeNode<T> t = root;
        while(t!=null && !isInside(t, t1)){
            if(t.left!=null){
                    if(((Comparable)t.left.max).compareTo(t1)>0){
                    t=t.left;
                }
                else{
                    t=t.right;
                }
            }
            else{
                t=t.right;
            }
        }
        return t;
    }
    private boolean isInside(TreeNode<T> node, T t){
        Comparable cLow=(Comparable)node.low;
        Comparable cHigh=(Comparable)node.high;
        int i = cLow.compareTo(t);
        int j = cHigh.compareTo(t);
        if(i<=0 && j>=0){
            return true;
        }
        return false;
    }
}

Я только что нашел диапазон гуавы и RangeSet которые делают именно это.

он реализует все перечисленные операции:

  1. Союз

    RangeSet<Integer> intervals = TreeRangeSet.create(); 
    intervals.add(Range.closedOpen(1,4)); // stores {[1,4)}
    intervals.add(Range.closedOpen(8,10)); // stores {[1,4), [8,10)}
    // Now unite 3,7
    intervals.add(Range.closedOpen(3,7)); // stores {[1,7), [8,10)}
    
  2. Subraction

    intervals.remove(Range.closedOpen(3,5)); //stores {[1,3), [5, 7), [8, 10)}
    
  3. перекресток

    intervals.contains(3); // returns false
    intervals.contains(5); // returns true
    intervals.encloses(Range.closedOpen(2,4)); //returns false
    intervals.subRangeSet(Range.closedOpen(2,4)); // returns {[2,3)} (isEmpty returns false)
    intervals.subRangeSet(Range.closedOpen(3,5)).isEmpty(); // returns true
    
  4. Поиск пустых пространств (это будет та же сложность, что и итерация набора в худшем случае случай):

    Range freeSpace(RangeSet<Integer> ranges, int size) {
        RangeSet<Integer> frees = intervals.complement().subRangeSet(Range.atLeast(0));
        for (Range free : frees.asRanges()) {
            if (!free.hasUpperBound()) {
                return free;
            }
            if (free.upperEndpoint() - free.lowerEndpoint() >= size) {
                return free;
            }
        }