简体   繁体   English

Python 递归 function 搜索二叉树

[英]Python recursive function to search a binary tree

Very new at Python and I'm trying to understand recursion over a binary tree.在 Python 上非常新,我正在尝试了解二叉树的递归。 I've implemented a very simple tree, which funnily enough maps English characters to binary (1's and 0's).我已经实现了一个非常简单的树,它很有趣地将英文字符映射到二进制(1 和 0)。 I've only used a very simple structure because I am struggling to get my head round a more complex question that I've been set.我只使用了一个非常简单的结构,因为我正在努力思考一个我已经设定好的更复杂的问题。 I figure if I can get my head round my example then I should be able to go away and look at the question I've been set myself.我想如果我能理解我的例子那么我应该能够离开 go 并看看我自己设置的问题。

The following creates the class BinaryTree and an instance of this下面创建 class BinaryTree 和它的一个实例

class BinaryTree:
    """A rooted binary tree"""
    def __init__(self):
        self.root = None
        self.left = None
        self.right = None

def is_empty(testtree: BinaryTree) -> bool:
        """Return True if tree is empty."""
        return testtree.root == testtree.left == testtree.right == None

def join(item: object, left: BinaryTree, right: BinaryTree) -> BinaryTree:
    """Return a tree with the given root and subtrees."""
    testtree = BinaryTree()
    testtree.root = item
    testtree.left = left
    testtree.right = right
    return testtree

EMPTY = BinaryTree()
C = join('C',EMPTY,EMPTY)
D = join('D',EMPTY,EMPTY)
E = join('E',EMPTY,EMPTY)
F = join('F',EMPTY,EMPTY)
A = join('A',C,D)
B = join('B',E,F)
BINARY = join('START',B,A)

I visualise it as follows我将其可视化如下

Visualisation of the Binary tree二叉树的可视化

Now I'm trying to create a function that will take two inputs, a BinaryTree and a single character and the output will be the binary code for the corresponding letter (as an example, D = " 10 ").现在我正在尝试创建一个 function,它将接受两个输入,一个二叉树和一个字符,而 output 将是相应字母的二进制代码(例如,D =“10”)。 I'm outputting as a string rather than an integer. My function and test case as follows我输出为字符串而不是 integer。我的 function 和测试用例如下

# global variable
result = ''

#Convert binary to letter
def convert_letter(testtree: BinaryTree, letter: str) -> str:
    global result
    if testtree == None:
        return False
    elif testtree.root == letter:
        return True        
    else:  
        if convert_letter(testtree.left, letter) == True:
            result += "1"
            return result
        elif convert_letter(testtree.right, letter) == True:
            result += "0"
            return result
     
#Test
test = 'D' #Return '10'
convert_letter(BINARY, test)

And unfortunately that's where I'm hitting a brick wall.不幸的是,这就是我碰壁的地方。 I had tried initialising an empty string within the function, but everytime it iterates over the function it overwrites the string.我曾尝试在 function 中初始化一个空字符串,但每次迭代 function 时都会覆盖该字符串。 Any help greatly appreciated.非常感谢任何帮助。

I took the liberty of simplfying your code a bit let me know if you have any questions about how this works.我冒昧地简化了您的代码,如果您对它的工作原理有任何疑问,请告诉我。

class node:
    """A rooted binary tree"""
    def __init__(self, value = None, left = None, right = None):
        self.value = value
        self.left = left
        self.right = right

C = node('C')
D = node('D')
E = node('E')
F = node('F')
A = node('A',C,D)
B = node('B',E,F)
BINARY = node('START',B,A)

def convert_letter(n,letter):
    if n.value == letter:
        return "1"+(convert_letter(n.left,letter) if not n.left is None else "")+(convert_letter(n.right,letter)if not n.right is None else "")
    else:
        return "0"+(convert_letter(n.left,letter) if not n.left is None else "")+(convert_letter(n.right,letter)if not n.right is None else "")

def walk(n):
    return n.value+(walk(n.left) if not n.left is None else "")+(walk(n.right) if not n.right is None else "")

test = 'D'
print(convert_letter(BINARY, test))
print(walk(BINARY))

This is not how I would personally structure an answer, but I think it most closely follows what you are attempting.这不是我个人构建答案的方式,但我认为它最接近您的尝试。 The shortcoming of your answer only being that you are only returning one value, but kind of tracking two values.你的答案的缺点只是你只返回一个值,但有点跟踪两个值。 Note, I have taken the liberty of correcting:请注意,我冒昧地更正了:

BINARY = join('START',A,B)

Let's modify your method to return both a Boolean indicating if the letter was found as well as the indicator of the path.让我们修改您的方法以返回 Boolean 指示是否找到字母以及路径指示符。

