用于找出重叠区域的区间树用法

时间:2016-05-27 07:10:10

标签: python algorithm tree

我正在尝试使用区间树来解决此问题problem。以下是我的尝试,但可以理解的是它没有工作,即它没有返回所有间隔。

将举行板球比赛。该字段由1D平面表示。作为板球运动员,X先生最喜欢的投篮。每个镜头都有一个特定的范围。第i次射击的范围是​​从A(i)到B(i)。这意味着他最喜欢的镜头可以在这个范围内的任何地方对方球队中的每个球员只能在特定范围内进行比赛。玩家可以从A(i)到B(i)。你会得到X先生和M级球员最喜欢的镜头。

对于某些测试用例,蛮力解决方案是超时的。我只需要一个想法。

class node:
    def __init__(self, low, high):
        self.left = None
        self.right = None
        self.highest = high
        self.low = low
        self.high = high

class interval:
    def __init__(self):
        self.head = None
        self.count = 0

    def add_node(self, node):
        if self.head == None:
            self.head = node
        else:
            if self.head.highest < node.high:
                self.head.highest = node.high         
            self.__add_node(self.head, node)

    def __add_node(self, head, node):                   
        if node.low <= head.low:         
            if head.left == None:            
                head.left = node
            else:            
                if head.left.highest < node.high:
                    head.left.highest = node.high
                self.__add_node(head.left, node)
        else:           
            if head.right == None:                
                head.right = node
            else:               
                if head.right.highest < node.high:
                    head.right.highest = node.high          
                self.__add_node(head.right, node)

    def search(self, node):
        self.count = 0
        return self._search(self.head, node)

    def _search(self, head, node):
        if node.low <= head.high and node.high >= head.low:
            self.count += 1 
        print(self.count, head.high, head.low)        
        if head.left != None and head.left.highest >= node.low:
                return self._search(head.left, node)
        elif head.right != None:
                return self._search(head.right, node)       
        return self.count

data = input().split(" ")
N = int(data[0])
M = int(data[1])
intervals = interval()
for i in range(N):
    data = input().split(" ")
    p = node(int(data[0]), int(data[1]))
    intervals.add_node(p)
count = 0
for i in range(M):  
    data = input().split(" ")
    count += intervals.search(node(int(data[0]), int(data[1]))) 
print(count)

2 个答案:

答案 0 :(得分:1)

解决问题的关键是要意识到不需要将单个射程范围与单个射击范围进行比较,因为只需要知道相交的总数。为了在O(n log n)时间内实现这一点,可以使用以下算法。

获取镜头范围并创建两个有序列表:一个用于起始值,另一个用于结束值。示例问题有镜头[[1, 2], [2, 3], [4, 5], [6, 7]],排序后我们有两个列表:[1, 2, 4, 6][2, 3, 5, 7]。到目前为止,所有事情都可以在O(n log n)时间内完成。

接下来处理外场球员。第一位玩家的范围为[1, 5]。当我们使用起始值1进行二元搜索以排序结束值[2, 3, 5, 7]时,我们注意到所有镜头范围都在起始值之后结束。接下来,我们使用结束值5进行另一次搜索,以排序起始值[1, 2, 4, 6]我们注意到3点击范围在结束值之前或结束时开始。然后我们进行简单的计算3 - 0,得出结论:第一个外场球员可以与3范围相交。对所有外场球员(M)重复此操作需要O(m log n)时间。

答案 1 :(得分:0)

我做了一些功课并尝试用间隔树解决它。但是你已经意识到,传统的间隔树可能不适合这个问题。这是因为在搜索间隔树时只返回一个匹配,但是我们需要找到所有匹配。更确切地说,我们只需要计算所有匹配项,不需要查找所有匹配项。

所以我为你的节点添加了两个字段以便修剪。我不熟悉python,它在java中看起来像这样:

 static class Node implements Comparable<Node> {
    Node left;//left child
    Node right;//right child
    int low;//low of current node
    int high;//high of current node
    int lowest;//lowest of current subtree
    int highest;//highest of current subtree
    int nodeCount;//node count of current subtree

    @Override
    public int compareTo(Node o) {
        return low - o.low;
    }
}

为了制作一个平衡的树,我对所有间隔进行排序,然后递归地从中间到两侧构建树(使用红黑树可能更好)。这对性能影响很大,所以我建议将此功能添加到您的程序中。

到目前为止,准备工作已经完成。搜索方法如下:

  private static int search(Node node, int low, int high) {
    //pruning 1: interval [low,high] totally overlaps with subtree,thus overlaps with all children
    if (node.lowest >= low && node.highest <= high) {
        return node.nodeCount;
    }
    //pruning 2: interval [low,high] never overlaps with subtree
    if (node.highest < low || node.lowest > high) {
        return 0;
    }

    //can't judge,go through left and right child

    //overlapped with current node or not
    int c = (high < node.low || low > node.high ? 0 : 1);
    if (node.left != null) {
        c += search(node.left, low, high);
    }
    if (node.right != null) {
        c += search(node.right, low, high);
    }
    return c;
}

评论显示有两个主要的修剪。当前子树完全重叠或从不重叠时,没有必要经过孩子。

它在大多数情况下运行良好,并且已被系统接受。解决最复杂的测试用例(N = 99600,M = 98000)需要大约4000ms。我还在尝试进行更多优化,希望能提供帮助。