处理间隔的数据结构

时间:2009-12-30 20:48:37

标签: algorithm data-structures tree intervals

我有一系列时间间隔(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)}之后找到最小长度的第一个“非间隔”:在4到7之间存在长度为3的“非间隔”。 / p>

我想知道实现这一点的好方法,复杂度低(所有操作的log n都会这样做)。

相关问题:Data structure for quick time interval look up

5 个答案:

答案 0 :(得分:16)

听起来你可以使用所有边界时间的平衡二叉树

例如,将{(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 n),所以走三次(向下,向上,可能再次向下)是O(log n)。

答案 1 :(得分:5)

不知道具体细节,我建议阅读Interval Trees。区间树是更通用的kd-trees的特殊1维情况,具有O(n log n)构造时间和O(log n)典型操作时间。您需要自己找到精确的算法实现,但您可以先查看CGAL

答案 2 :(得分:1)

我知道你已经接受了答案,但既然你表示你可能会用C ++实现,你也可以查看Boosts Interval容器库(http://www.boost.org/doc/libs/1_46_1/libs/icl/doc/html/index.html)。

答案 3 :(得分:1)

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

答案 4 :(得分:1)

我刚刚发现Guava's RangeRangeSet就是这样做的。

它实现了引用的所有操作:

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