def convert_letter2(testtree: BinaryTree, letter: str):
    if not testtree:
        return (False, "")

    if testtree.root == letter:
        return (True, "")

    test, val = convert_letter2(testtree.left, letter)
    if test:
        return (True, "1" + val)

    test, val = convert_letter2(testtree.right, letter)
    if test:
        return (True, "0" + val)

    return (False, "")

Then if we:那么如果我们:

print(convert_letter2(BINARY, "D")[1])

We should get back "10"我们应该找回"10"

The problem is that your function will sometimes return a boolean, sometimes a string, and sometimes None .问题是您的 function 有时会返回 boolean,有时是字符串,有时是None So with this code:所以用这段代码:

    if convert_letter(testtree.left, letter) == True:
        result += "1"
        return result
    elif convert_letter(testtree.right, letter) == True:
        result += "0"
        return result

... you are not capturing all successful searches, as a successful search would return the actual string of "0" and "1" which obviously is not True . ...您没有捕获所有成功的搜索,因为成功的搜索将返回 "0" 和 "1" 的实际字符串,这显然不是True In that case the execution has no else to go to and returns None -- even when the letter was found in a deeper node.在那种情况下,执行没有else到 go 并返回None - 即使在更深的节点中找到该字母。

Your function should not return a boolean -- that doesn't match the type hint either.您的 function 不应返回 boolean - 也不匹配类型提示。 It should be a string (the result).它应该是一个字符串(结果)。 You could reserve None to indicate the letter was not found.您可以保留None以指示未找到该字母。

Some other problems:其他一些问题:

  • result += "0" will append the digit, but since you already made the recursive call, you need to prepend the digit -- as you are higher up in the tree now. result += "0"将为append数字,但由于您已经进行了递归调用,因此您需要在数字前面加上- 因为您现在在树中更高。

  • The initialisation of your tree makes a different tree than you put in the image: A should be the left child, not the right child.树的初始化与您在图像中放置的树不同: A应该是左孩子,而不是右孩子。 So it should be join('START', A, B)所以它应该是join('START', A, B)

With those fixes, you'd have this code:通过这些修复,您将获得以下代码:

def convert_letter(testtree: BinaryTree, letter: str) -> str:
    global result
    if testtree is None:
        result = None  # Not found here 
    elif testtree.root == letter:
        result = ''  # Found! Start a path
    elif convert_letter(testtree.left, letter) is not None:
        result = "1" + result  # Prepend
    elif convert_letter(testtree.right, letter) is not None:
        result = "0" + result  # Prepend
    else:
        result = None  # Not found here
    return result

If you also correct to use join('START', A, B) , then the output will be 10 .如果您还正确使用join('START', A, B) ,则 output 将为10

Better Practice更好的实践

There are some things you can do better:有些事情你可以做得更好:

  • Don't use a global variable for storing the function result.不要使用全局变量来存储 function 结果。 As you return it, you can capture the result you get from a recursive call as a local variable, prepend to it, and return it again.当您返回它时,您可以捕获从递归调用中获得的result作为局部变量,添加到它之前,然后再次返回它。

  • The definition of EMPTY makes your tree unnecessarily big. EMPTY的定义使你的树变得不必要的大。 Just use None to denote an empty tree.只需使用None来表示一棵空树。

  • Don't call a node's value root .不要调用节点的值root A rooted tree has only one root, and it is a node, not a value of a node.有根树只有一个根,它是一个节点,而不是节点的值。 So call that attribute value or data , but not root .因此调用该属性valuedata ,而不是root

  • The join function is nice, but why not use the constructor for that feature? join function 很好,但为什么不使用该功能的构造函数呢? The constructor can take those arguments as optional and immediately initialise the left and right attributes with those arguments.构造函数可以将那些 arguments 作为可选值,并立即right left

  • The code-comment above the convert_letter function describes the opposite from what the function does. convert_letter function 上方的代码注释与 function 的作用相反。

Taking all that into account, your code could look like this:考虑到所有这些,您的代码可能如下所示:

class BinaryTree:
    def __init__(self, value, left: 'BinaryTree'=None, right: 'BinaryTree'=None):
        self.value = value
        self.left = left
        self.right = right

def convert_letter(tree: BinaryTree, letter: str) -> str:
    if not tree:
        return # Not found here, return None
    if tree.value == letter:
        return ""  # Bingo: return an empty path
    # No more global. path is a local variable
    path = convert_letter(tree.left, letter)
    if path is not None:
        return "1" + path
    path = convert_letter(tree.right, letter)
    if path is not None:
        return "0" + path

# Look how nice it is to create a tree using the constructor arguments
binary = BinaryTree("Start",
    BinaryTree("A",
        BinaryTree("C"), BinaryTree("D")
    ),
    BinaryTree("B",
        BinaryTree("E"), BinaryTree("F")
    )
)

# Test
test = 'D'
print(convert_letter(binary, test))  # 10

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM