【问题标题】:How do I get the PreOrder,InOrder,PostOrder to work?如何让 PreOrder、InOrder、PostOrder 工作?
【发布时间】:2021-06-17 12:55:57
【问题描述】:

我如何让 PreOrder、InOrder、PostOrder 工作?

这是我当前的代码和实现,请参阅 InOrder()、PreOrder()、PostOrder()。我有来自 Geek4Geek (https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/) 的参考资料。

当我执行 print(bst.InOrder()) 时,它返回 None?

import os
import pygraphviz as pgv
from collections import deque
from IPython.display import Image, display

class BST:
    root=None

    def get(self,key):
        p = self.root
        while p is not None:
            if p.key == key:
                return p.val
            elif p.key > key: #if the key is smaller than current node, then go left (since left are all the small ones)
                p = p.left
            else:   # else if key is bigger than current node, go to right (since right are all the big ones)
                p = p.right 
        return None
        
    def put(self, key, val):
        self.root = self.put2(self.root, key, val)

    def put2(self, node, key, val):
        if node is None:
            #key is not in tree, create node and return node to parent
            return Node(key, val)
        if key < node.key:
            # key is in left subtree
            node.left = self.put2(node.left, key, val)
        elif key > node.key:
            # key is in right subtree
            node.right = self.put2(node.right, key, val)
        else:
            node.val = val
        return node

    # draw the graph
    def drawTree(self, filename):
        # create an empty undirected graph
        G=pgv.AGraph('graph myGraph {}')

        # create queue for breadth first search
        q = deque([self.root])
        # breadth first search traversal of the tree
        while len(q) != 0:
            node = q.popleft()
            G.add_node(node, label=node.key+":"+str(node.val))
            if node.left is not None:
                # draw the left node and edge
                G.add_node(node.left, label=node.left.key+":"+str(node.left.val))
                G.add_edge(node, node.left)
                q.append(node.left)
            if node.right is not None:
                # draw the right node and edge
                G.add_node(node.right, label=node.right.key+":"+str(node.right.val))
                G.add_edge(node, node.right)
                q.append(node.right)

        # render graph into PNG file
        G.draw(filename,prog='dot')
        display(Image(filename))

    def createTree(self):
        self.put("F",6)
        self.put('I',9)
        self.put("J",10)
        self.put("G",7)
        self.put("H",8)
        # left side of F:6
        self.put("D",4)
        self.put("C",3)
        self.put("B",2)
        self.put("A",1)
        self.put("E",5)

   

    def createBalancedTree(self):
      self.put("F",6)
      self.put("A",1)
      self.put("B",2)
      self.put("C",3)
      self.put("D",4)
      self.put("E",5)
      self.put("G",7)
      self.put("H",8)
      self.put('I',9)
      self.put("J",10)
    
    def find(self, key):
        p = self.root
        while p is not None:
            if p.key == key:
                return p
            elif p.key > key:
                p = p.left
            else:
                p = p.right
        return

    def size(self,key): 
      return self.size2(self.find(key)) #using the find function which gives the node instead
    
    def size2(self, subtree):
      if not subtree: #if given key is not found in entire tree (means not a node in this tree)
        return 0
      else:
        return 1 + self.size2(subtree.left) + self.size2(subtree.right)
    

    def depth(self,key):
      p = self.root                         # set the default node as Root, since we start from Root then top-bottom approach. 
      if p is not None:
        if p.key == key:                    # if key is root, then return 0 (cus at Root, there is no depth)
          return 0
        elif p.key > key:                   # if Key is smaller than current node, then search in the left side 
          return self.depth2(p.left,key,0)
        else:                               # if key is bigger than current node, search the right side 
          return self.depth2(p.right,key,0)
    
    def depth2(self,node,key,counter):
      # lets say you put a depth(Z), at depth(), it wouldt know Z exits or not, so it will call depth2() to find out. In depth2(), It will check 'Z' throughtout node.key>key and node.key<key..
      # still cannot find after all the iteration, then it will return None
      if node is not None:                 
        if node.key > key:        
          return self.depth2(node.left,key,counter+1)
        elif node.key < key:                     
          return self.depth2(node.right,key,counter+1)
        elif node.key == key:   
          return counter + 1  # this code will only run when you find your key. So example you do depth(E), it will start from F, then D, then found E. In total 2
      else:
        return None
    

 
    def height(self,key):
      x = self.root
      if x == key:
        return 0
      else:
        return self.height2(self.find(key))
    
    def height2(self,subtree):
        if not subtree:
          return -1 #Key is not a node in the tree
        else:
          return max(self.height2(subtree.left), self.height2(subtree.right)) + 1
    

    def InOrder(self):
      if self == self.root:
        InOrder(self.left)
        print(self.key)
        InOrder(self.right)
    
    #def PreOrder(self):
    #def PostOrder(self):
        
      
class Node:
    left = None
    right = None
    key = 0
    val = 0

    def __init__(self, key, val):
        self.key = key
        self.val = val

我应该怎么做才能打印出来?

【问题讨论】:

  • size 方法忽略它的参数。它总是完全相同。递归调用,怎么会停止呢?

标签: python recursion key binary-search-tree root


【解决方案1】:

顺序

您应该针对有关此代码的每个独特问题打开新帖子。当前的问题与原来的问题有很大的不同。无论如何,遵循现有代码的模式,这就是我可能会如何处理inorder -

class BST:
    # ...
    def inorder(self):
        return self.inorder2(self.root)

    def inorder2(self, t):
        if not t: return
        yield from self.inorder2(t.left)
        yield (t.key, t.val)
        yield from self.inorder2(t.right)

另一种编写方式是使用嵌套函数 -

class BST:
    # ...
    def inorder(self):
        def loop(t):
            if not t: return
            yield from loop(t.left)
            yield t
            yield from loop(t.right)
        return loop(self.root)

注意print 是如何与inorder 函数分离的。这允许调用者使用遍历逻辑并为每个节点选择操作 -

for node in bst.inorder():
  print(node.key, node.val)

可重用性

定义inorder 后,您可以重新定义BST 类中的一些其他函数-

class BST:
  # ...
  def find(self, key):
    for node in self.inorder():
      if node.key == key:
        return node

  def size(self, key):
    p = self.find(key)
    if not p: return 0
    return sum(1 for _ in BST(p).inorder())

【讨论】:

  • 您好,很抱歉造成混乱!!!作为 stackoverflow 的新手,我只允许每 3-4 天发布一次。反正我已经想通了嘿嘿。非常感谢你一直以来的帮助:) 真的很感激,你帮助我理解的比教给我的要清楚得多。
【解决方案2】:

代码审查和修复

第一个问题是size 使用get,它返回树的,而不是节点。为了解决这个问题,我们将您的 get 函数重写为 find,但这次它返回一个节点 -

class BST:
    root=None
    
    def put(self, key, val): # ...
    def put2(self, node, key, val): # ...
    def createTree(self): # ...
    def createBalancedTree(self): # ...

    def find(self,key):
        p = self.root
        while p is not None:
            if p.key == key:
                return p       # return p
            elif p.key > key:
                p = p.left
            else:
                p = p.right 

        return None            # return None, not "None"

我们不需要在get 中重复这个逻辑。相反,我们调用find 来获取节点。如果节点存在,那么我们返回值 -

class BST:
    # ...

    def get(self, key):
      p = self.find(key)       # call to find
      if not p:
        return None
      else:
        return p.val           # return p.val

接下来,在size函数中,我们将使用find来获取节点。与您编写 put2 帮助程序的方式类似,我们可以编写处理循环的 size2 -

class BST:
    # ...

    def size(self,key):
      return self.size2(self.find(key)) # find, not get

    def size2(self, subtree):           # define size2 like you did put2
      if not subtree:
        return 0
      else:
        return 1 + self.size2(subtree.left) + self.size2(subtree.right)

这意味着我们没有在 Node 类中定义 size -

class Node:
    left = None
    right = None
    key = 0
    val = 0

    def __init__(self, key, val):
        self.key = key
        self.val = val

    # do not define "size" on the Node class

让我们用你的createBalancedTree() 来测试一下 -

bst = BST()
bst.createBalancedTree()

#   F
#  / \
# A   G
#  \   \
#   B   H
#    \   \
#     C   I
#      \   \
#       D   J
#        \
#         E
print(bst.size('F')) # 10
print(bst.size('A')) # 5
print(bst.size('H')) # 3
print(bst.size('D')) # 2

身高

在您的帮助下也进行了更新,我尝试了相同的方法来查找 height(),但它返回错误的答案。

我们可以写height类似于size -

class BST:
    # ...
    def height(self,key):
      return self.height2(self.find(key))
    
    def height2(self,subtree):
        if not subtree:
            return 0 
        else:
            return max(self.height2(subtree.left), self.height2(subtree.right)) + 1

深度

所以如果我做一个深度('B'),它应该返回 3。由于 B 到 F,深度级别是 3。如果我做一个深度('F'),它应该返回 0。因为有在根 F 中没有深度

