Python 关于二叉树最大直径问题的困惑

Python 关于二叉树最大直径问题的困惑,python,algorithm,binary-tree,depth-first-search,Python,Algorithm,Binary Tree,Depth First Search,不知道这是怎么回事 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_hei

不知道这是怎么回事

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\u height
中的最后一行。为什么需要返回
max(leftTreeDiameter,rightTreeDiameter)+1

我显然不明白,但我知道的是,对于每个
currentNode
,我们将继续沿着树的左侧走,同样地,然后对右侧走。如果我们最终没有节点(意思是在我们处于叶节点之前),那么我们返回0,因为我们不想为不存在的节点添加1

除了0之外,似乎唯一要添加任何内容的地方是
calculate_height
中的最后一行代码,因为尽管我们正在添加
leftTreeDiameter+rightTreeDiameter+1
以获得总直径,但这只可能是因为
返回0
返回最大值(leftTreeDiameter,rightTreeDiameter)+1
正确吗

此外,我还不明白为什么可以为leftTreeDiameter分配
self.calculate\u height(currentNode.left)
。我的意思是我想我需要像

def calculate_left_height(self, currentNode, height=0):
  if currentNode is None:
    return 0

  self.calculate_height(currentNode.left, height + 1)
  
  return height
我们每次只在高度上加1。在本例中,我没有执行类似于
leftTreeDiameter+=self.calculate_height(currentNode.left)
的操作,而是在每次看到节点时作为参数
height+1
传入

但是如果我这样做的话,我需要一个单独的方法来计算正确的高度,在我的
find_diameter
方法中,需要递归调用
find_diameter
,使用
root.left
root.right


我的逻辑哪里错了?计算高度是如何工作的。我想我在试图找出如何跟踪堆栈时遇到了问题?

此代码中使用的名称令人困惑:
leftTreeDiameter
rightTreeDiameter
不是直径,而是高度

第二,函数
calculate_height
有副作用,不是很好。一方面,它返回一个高度,同时指定一个直径。这令人困惑。许多Python程序员希望函数是纯函数,只返回一些东西,而不改变其他任何东西。或者,函数只能改变某些状态而不能返回它。两者兼而有之可能会令人困惑

此外,令人困惑的是,尽管该类被称为
TreeNode
,但它的
find\u 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
的重复赋值始终是同一个属性。不是在每个节点上都创建此属性。。。就在您调用的节点上
查找直径

我希望插入的注释阐明了该算法的工作原理


注意:您自己关于创建
calculate\u left\u height
的想法是行不通的:它从不更改作为参数接收的
height
的值,并最终返回该值。因此,它返回与已接收到的值相同的值。这显然没有多大作用……

此代码中使用的名称令人困惑:
leftTreeDiameter
rightTreeDiameter
不是直径,而是高度

第二,函数
calculate_height
有副作用,不是很好。一方面,它返回一个高度,同时指定一个直径。这令人困惑。许多Python程序员希望函数是纯函数,只返回一些东西,而不改变其他任何东西。或者,函数只能改变某些状态而不能返回它。两者兼而有之可能会令人困惑

此外,令人困惑的是,尽管该类被称为
TreeNode
,但它的
find\u 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
的重复赋值始终是同一个属性。不是在每个节点上都创建此属性。。。就在您调用的节点上
查找直径

我希望插入的注释阐明了该算法的工作原理


注意:您自己关于创建
calculate\u left\u height
的想法是行不通的:它从不更改作为参数接收的
height
的值,并最终返回该值。因此,它返回与已接收到的值相同的值。这显然没有多大作用……

树的“直径”是多少?我不确定是否能很好地解释它,但在父节点,如果你转到左边的子节点,然后从该子节点转到左边和左边,依此类推,从父节点执行同样的操作,但每次看到一个节点时向右计数,并为父节点添加1。这就是这个问题的直径。如果存在直径大于该直径的子树,则应考虑该直径。这有意义吗?也许一个问题的链接会是最好的…对不起,这是一个在线课程,是付费的,所以我不能简单地提供一个链接,但我认为@trincot已经为我澄清了