给定堆的前序遍历,构造堆

时间:2012-09-21 13:14:46

标签: algorithm heap tree-traversal

在互联网的某个地方看到这个问题并尝试解决它。我可以解决堆是严格二叉树的情况(通过反复分区前序遍历)但当堆只是一个完整的二叉树时无法找出算法。

例如,如果1, 2, 3, 4, 5, 6, 7是最小堆的前序遍历,

堆的大小为7

1是堆中的第一个元素(考虑到,堆表示为数组)

下一个(size - 1) / 2元素将位于1

的左子树中

2, 3, 4将位于1

的左子树中

最后(size - 1) / 2个元素位于1

的右子树中

5, 6, 7将位于1

的右侧子树中

可以通过递归应用此逻辑来构造完整的堆。

该解决方案适用于这些情况,其中堆是严格二叉树

       1
    2     3
  4   5  6  7

但显然,这对于非叶元素有一个或没有子元素的堆的情况不起作用。例如,

          1                1
       2     3         2     3
     4   5  6        4     5

我想不出任何可以做同样事情的干净算法。任何解决方案/建议都会有所帮助。

4 个答案:

答案 0 :(得分:2)

查看一些示例将使这更容易。随着孩子数量的增加,我们看到以下模式:

  • 如果孩子的数量是2,则分割是:(1,1)
  • 如果孩子的数量是3,则分割是:(2,1)

当孩子的数量在2到6之间时,继续这样,我们得到以下分裂:

(1, 1), (2, 1), (3, 1), (3, 2), (3, 3)

当孩子的数目在6到14之间时,我们得到:

(3, 3), (4, 3), (5, 3), (6, 3), (7, 3), (7,4), (7, 5), (7, 6), (7, 7)

所以,当孩子的数量介于(2 ^ k-2)和(2 ^ {k + 1} -2)之间时,我们得到:

 either a split of the form (2^{k-1}-1+l, 2^{k-1}-1) where   0 <= l <= 2^{k-1} or
                            (2^k-1, 2^{k-1}-1+l)     where   0 <= l <= 2^{k-1}

然后逻辑是找到一个k,使得(2 ^ k-2)&lt; = childCount&lt; =(2 ^ {k + 1} -2)并按如下方式拆分:

Let l = childCount - (2^k-2)
If  l <= 2^{k-1} 
    split with (2^{k-1}-1+l, remaining)
Else 
    split with (2^k-1, remaining)

答案 1 :(得分:0)

你试图通过仅应用给你的两条信息中的一条来解决这个问题。

您拥有的信息是:

  • 你有一棵二叉树
  • 所述树是堆排序的

现在,虽然这是真的,你通常需要两个二进制遍历来获得第三个(前,后,有序为三),这里,你有一个额外的信息:二叉树是heap

二进制堆总是一个完整的二叉树。 完整二叉树是这样的二叉树,其中树的所有级别都已满,除了最后一级,它总是从左到右填充。换句话说,堆不可能有一个内部节点少于两个子节点。

答案 2 :(得分:0)

将预订遍历转换为标准堆表示应该很简单。预购自我,左,右。对于基于1的数组中的堆,节点N的左子节点为2N,右子节点为2N + 1。 这直接导致了这个算法:

def constructHeap(preorder, pidx, heap, hidx)  
    return pidx if (hidx>=heap.size)         #no more children
    heap[hidx] = preorder[pidx]              #self
    pidx = constructHeap(preorder, pidx+1, heap, hidx*2) #left
    return constructHeap(preorder, pidx, heap, hidx*2+1) #right
end

preorder = [1,2,3,4,5,6,7]
heap = Array.new(preorder.size+1)            #create storage
constructHeap(preorder, 0, heap, 1)
puts heap

答案 3 :(得分:0)

使用前序遍历,以排序顺序生成其元素的堆就是这样 它由矢量(1,2,5,3,4,6,7)表示。没有堆 inorder遍历以排序顺序生成密钥。这是因为 在一个堆中,父级总是少于它的所有子级或大于它的所有子级 儿童。由(7,3,6,1,2,4,5)表示的堆是其中一个的示例 在后序遍历期间按排序顺序生成其键。