我们可以写depth 非常类似于我们写find -

class BST:
    # ...
    def depth(self,key):
        p = self.root
        d = 0
        while p is not None:
            if p.key == key:
                return d
            elif p.key > key:
                p = p.left
            else:
                p = p.right
            d = d + 1 
        return None

你做得很好!您的代码没有问题,如下所示-

bst2 = BST()
bst2.createTree()

#          F
#        /   \
#       D     I
#      / \   / \
#     C   E G   J
#    /       \
#   B         H
#  /
# A 

print(bst2.depth("F")) # 5
print(bst2.depth("I")) # 3
print(bst2.depth("B")) # 2
print(bst2.depth("Z")) # 0

改进

您能否解释一下为什么需要put2size2?抱歉,我没有找到put2...这是我的作业给定的代码

您实际上并不需要put2size2,我会说它们是一种不好的做法。问题是所有的树逻辑都在类中纠缠在一起。在答案的这一部分中,我将向您展示您的 bst 模块的总修订版。

首先我们从一个基本的node 接口开始。除了分配属性,我们只需要一个简单的__init__ 构造函数。 keyval 是必需的。 leftright 是可选的,如果未指定,则默认为 None -

# bst.py

class node:
  def __init__(self, key, val, left = None, right = None):
    self.key = key
    self.val = val
    self.left = left
    self.right = right

现在我们编写一个普通的put 函数。请注意,没有引用像 self 这样的特殊变量。另一件至关重要的事情是我们永远不会通过重新分配leftright 属性来改变(覆盖)节点。而是创建了一个新的node -

# bst.py (continued)

def put(t, k, v):
  if not t:
    return node(k, v)
  elif k < t.key:
    return node(t.key, t.val, put(t.left, k, v), t.right)
  elif k > t.key:
    return node(t.key, t.val, t.left, put(t.right, k, v))
  else:
    return node(t.key, v, t.left, t.right)

我们将继续以这种方式编写普通函数。接下来我们定义get,它是find的特化——

# bst.py (continued)

def get(t, k):
  r = find(t, k)
  if not r:
    return None
  else:
    return r.val

def find(t, k):
  if not t:
    return None
  elif k < t.key:
    return find(t.left, k)
  elif k > t.key:
    return find(t.right, k)
  else:
    return t

这里我们将稍微偏离size。这次它不需要key 参数。相反,调用者将能够在任何节点上调用size。用法将在下面演示-

# bst.py (continued)

def size(t):
  if not t:
    return 0
  else:
    return 1 + size(t.left) + size(t.right)

如果我们可以从节点列表构建树会很方便。这是对createBalancedTree 的改进,后者一遍又一遍地调用.put。我们可以叫它from_list -

# main.py

nodes = \
  [("F",6), ("A",1), ("B",2), ("C",3), ("D",4), ("E",5), ("G",7), ("H",8), ('I',9), ("J",10)]

t = bst.from_list(nodes)

我们可以在bst 模块中轻松实现from_list -

# bst.py (continued)

def from_list(l):
  t = None
  for (k,v) in l:
    t = put(t, k, v)
  return t

这是模块的最大区别。我们编写了 bst 类,但它是简单函数 putfindgetsizefrom_list 的简单包装。类中的复杂逻辑为零-

# bst.py (continued)

class bst:
  def __init__(self, t): self.t = t
  def put(self, k, v): return bst(put(self.t, k, v))
  def find(self, k): return bst(find(self.t, k))
  def get(self, k): return get(self.t, k)
  def size(self): return size(self.t)
  def from_list(l): return bst(from_list(l))

我们都完成了。我们将编写从我们的bst 模块导入的main 程序-

# main.py

from bst import bst

nodes = \
  [("F",6), ("A",1), ("B",2), ("C",3), ("D",4), ("E",5), ("G",7), ("H",8), ('I',9), ("J",10)]

t = bst.from_list(nodes)
#   F
#  / \
# A   G
#  \   \
#   B   H
#    \   \
#     C   I
#      \   \
#       D   J
#        \
#         E

还记得我说过size 不接受key 参数吗?那是因为它可以在任何节点上调用。所以要找到特定节点的大小,我们首先find它,然后size它!这是编写可重用函数的核心原则:每个函数应该只做一件事 -

print(t.find('F').size()) # 10
print(t.find('A').size()) # 5
print(t.find('H').size()) # 3
print(t.find('D').size()) # 2

功能性

我们使用的技术的一个低调的优势是我们的bst 模块可以以面向对象的方式(上)或以功能方式(下)使用。这种双重接口使我们的模块非常灵活,因为它可以以多种样式使用-

