排序间隔查询

时间:2015-02-13 10:07:10

标签: algorithm data-structures interval-tree

我正在寻找一种数据结构,该数据结构可以在封闭的时间间隔内有效运行,具有以下属性:

  • 动态添加或删除间隔

  • 设置并随时更改每个间隔的数字(“深度”)。没有两个深度是相同的

  • 找到与任何给定间隔重叠的所有间隔,按“深度”

  • 排序

我找到的最接近的结构是Interval tree,但是它根据深度以任意顺序列出了找到的间隔。我可以像报告的那样收集所有“未分类”的间隔,然后对它们进行排序,但我正在跳跃,可以避免对每个查询的结果进行排序。

请知道,有没有人知道这样的数据结构,或者有任何建议如何(如果可能的话)增强Interval树来支持这样的排序?

示例:

  1. 将[1,2]添加到空结构并将其深度设置为1
  2. 添加[10,100],深度= 2
  3. 添加[5,55],深度= 3
  4. 查询[5,50]报告[10,100]和[5,55]
  5. 将[10,100]的深度设置为3,将[5,55]设置为2
  6. 查询[5,50]报告[5,55]和[10,100]
  7. 修改

    我对快速添加/删除和查询更感兴趣,而不是更新深度。深度可以与O(n)一样多,如果这有助于加速其他操作。

4 个答案:

答案 0 :(得分:4)

假设您想要的算法存在。然后让我们创建一组一百万个区间,每个区间为[1, 1],具有随机深度,并将它们插入到这样的区间树中。然后让我们查询间隔[1, 1]。它应按排序顺序返回所有区间,复杂度为O(M + log N),但N = 1,因此我们在线性时间内对一组M元素进行排序。

换句话说,从区间树中获取元素之后按深度排序元素在复杂性方面与理论上一样好。

答案 1 :(得分:1)

您设置的深度等同于虚构list中的间隔位置。所以,通常的数字对列表就足够了。列表可以轻松添加,删除或切换其项目。

如果您还需要找到给定间隔的深度,请为其创建一个函数(但您没有提到它的需要)

答案 2 :(得分:1)

这是 Java 中使用TreeMap的解决方案,基本上是Binary-Tree

测试:http://ideone.com/f0OlHI

<强>复杂性

     Insert : 2 * O(log n)

     Remove : 2 * O(log n)

     Search : 1 * O(log n)

ChangeDepth : 7 * O(log n)

findOverlap : O(n)

IntervalDataSet.java

class IntervalDataSet
{
    private TreeMap<Integer,Interval> map;

    public IntervalDataSet ()
    {
        map = new TreeMap<Integer,Interval> ();
    }

    public void print ()
    {
        for(Map.Entry<Integer,Interval> entry : map.entrySet())
        {
          Integer key = entry.getKey();
          Interval value = entry.getValue();

          System.out.println(key+" => ["+value.min+","+value.max+"] ");
        }
    }

    public boolean changeDepth (int depth, int newDepth)
    {
        if (!map.containsKey(depth)) return false;

        if (map.containsKey(newDepth)) return false;

        Interval in = map.get(depth);

        in.depth = newDepth;

        remove(depth); insert(in);

        return true;
    }

    public boolean insert (Interval in)
    {
        if (in == null) return false;

        if (map.containsKey(in.depth)) return false;

        map.put(in.depth, in); return true;
    }

    public boolean remove (int depth)
    {
        if (!map.containsKey(depth)) return false;

        map.remove(depth); return true;
    }

    public Interval get (int depth)
    {
        return map.get(depth);
    }

    public void print (int depth)
    {
        if (!map.containsKey(depth))
          System.out.println(depth+" => X ");
        else
          map.get(depth).print();
    }

    public void printOverlappingIntervals (Interval in)
    {
        for (Interval interval : map.values())
            if (interval.intersect(in))
                interval.print();
    }

    public ArrayList<Interval> getOverlappingIntervals (Interval in)
    {
        ArrayList<Interval> list = new ArrayList<Interval>();

        for (Interval interval : map.values())
            if (interval.intersect(in))
                list.add(interval);

        return list;
    }

    public int size ()
    {
        return map.size();
    }

}

Interval.java

class Interval
{
    public int min;
    public int max;
    public int depth;

    public Interval (int min, int max, int depth)
    {
        this.min = min;
        this.max = max;
        this.depth = depth;
    }

    public boolean intersect (Interval b)
    {
        return (b != null
            && ((this.min >= b.min && this.min <= b.max)
                || (this.max >= b.min && this.max <= b.max))
            );
    }

    public void print ()
    {
        System.out.println(depth+" => ["+min+","+max+"] ");
    }
}

Test.java

class Test
{
  public static void main(String[] args) 
  {
    System.out.println("Test Start!");
    System.out.println("--------------");

    IntervalDataSet data = new IntervalDataSet ();

    data.insert(new Interval( 1,3, 0 ));
    data.insert(new Interval( 2,4, 1 ));
    data.insert(new Interval( 3,5, 3 ));
    data.insert(new Interval( 4,6, 4 ));
    System.out.println("initial values");
    data.print();

    System.out.println("--------------");
    System.out.println("Intervals overlapping [2,3]");

    data.printOverlappingIntervals(new Interval( 2,3, -1 ));
    System.out.println("--------------");

    System.out.println("change depth 0 to 2");
    data.changeDepth( 0, 2 );
    data.print();
    System.out.println("--------------");

    System.out.println("remove depth 4");
    data.remove( 4 );
    data.print();
    System.out.println("--------------");

    System.out.println("change depth 1 to 4");
    data.changeDepth( 1, 4 );
    data.print();
    System.out.println("--------------");

    System.out.println("Test End!");
  }
}

<强> IntervalDataSet2

复杂性

initialization : O(n)

   findOverlap : 2 * O(log n) + T(merge)

class IntervalDataSet2
{
    private Integer [] key;
    private TreeMap<Integer,Interval> [] val;
    private int min, max, size;

    public IntervalDataSet2 (Collection<Interval> init)
    {
        TreeMap<Integer,TreeMap<Integer,Interval>> map
            = new TreeSet<Integer,TreeMap<Integer,Interval>> ();

        for (Interval in : init)
        {
            if (!map.containsKey(in.min))
                map.put(in.min,
                    new TreeMap<Integer,Interval> ());

            map.get(in.min).put(in.depth,in);

            if (!map.containsKey(in.max))
                map.put(in.max,
                    new TreeMap<Integer,Interval> ());

            map.get(in.max).put(in.depth,in);
        }

        key = new Integer [map.size()];
        val = new TreeMap<Integer,Interval> [map.size()];

        int i = 0;
        for (Integer value : map.keySet())
        {
            key [i] = value;
            val [i] = map.get(value);
            i++ ;
        }

        this.size = map.size();
        this.min = key [0];
        this.max = key [size-1];

    }

    private int binarySearch (int value, int a, int b)
    {
        if (a == b)
            return a;

        if (key[(a+b)/2] == value)
            return ((a+b)/2);

        if (key[(a+b)/2] < value)
            return binarySearch(value, ((a+b)/2)+1, b);
        else
            return binarySearch(value, (a, (a+b)/2)-1);
    }

    public TreeMap<Integer,Interval> findOverlap (Interval in)
    {
        TreeMap<Integer,Interval> solution
            = new TreeMap<Integer,Interval> ();

        int alpha = in.min;
        int beta = in.max;

        if (alpha > this.max || beta < this.min)
            return solution;

        int i = binarySearch(alpha, 0,(size-1));
        int j = binarySearch(beta, 0,(size-1));

        while (alpha <= beta && key[i] < alpha) i++;
        while  (alpha <= beta && key[j] > beta) j--;

        for (int k = i; k <= j; k++)
            solution.addAll ( val[k] );

        return solution;
    }

}

答案 3 :(得分:0)

最后,很难想到这个问题。你所拥有的实际上是一维空间实际上是一条线。通过添加深度,您将获得第二个坐标。通过请求深度是唯一的,您可以实际绘制图片。

您的查询是将图片的每个间隔(线)与x1到x2的矩形以及Y中的每个y相交。

所以天真地看待这个问题就是如果相交,则按y的顺序比较每一行。由于您需要所有结果,因此需要O(n)才能找到答案。此答案也必须按O(m log m)深度排序。

您尝试使用一维R树。允许您随时定义区域。在这种结构中,每个节点跨越从min到max的区域。您现在可以进一步拆分此区域。由于分割实际上是分割它,因此在两个部分中都存在间隔拟合,因此它们存储在节点内(而不是存储在子节点内)。在这些子节点中,您将再次获得此类列表,依此类推。

对于您的搜索,您检查所有节点和子节点,哪些区域与您的搜索间隔相交。

在每个节点中,间隔列表根据其深度值进行排序。

因此问题被简化为许多排序列表(可能包含在搜索间隔内的所有intervalls)。现在必须针对与搜索间隔真正相交的每个间隔过滤这些列表。但是你不需要过滤所有。因为如果这样的节点间隔完全包含在您的搜索间隔内,则它的所有间隔及其所有子间隔都与您的搜索间隔真正相交(因为它们完全包含在其中)。

要组合所有这些列表,您只需使用union组合,您可以选择具有最小深度的该联合的下一个元素。您可以按其第一个元素(每个列表中具有最小深度的元素)的深度对所有这些列表进行排序。现在,您查找第一个元素并将其移动到结果中。您现在将列表中的下一个元素与下一个列表的第一个元素进行比较,如果深度仍然较小,则将其复制到。如果深度变大,您只需使用正确的位置对其进行排序,记录k(其中k是您的集合中非空列表的数量),然后继续使用现在的第一个列表并重复它直到所有列表都为空或通过因为你保持每个列表的光标位置。

这样您只需对列表进行排序,并将其与下一个元素进行比较(如果它仍然较小或插入它)。

这是我能想到的最好的结构。首先,你很容易排除几乎所有不能与之交叉的intervalls。比你根据潜在列表组成结果,你知道列表中你知道这些列表是完全结果的一部分(可以说只有一定数量的intervall列表必须被检查,因为树分裂得非常快)。通过控制拆分策略,您可以控制每个部件的成本。例如,如果您仅以> 10个间隔分割,则将确保k

最糟糕的情况很糟糕,但我想预期的实际表现会好于O(n + m log m),因为你已经使用了已排序的不同潜在交叉点列表。

请记住,如果节点具有子元素,则它本身包含与分割点相交的所有间隔的列表。因此,如果您的搜索intervall也与分割点相交,则此节点的每个间隔也是结果的一部分。

所以请随意尝试这种结构。它应该很容易在一天内实施。