Как getHeight рекурсивно определяет высоту двоичного дерева? - PullRequest
3 голосов
/ 08 ноября 2019

Я действительно не понимаю логику кода для вычисления высоты двоичного дерева. Если кто-то это понимает, вы можете объяснить это по-простому?

Я пытался понять, расставляя точки останова, но все же логика мне неясна.

import pdb
class Node:
  def __init__(self,data):
      self.right=self.left=None
      self.data = data
      #print(self.data)
class Solution:  #creating and inserting the nodes
  def insert(self,root,data):
      #print(data)
      if root==None:
         return Node(data)
      else:
         if data<=root.data:
             cur=self.insert(root.left,data)
             root.left=cur
         else:
             cur=self.insert(root.right,data)
             root.right=cur
      return root
  def getHeight(self,root): #finding the height of the largest 
                               branchintree
    #Write your code here
    if not root:
        return -1
    if not root.left and not root.right:
        return 0
    left_height=self.getHeight(root.left)
    #print('left_height',left_height)
    right_height=self.getHeight(root.right)
    #print('right_height',right_height)
    r=max(left_height,right_height) + 1
    #print('r',r)
    return max(left_height,right_height) + 1




T=int(input())
pdb.set_trace()
myTree=Solution()
root=None
for i in range(T):
   data=int(input())
   root=myTree.insert(root,data)
height=myTree.getHeight(root)
print(height)

1 Ответ

1 голос
/ 09 ноября 2019

Обратите внимание, что алгоритм нахождения высоты дерева одинаков как для двоичных деревьев поиска, так и для общих двоичных деревьев, поэтому для целей этого обсуждения я буду игнорировать BST.


Этот код не совсем понятен, поэтому я не виню вас за то, что вы его не поняли.

Вот переписать, чтобы сократить шум:

from collections import namedtuple

Node = namedtuple("Node", "data left right")

def height(root): 
    return max(height(root.left), height(root.right)) + 1 if root else 0
    #      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^         ^^^^^^     
    #      |                                          |           |
    #      |                                          |           base case; root is None
    #      |                                          add the current node's height
    #      recursively find the maximum height of the current node's children


if __name__ == "__main__":
    """     1 
          /   \
         2     3
          \    /
           4  5
               \
                6 
    """
    root = Node(
        1,
        Node(
            2,
            None,
            Node(4, None, None)
        ),
        Node(
            3,
            Node(
                5,
                None,
                Node(6, None, None)
            ),
            None
        )
    )
    print(height(root)) # => 4

Теперь у нас есть два случая в данном вызове height:

  • root равно None, и в этом случае мы возвращаем 0, потому что у нас ничего нет. Это наш базовый случай или условие, которое завершает рекурсию.
  • root не равно None, и в этом случае мы возвращаем 1, чтобы подсчитать высоту текущего рассматриваемого узлаэтим конкретным рекурсивным вызовом плюс максимум высоты левого и правого поддеревьев, укорененных в текущем узле. Это критический рекурсивный шаг.

Давайте пройдемся по вызову к height в приведенном выше примере дерева.

Сначала мы посетим узел {1} в качестве нашего корня. Это не базовый случай, поэтому {1} запрашивает у своего левого потомка {2} максимальную высоту. {2} также не является базовым случаем, поэтому он запрашивает у своего левого потомка, None. None является нашим первым базовым случаем и возвращает 0 обратно к {2}. {2} затем спрашивает высоту своего правого потомка, {4}. {4} - это лист с двумя пустыми None ссылками, которые возвращают 0, но {4} добавляет 1 для себя и возвращает это {2}.

Узел {2} теперь может вычислять max(height(root.left), height(root.right)), что составляет max(0, 1) => 1, а {2} может добавить 1, чтобы подсчитать собственную высоту и сообщить {1}, общее число 2.

Наш корень {1} теперь имеет высоту 2 для своего левого поддерева, но он еще не проверил свое правое поддерево, поэтому max(height(root.left), height(root.right)) должен ждать.

Процесс в основном такой же для правого поддерева {1}: если узел не является конечным узлом, он запрашивает у своих дочерних элементов их соответствующие высоты и принимает максимальное значение, добавляя 1 к самому себе,и сообщать родителю. В этом случае {6} сообщает о высоте от 1 до {5}, {5} сообщает о высоте от 2 до {3}, а {3} сообщает о высоте от 3 до {1}. Наконец, {1} может вычислить max(2, 3) и добавить 1 для его собственной высоты, возвращая конечному результату 4 для вызывающего абонента.

Если все это немного абстрактно, вы всегда можете использовать инструментрекурсивный вызов для добавления параметра depth и вывода его статуса.

from collections import namedtuple

Node = namedtuple("Node", "data left right")

def height(root, depth=0): 
    if root:
        print(" " * depth + f"{{{root.data}}} asking children...")
        left_height = height(root.left, depth + 4)
        right_height = height(root.right, depth + 4)
        ret = max(left_height, right_height) + 1
        print(" " * depth + f"{{{root.data}}} reporting max({left_height}, {right_height}) + 1 = {ret}")
        return ret

    print(" " * depth + "None returning 0")
    return 0


if __name__ == "__main__":
    """     1 
          /   \
         2     3
          \    /
           4  5
               \
                6 
    """
    root = Node(
        1,
        Node(
            2,
            None,
            Node(4, None, None)
        ),
        Node(
            3,
            Node(
                5,
                None,
                Node(6, None, None)
            ),
            None
        )
    )
    print(height(root)) # => 4

Вывод:

{1} asking children...
    {2} asking children...
        None returning 0
        {4} asking children...
            None returning 0
            None returning 0
        {4} reporting max(0, 0) + 1 = 1
    {2} reporting max(0, 1) + 1 = 2
    {3} asking children...
        {5} asking children...
            None returning 0
            {6} asking children...
                None returning 0
                None returning 0
            {6} reporting max(0, 0) + 1 = 1
        {5} reporting max(0, 1) + 1 = 2
        None returning 0
    {3} reporting max(2, 0) + 1 = 3
{1} reporting max(2, 3) + 1 = 4
4
...