如何查找提供特定最大堆结构的输入

时间:2016-05-23 01:03:50

标签: algorithm computer-science

我理解堆是如何工作的,但有一个问题我不知道如何解决。

假设你有一个最大堆(不是BST),

[149,130​​,129,107,122,124,103,66,77,91,98,10,55,35,72]

找到一个输入列表,它们会为您提供相同的堆结构,这样每个连续的值都可能是最大值:

[66,91,10,107,122,35,55,77,130,98,149,124,129,72,103]

所以换句话说,如果你要先插入66然后插入91然后插入10然后插入107,依此类推到空的最大堆中,那么在所有冒泡之后你最终会得到给定的堆结构,依此类推。你怎么会在第一时间找到这个输入?

有人可以提出任何想法吗?

由于

1 个答案:

答案 0 :(得分:2)

考虑这个最大堆(我将其绘制为树,但代表[7, 6, 5, 4, 3, 1, 2]

    7
 6     5
4 3   1 2

可插入的最后一个元素是什么?堆中填充的最后一个槽必须是树的右下角,并且冒泡过程只能沿着从该节点到顶部的路径触摸元素。因此插入的前一个元素必须是7,5或2.并非所有这些都是可能的。如果它是7,那么树在插入之前必须看起来像这样(_代表我们要在冒泡之前插入的插槽):

    5
 6     2
4 3   1 _

违反了堆约束。如果5是要插入的最后一个元素,那么堆将如下所示:

    7
 6     2
4 3   1 _

这样可行,所以5可能是插入的最后一件事。同样,2也可能是插入的最后一件事。

通常,如果沿着路径下面的所有节点都至少与其父节点的另一个子节点一样大,则沿着最右下方节点的路径中的元素可能是最后插入的元素。在我们的例子中:7不能是插入的最后一个因为5< 6. 5可以是插入的最后一个因为2> 1. 2可以是最后插入的东西,因为它没有任何孩子。

通过这种观察,可以生成所有可能通过递归导致堆的输入序列(以相反的顺序)。

这是在您给出的示例上运行的一些代码,并验证它生成的每个输入序列实际上是否生成给定的堆。有226696个不同的输入,但程序只需几秒钟就可以运行。

# children returns the two children of i. The first
# is along the path to n.
# For example: children(1, 4) == 4, 3
def children(i, n):
    i += 1
    n += 1
    b = 0
    while n > i:
        b = n & 1
        n //= 2
    return 2 * i + b - 1, 2 * i - b

# try_remove tries to remove the element i from the heap, on
# the assumption is what the last thing inserted.
# It returns a new heap without that element if possible,
# and otherwise None.
def try_remove(h, i):
    h2 = h[:-1]
    n = len(h) - 1
    while i < n:
        c1, c2 = children(i, n)
        h2[i] = h[c1]
        if c2 < len(h) and h[c1] < h[c2]:
            return None
        i = c1
    return h2

# inputs generates all possible input sequences that could have
# generated the given heap.
def inputs(h):
    if len(h) <= 1:
        yield h
        return
    n = len(h) - 1
    while True:
        h2 = try_remove(h, n)
        if h2 is not None:
            for ins in inputs(h2):
                yield ins + [h[n]]
        if n == 0: break
        n = (n - 1) // 2

import heapq

# assert_inputs_give_heap builds a max-heap from the
# given inputs, and asserts it's equal to cs.
def assert_inputs_give_heap(ins, cs):
    # Build a heap from the inputs.
    # Python heaps are min-heaps, so we negate the items to emulate a max heap.
    h = []
    for i in ins:
        heapq.heappush(h, -i)
    h = [-x for x in h]
    if h != cs:
        raise AssertionError('%s != %s' % (h, cs))

cs = [149, 130, 129, 107, 122, 124, 103, 66, 77, 91, 98, 10, 55, 35, 72]

for ins in inputs(cs):
    assert_inputs_give_heap(ins, cs)
    print ins