我发现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;
}
我的问题如下:
这是辅助方法,实际调用的方法是什么,或者作为参数传递的是什么(int[] array
和int i
)?我们不知道二叉树的大小。
该方法如何运作?当tree
为null
时,它会返回i
。这是什么意思?
扁平化是如何发生的?为什么i+1
传递给right tree
而i
传递给left tree
?
如果您可以使用此二叉树进行演示,则可以很容易地遵循:
答案 0 :(得分:23)
除了其他答案,我还是用这个gif来说明算法的执行情况。您在左侧看到了必须处理的元素,右侧是数组。
答案 1 :(得分:5)
这是一个简单的递归示例,一旦您理解了这一点,所有递归树算法都应该开始有意义。我会尽力回答你的问题:
这是辅助方法,如何实际调用实际方法,或者将什么作为参数传递给int i和int []数组 - 我们不知道二叉树的大小 < / p>
查看代码,由于这是一个面试类型的问题,我们假设int []数组足够大以适应所有内容。
i
似乎是要填充的数组的当前索引,因此第一次展平树的调用是将i
设置为0。
此方法还返回一个整数,该整数是到目前为止已填充的元素数。这意味着最终返回将返回树的全长(以及数组中填充的元素数)
int[] array = new int[size];
Node root = ...
int bytesWritten = FlattenTreeIntoArray(root, array, 0);
//bytesWritten should equal size
assert(bytesWritten == size);
该方法如何起作用:当树为空时,它返回i。这是什么意思?
如果没有孩子,则假定Node
的左侧或右侧字段指向null。由于我们使用i
维护数组中的位置,如果没有子节点,我们不会更新i
的值。
扁平化是如何发生的?为什么i + 1被传递到右树,但我传给了左树。
通过找到最左边的节点并将其放入i
来进行展平。然后找到下一个最左边的节点并将其放入i+1
等
让我们来看看你给出的树例子:
node = 8 i = 0; array = {}
得到root的左边节点,这是3并递归调用这个flatten方法,当得到1的左边节点为null时,得到3的左边节点为1,并再次递归调用flatten方法。返回i
= 0。
现在我们处于节点1 array[0] = Node 1's value
=`array [0] = 1。
现在调用节点1的右侧字段并展平增量i。节点1的右侧字段为空,因此我们返回i的当前值(即1)。
现在我们回到节点1的flatten方法并且已经finsished左右递归并且已经到达方法的末尾,所以我们返回当前值i
,即1。
现在我们又回到了节点3的flatten方法。我们刚刚完成了对left字段的flatten方法的调用,返回值为1,这就是我们设置的值。
现在我们用节点3的值
更新数组array[1] = 3;
现在我们用增加的i展平节点3的右侧字段(节点6),以便i=2
等
希望有所帮助
答案 2 :(得分: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?
这意味着您正在检查不存在的“子”,因此您不会更改索引。
扁平化是如何发生的?为什么我+ 1被传递到右树,但我 离开了树。
您将i传递给左侧树,因为该调用将通过递归证明传回当前节点上的数据所属的树中的索引。然后,您必须告诉正确的树索引已增加,因为根据定义,右侧树中的任何内容都将具有比当前节点更高的值。
您的电话将FlattenTreeIntoArray(my_tree, my_array, 0)
my_array.length == 9
在第一次调用时,左子树是根节点3
,它通过索引0.在调用左子树之后, new 左子树是一个叶子数据1.当以 1 作为根调用FlattenTreeIntoArray
时,它会将空子项传递给FlattenTreeIntoArray
,从而返回0.
您的下一个电话将是my_array[0] = 1
,因此数组的第一个元素是树中的最小数字。然后它将递增并返回索引。
因此,最终根目录为3的调用将返回1
作为索引,并将调用my_array[1] = 3
。等等,冲洗并重复。
答案 3 :(得分:1)
这是非常实用的代码(请参阅functional-programming)。这种类型的树遍历称为in-order。
i
仅增加一次,如果当前节点不为空。 i
永远不会减少。array
仅写入。唯一的写入是在索引i
上。 array
不执行任何其他操作(传递其引用除外)。array
。i
增量发生在每次array[i]
写入后,不会执行其他i
增量。tree
显然是要处理的树。i
的含义是“array
”中第一个(从左到右)空位置的索引。所以一开始应该是0。array
应包含展平的树。很明显,array
从左到右填充,没有机会调整其大小。因此应将其初始化为足够长的空数组。如果时间不够长,ArrayIndexOutOfBoundsException
会在i
值超过其大小时在递归树的某处抛出。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在程序运行(指令序列)上证明它们。就这些。我不会在这里发布详细信息,因为它很简单,但很难正式描述,因此需要很大的空间。但我很乐意回答评论中的任何问题,也许可以编辑我的答案,包括其中一些问题。