【问题标题】:Given the preorder traversal of a heap, construct the heap给定堆的前序遍历,构造堆
【发布时间】:2012-09-21 13:14:46
【问题描述】:

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

例如,如果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

我想不出任何干净的算法可以做到这一点。任何解决方案/建议都会有帮助。

【问题讨论】:

  • A binary heap 根据定义是一棵完整的树,因此您所描述的它不起作用的情况 - 不会发生在二叉堆中。你说的是二叉堆吗?
  • considering, the heap is represented as an array,不,表示为前序遍历。忘记它是如何存储在数组中的。
  • But apparently, this does not work in case of heap where a non-leaf element has one or no children. I couldn't think of any clean algorithms that could do the same. 你能举个这样的例子吗,所以我们都在谈论同一件事?
  • @phant0m,想象一棵大小为 4 的树:A 的孩子是 B 和 C,B 的孩子是 D。这是一棵完全二叉树,其中非叶元素有一个孩子。我认为其他情况(没有孩子)是不可能的,因为根据定义,非叶节点必须至少有一个孩子。
  • @Kevin,不,我的意思是一个预购遍历示例,它显示了他的想法失败的地方。据我了解,他只提供了一个他的想法有效的深入示例,但没有提供失败的示例,这会更有趣。此外,每个人都可以根据相同的示例提出解决方案。

标签: algorithm heap tree-traversal


【解决方案1】:

看几个例子会让这更容易。随着孩子数量的增加,我们看到以下模式:

  • 如果子节点数为 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}

然后的逻辑是找到一个满足 (2^k-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)

【讨论】:

    【解决方案2】:

    您正试图通过仅应用提供给您的两条信息之一来解决此问题。

    你掌握的信息是:

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

    现在,虽然您通常需要两次二叉遍历来获得第三个(前、后、按顺序是三个),但在这里,您有一个额外的信息:二叉树是heap.

    二叉堆总是是一棵完全二叉树。 完全二叉树就是这样一棵二叉树,其中树的所有层都是满的,也许最后一层除外,它总是从左到右被填满。换句话说,堆不可能有一个内部节点少于两个子节点。

    【讨论】:

    • 不,binary heap 始终是一个完整的二叉树。堆只是满足堆属性的树。
    • @phant0m 是的。但正如我所理解的那样,它是关于二进制堆的。问题是如何处理具有 1 或 0 个子元素的非叶元素(这有点令人困惑,因为根据定义,具有 0 个子元素的元素是叶)。编辑于。
    • 你是对的。也许我应该选择一个更“宽松”的措辞:)
    【解决方案3】:

    将前序遍历转换为标准堆表示应该很简单。预购访问自己,左,右。对于从 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
    

    【讨论】:

      【解决方案4】:

      通过前序遍历,一个按排序顺序生成其元素的堆是 它由向量 (1,2,5,3,4,6,7) 表示。不存在堆 中序遍历按排序顺序生成键。这是因为 在堆中,父总是小于其所有子或大于其所有 孩子们。由 (7,3,6,1,2,4,5) 表示的堆是其中的一个示例 在后序遍历期间按排序顺序生成其键。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-06
        • 2012-02-17
        • 2016-07-14
        • 2011-03-28
        • 2016-08-13
        相关资源
        最近更新 更多