在二叉树中查找最大独立集的大小 - 为什么错误的“解决方案”不起作用?

时间:2012-05-07 17:05:36

标签: algorithm binary-tree

以下是一个类似问题的链接,答案很好:Java Algorithm for finding the largest set of independent nodes in a binary tree

我想出了一个不同的答案,但我的教授说它不起作用,我想知道为什么(他没有回复电子邮件)。

问题:

  

给定一个包含n个整数的数组A,其索引从0开始(即A[0],   A[1],...,A[n-1])。我们可以将A解释为两者中的二叉树   A[i]的孩子是A[2i+1]A[2i+2],以及每个孩子的价值   element是树的节点权重。在这棵树上,我们说a   如果顶点集合不包含任何顶点,则它们是“独立的”   亲子对。独立集的权重就是   其元素的所有权重的总和。开发算法   计算任何独立集合的最大权重。

我提出的答案使用了以下关于二叉树中独立集的两个假设:

  1. 同一级别的所有节点彼此独立。
  2. 交替级别上的所有节点彼此独立(没有父/子关系)
  3. 警告:我在考试期间想出了这个,但这并不漂亮,但我只是想知道我是否可以争取至少部分学分。

    那么,为什么你不能只建立两个独立的集合(一个用于奇数级别,一个用于偶数级别)?

    如果每组中的任何权重都是非负数,则将它们相加(丢弃负数元素,因为这不会影响最大权重集)以找到具有最大权重的独立集合。

    如果集合中的权重都是负数(或等于0),请对其进行排序并返回最接近0的负数。

    比较两组中每一组的最大独立集的权重,并将其作为最终解决方案返回。

    我的教授声称它不起作用,但我不明白为什么。为什么它不起作用?

2 个答案:

答案 0 :(得分:1)

Interjay已经注意到为什么你的回答不正确。问题可以通过递归算法find-max-independent来解决,给定二叉树,它考虑两种情况:

  1. 根节点给出的最大独立集是什么 包括在内呢?
  2. 根节点给出的最大独立集是什么 不包括在内?
  3. 在案例1中,由于包含了根节点,因此它的子节点都不能。因此,我们将root的孙子find-max-independent的值加上root的值(必须包括),然后返回。

    在案例2中,我们返回子节点的find-max-independent的最大值(如果有的话)(我们只能选择一个)

    算法看起来像这样(在python中):

    def find_max_independent ( A ):
        N=len(A)
    
        def children ( i ):
            for n in (2*i+1, 2*i+2):
                if n<N: yield n
    
        def gchildren ( i ):
            for child in children(i):
                for gchild in children(child):
                    yield gchild
    
        memo=[None]*N
    
        def rec ( root ):
            "finds max independent set in subtree tree rooted at root. memoizes results"
    
            assert(root<N)
    
            if memo[root] != None:
                return memo[root]
    
            # option 'root not included': find the child with the max independent subset value
            without_root = sum(rec(child) for child in children(root))
    
            # option 'root included': possibly pick the root
            # and the sum of the max value for the grandchildren
            with_root =  max(0, A[root]) + sum(rec(gchild) for gchild in gchildren(root))
    
            val=max(with_root, without_root)
            assert(val>=0)
            memo[root]=val
    
            return val
    
    
        return rec(0) if N>0 else 0
    

    一些测试用例说明:

    tests=[
        [[1,2,3,4,5,6], 16], #1
        [[-100,2,3,4,5,6], 6], #2
        [[1,200,3,4,5,6], 200], #3
        [[1,2,3,-4,5,-6], 6], #4
        [[], 0],
        [[-1], 0],
    ]
    
    for A, expected in tests:
        actual=find_max_independent(A)
        print("test: {}, expected: {}, actual: {} ({})".format(A, expected, actual, expected==actual))
    

    示例输出:

    test: [1, 2, 3, 4, 5, 6], expected: 16, actual: 16 (True)
    test: [-100, 2, 3, 4, 5, 6], expected: 15, actual: 15 (True)
    test: [1, 200, 3, 4, 5, 6], expected: 206, actual: 206 (True)
    test: [1, 2, 3, -4, 5, -6], expected: 8, actual: 8 (True)
    test: [], expected: 0, actual: 0 (True)
    test: [-1], expected: 0, actual: 0 (True)
    

    测试案例1

    test case 1

    测试案例2

    test case 2

    测试案例3

    test case 3

    测试案例4

    test case 4

    记忆算法的复杂性为O(n),因为每个节点都会调用rec(n)一次。这是一种使用深度优先搜索的自上而下的动态编程解决方案。

    (测试用例插图由leetcode的交互式二叉树编辑器提供)

答案 1 :(得分:0)

您的算法不起作用,因为它返回的节点集将全部来自奇数级别,或者全部来自偶数级别。但最佳解决方案可以包含两者的节点。

例如,考虑一个树,其中所有权重都是0,除了两个具有权重1的节点。这些节点中的一个处于级别1而另一个处于级别4.最佳解决方案将包含这两个节点并且具有权重但是你的算法只给出其中一个节点并且权重为1。