问题描述
不确定这是如何工作的...
class TreeNode:
def __init__(self,val,left=None,right=None):
self.val = val
self.left,self.right = left,right
def find_diameter(self,root):
self.calculate_height(root)
return self.treeDiameter
def calculate_height(self,currentNode):
if currentNode is None:
return 0
leftTreeDiameter = self.calculate_height(currentNode.left)
rightTreeDiameter = self.calculate_height(currentNode.right)
diameter = leftTreeDiameter + rightTreeDiameter + 1
self.treeDiameter = max(self.treeDiameter,diameter)
return max(leftTreeDiameter,rightTreeDiameter) + 1
以上代码用于获取二叉树的最大直径,但我不明白 calculate_height
中的最后一行。为什么我们需要返回 max(leftTreeDiameter,rightTreeDiameter) + 1
我显然不明白,但我所知道的是,对于每个 currentNode
,我们将继续沿着树的左侧向下移动,然后类似地对右侧执行相同的操作。如果我们最终没有节点(意思是在我们处于叶节点之前),那么我们将返回 0,因为我们不想为不存在的节点添加 1。
似乎添加除 0 之外的任何内容的唯一地方是 calculate_height
中的最后一行代码,因为尽管我们添加了 leftTreeDiameter + rightTreeDiameter + 1
以获得总直径,但这只是因为 {{ 1}} 和 return 0
正确吗?
另外,我对为什么可以为 leftTreeDiameter 分配 return max(leftTreeDiameter,rightTreeDiameter) + 1
感到困惑。我的意思是,我认为我需要类似......
self.calculate_height(currentNode.left)
我们每次只在高度上加 1。在这种情况下,我没有做类似 def calculate_left_height(self,currentNode,height=0):
if currentNode is None:
return 0
self.calculate_height(currentNode.left,height + 1)
return height
之类的操作,而是在每次看到节点时将其作为参数 leftTreeDiameter += self.calculate_height(currentNode.left)
传入。
但如果我这样做,我将需要一个单独的方法来计算正确的高度,并且在我的 height + 1
方法中需要使用 find_diameter
和 find_diameter
递归调用 root.left
与root.right
。
我的逻辑哪里错了,calculate_height
是如何工作的。我想我在试图弄清楚如何跟踪堆栈时遇到了麻烦?
解决方法
此代码中使用的名称令人困惑:leftTreeDiameter
和 rightTreeDiameter
不是直径,而是高度。
其次,函数calculate_height
有副作用,不是很好。一方面它返回一个高度,同时它分配一个直径。这令人困惑。许多 Python 编码人员更喜欢纯函数,并且只返回某些东西,而不改变其他任何东西。或者,一个函数只能改变某些状态而不返回它。两者都做可能会令人困惑。
另外,虽然类被称为TreeNode
,但它的find_diameter
方法仍然需要一个节点作为参数,这一点令人困惑。这是违反直觉的。我们希望该方法将 self
作为要操作的节点,而不是参数。
但让我们重命名变量并添加一些注释:
leftHeight = self.calculate_height(currentNode.left)
rightHeight = self.calculate_height(currentNode.right)
# What is the size of the longest path from leaf-to-leaf
# whose top node is the current node?
diameter = leftHeight + rightHeight + 1
# Is this path longer than the longest path that we
# had found so far? If so,take this one.
self.treeDiameter = max(self.treeDiameter,diameter)
# The height of the tree rooted at the current node
# is the height of the highest childtree (either left or right),# with one added to account for the current node
return max(leftHeight,rightHeight) + 1
应该很清楚,但是要意识到,这个过程中的 self
始终是调用 find_diameter
方法的实例,并没有真正扮演实际节点的角色,作为根作为参数传递。因此,对 self.treeDiameter
的重复分配总是分配给相同的 one 属性。这个属性不是在每个节点上创建的……只是在你调用 find_diameter
的节点上。
我希望插入的评论已经阐明了这个算法是如何工作的。
注意:您自己创建 calculate_left_height
的想法不会这样做:它永远不会改变它作为参数接收的 height
的值,并最终返回它。所以它返回它已经收到的相同值。那显然不会做太多...