【问题标题】:Flattening a binary tree to an array in Java在Java中将二叉树展平为数组
【发布时间】:2014-01-06 18:46:52
【问题描述】:

我发现this code 用于在 Java 中将二叉树展平为数组。我很难理解它是如何工作的。

代码如下:

private static int FlattenTreeIntoArray(Node tree, int[] array, int i)
{
    if (tree == null) return i;
    // Flatten left subtree
    i = FlattenTreeIntoArray(tree.Left, array, i);
    // Get data from the current node
    array[i] = tree.Data;
    // Flatten right subtree
    i = FlattenTreeIntoArray(tree.Right, array, i + 1);
    return i;
} 

我的问题如下:

  1. 这是辅助方法,真正的方法是如何实际调用的,或者作为参数传递了什么(int[] arrayint i)?我们不知道二叉树的大小。

  2. 该方法如何工作?当treenull 时,它返回i。这是什么意思?

  3. 扁平化是如何发生的?为什么i+1 传递给right treei 传递给left tree

如果你可以用这棵二叉树来演示,那将很容易理解:

【问题讨论】:

  • 作为参考,树基本上会被展平为一个整数列表,表示来自中序遍历的数据。仅此一项信息不足以准确地重建树;你需要额外的信息。
  • i 是数组中下一个元素所在的位置。该算法需要知道树中的元素数量才能在不抛出 ArrayIndexOutOfBoundsException 的情况下工作。通常,你会有一个 Tree.size() 方法或给你这个数字的东西。

标签: java arrays algorithm binary-tree


【解决方案1】:

除了其他答案,我做了这个gif来说明算法的执行。左边是要处理的元素,右边是数组。

【讨论】:

  • 感谢精美的插图。我看到这是一个中序遍历,但我的问题是该方法是如何工作的。其他帖子已在此处进行了解释。为 gif +1。
  • @user1988876 哦,好吧,对不起:)。嗯,这可能对其他人有帮助
  • @user2336315 gif 在哪里? :-P
【解决方案2】:

这是一个简单的递归示例,一旦理解了这一点,所有递归树算法都应该开始有意义了。我会尽量回答你的问题:

这是helper方法,真正的方法是怎么调用的,或者作为参数传递给int i和int[]数组的东西——我们不知道二叉树的大小

查看代码,由于这是一个面试类型的问题,我们将假设 int[] 数组足够大以容纳所有内容。

i 似乎是要填充的数组的当前索引,因此第一个展平树的调用是将i 设置为 0。

此方法还返回一个整数,它是到目前为止已填充的元素数。这意味着最终返回将返回树的全长(以及数组中填充的元素数量)

int[] array = new int[size];
Node root = ...
int bytesWritten = FlattenTreeIntoArray(root, array, 0);

//bytesWritten should equal size
assert(bytesWritten == size);

方法作用如何:当树为null时,返回i。这是什么意思?

这假设如果没有孩子,Node 的左或右字段指向 null。由于我们使用i 来维护我们在数组中的位置,如果没有孩子,我们不会更新i 的值。

扁平化是如何发生的?为什么 i+1 传递给右树,而 i 传递给左树。 通过找到最左边的节点并将其放入i 来进行展平。然后找到下一个最左边的节点并将其放入i+1等中。

让我们看一下您给出的树示例:

节点= 8 我=0; 数组 = {}

获取root的左节点为3,递归调用该flatten方法,获取3的左节点为1,再次递归调用flatten方法,当获取为null的1的左节点。返回i = 0。 现在我们在 Node 1 array[0] = Node 1's value = `array[0] = 1 的 flatten 方法中。

现在调用节点 1 的右侧字段并将递增的 i 展平。节点 1 的右字段为 null,因此我们返回 i 的当前值(即 1)。

现在我们回到节点 1 的 flatten 方法,完成了左右递归,已经到了方法的末尾,所以我们返回 i 的当前值,即 1。

现在我们回到节点 3 的 flatten 方法。我们刚刚完成了对 left 字段的 flatten 方法的调用,该方法返回值 1,现在我们将 i 设置为。

现在我们用节点 3 的值更新数组

array[1] = 3;

现在我们用递增的 i 展平节点 3 的右侧字段(节点 6)所以 i=2

希望有帮助