# functional.py

from bst import from_list, find, size

nodes = \
  [("F",6), ("A",1), ("B",2), ("C",3), ("D",4), ("E",5), ("G",7), ("H",8), ('I',9), ("J",10)]

t = from_list(nodes)

print(size(find(t, 'F'))) # 10
print(size(find(t, 'A'))) # 5
print(size(find(t, 'H'))) # 3
print(size(find(t, 'D'))) # 2

补充阅读

我已经写了大量关于此答案中使用的技术的文章。按照链接查看它们在其他上下文中的使用,并提供了额外的解释 -

【讨论】:

  • 谢谢,这也是 Gilles Ottervanger 的建议。但是您帮助我了解我不必在课堂上定义“大小”。你能解释一下为什么需要 put2 和 size2 吗?抱歉,我没有提出 put2... 这是我的作业的给定代码。试图了解整个事情是如何运作的。再次感谢您生动的绘图和解释:)
  • 富民我很乐意为您提供帮助。我添加了另一个部分,希望能回答您的其他问题。如果还有什么 lmk ,我会尽力提供帮助。
  • 哇!哈哈,谢谢您的解释,您实现树的方式非常清晰!但是,由于这个分配标准,我无法更改您建议的内容 T.T. 我对现在发生的事情有了更清晰的了解。我还将我的代码编辑到最新版本。在您的帮助下也进行了更新,我尝试了相同的方法来查找 depth(),但它返回了错误的答案。
  • 我在答案中添加了 depth 部分。你自己写depthdepth2 做得很棒。保持putput2sizesize2 的初始模式,这就是我会做的。也许您错误地可视化了输入树?我为bst.createTree 提供了一个树形视觉效果,我的代码示例中的depth 测量值符合我的预期。
  • 我想我没有清楚地解释 depth() 函数:( 假设返回它从根的深度。所以如果我做一个 depth('B'),它应该返回 3 .从B到F,深度级别是3。如果我做一个深度('F'),它应该返回0。因为根F没有深度。
【解决方案3】:

在我看来,您的停止条件不正确。孩子(和根)的默认值是None,所以你应该检查z == None。此外,您似乎正在混淆子节点和键。在我看来,最好的方法是首先找到具有所需键的节点,然后在该节点上递归计算子树大小。请参阅下面的代码。

# a function in the BST class that finds the node with a specific key
class BST:
    def find(self, key):
        p = self.root
        while p is not None:
            if p.key == key:
                return p
            elif p.key > key:
                p = p.left
            else:
                p = p.right
        return

    # the function that you asked about
    def getSize(self, key):
        subroot = self.find(key)
        if subroot:
            return subroot.size()
        return 0 # if the key is not found return zero

# a function in the node class to find the subtree size with that node as root
class Node:
    def size(self): 
      return 1 + (self.left.size() if self.left else 0) + (self.right.size() if self.right else 0)

【讨论】:

  • 谢谢你,但我仍然不清楚整个事情是如何运作的。我实现了您的代码,但它返回此错误“AttributeError:'int'对象没有属性'size'。”抱歉,我是新手……我已经编辑了上面的代码。请看看.. idk 有什么问题
  • 我在您的编辑中看到您使用了subroot = self.get(key) 而不是subroot = self.find(key)。您的 get 函数返回节点的值,而我的 find 函数返回节点对象本身(如果找不到,则返回 None)。除此之外,@vpfb 在我的size 函数中发现了一个错误。我用更正的版本更新了我的答案。
  • 您能解释一下您的递归函数是如何工作的吗?这一行“返回 1 + (self.left.size() if self.left else 0) + (self.right.size() if self.right else 0)”。我对它的理解是,如果你在 self.left 上找到一个节点,则加 1 否则为 0 ...类似地,如果你在右侧找到节点,则加 1 为 0。我的理解是否正确?谢谢顺便说一句,它有效。但我试图了解发生了什么。
  • 是的,1 + 用于当前节点,注意self.left.size() if self.left else 0 首先检查条件self.left,因此它检查是否有左孩子,如果有,则调用其成员函数left.size() 返回左子树的大小(这是递归调用)。如果没有左子树,则表示左子树的大小为零。必须这样做,因为您将 size 作为成员函数编写。如果 size 不是课程的一部分,您可以在内部进行此检查,如@thank-you 的回答。顺便说一句,如果我的回答对你有用,请点赞。
  • 我没有足够的声誉来投赞成票 :( 对不起。谢谢您的解释
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多