【讨论】:

  • 假设我们有一棵树,其中包含三个节点、根节点及其左右子节点。如果我想把它们放在数组中,left.data`将占据index 0root.data将占据index 1right.data将占据index 2。所以左右节点,变化了两个索引,但是我们在上面的代码中只做了1+1,这是为什么呢?
  • 根据上面的一个cmets,我无法从这个数组中重建二叉树,所以这是否意味着我必须进行前序遍历
  • @user1988876 我不确定我是否理解您在评论中提出的第一个问题。如果树中有 3 个节点,则返回的 int(i 的最终值是 3。因为我们从最右边的子节点的右字段返回了 i+1 结果。即使这个字段是null,我们也没有直到我们看了才知道。而且你是对的,填充数组不是重建二叉树的最佳方法。虽然有算法可以做到这一点。面试问题更多的是关于你了解递归和二叉树吗?
  • @user1988876 是的,如果您需要重建,预购将是理想的选择。 (假设这棵树确实是二叉搜索树。)
【解决方案3】:

这是辅助方法,真正的方法是如何实际调用的,或者 什么作为参数传递给 int i 和 int[] 数组——我们没有 知道二叉树的大小

int i 是数组中的索引 - 您希望从 0 开始,因为它是第一个元素。如果您不知道树的大小,最好使用LinkedList

How does the method function: when the tree is null, it returns i. what does that mean?

这意味着您正在检查一个不存在的“子”,因此您返回索引不变。

扁平化是如何发生的?为什么 i+1 被传递给右树但我 到左树。

您将 i 传递给左树,因为该调用将通过递归证明传回树中当前节点处的数据所属的索引。然后您必须告诉右树索引已增加,因为根据定义,右树中的任何内容都将具有比当前节点更高的值。

您的电话是FlattenTreeIntoArray(my_tree, my_array, 0),其中my_array.length == 9

第一次调用时,左子树是根节点3,它通过索引0。左子树调用后,new左子树是一个叶子,数据为1 . 当FlattenTreeIntoArray1为根调用时,它会将一个null child传递给FlattenTreeIntoArray,从而返回0。

您的下一个调用将是my_array[0] = 1,因此数组的第一个元素是树中最小的数字。然后它将递增并返回索引。

所以最终根为 3 的调用将返回1 作为索引,并将调用my_array[1] = 3。以此类推,冲洗并重复。

【讨论】:

  • node 6 是如何在数组中分配索引的?
  • @user1988876 写入 3 后,它将执行索引为 2 的右树。因此它将在 2 处写入 4。没有 4 的右子树,因此返回 3。这是写 6 的地方。
  • @user1988876 你真的应该添加一些 System.out.println() 来看看它是如何工作的。
【解决方案4】:

这是非常实用的代码(参见)。这种类型的树遍历称为in-order

让我们做一些观察

  1. 树只是被读取,从未被重写。
  2. 在每次调用中,i 只增加一次,前提是当前节点不为空。 i 永远不会递减。
  3. array 仅被写入。唯一的写入在索引i 上。不会对 array 执行任何其他操作(除了传递其引用)。
  4. 当前节点数据在处理左子树和右子树之间写入array
  5. i 增量发生在每次 array[i] 写入之后,不会执行其他 i 增量。

猜测参数含义

  • tree 显然是要处理的树。
  • i的含义是“array中第一个(从左到右)空位置的索引”。所以开头应该是0。
  • 从最顶层调用返回后,array 应该包含扁平树。很明显array是从左到右填充的,没有机会调整大小。因此它应该被初始化为一个足够长的空数组。如果不够长,一旦i 的值超过其大小,ArrayIndexOutOfBoundsException 将被扔到递归树的某个位置。
  • FlattenTreeIntoArray(tree, array, i) 扁平化 tree,将此节点值的有序序列写入 array,从索引 i 开始,并返回 array 中第一个未使用位置的索引。

所以最上面的调用看起来像:

// tree and MAX_TREE_SIZE come from outside
int[] array = new int[MAX_TREE_SIZE];
int tree_size = FlattenTreeIntoArray(tree, array, 0);

现在让我们证明一下

检查这些声明并使用上面的观察结果和induction 在程序运行(指令序列)中证明它们。就这些。我不会在这里发布细节,因为它是微不足道的,但很难真正正式地描述,所以会占用很多空间。但我很乐意回答 cmets 中的任何问题,并且可能会编辑我的答案以包含其中的一些问题。

【讨论】:

    猜你喜欢
    • 2017-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-31
    • 2015-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多