This article is a detailed example of how Python implements the resolution tree and binary tree traversal, including first-order traversal, middle-order traversal, and then-order traversal, if you have any need, refer. This article is a detailed example of how Python implements the resolution tree and binary tree traversal, including first-order traversal, middle-order traversal, and then-order traversal, if you have any need, refer.
Resolution tree
After implementing the tree, let's look at an example to show you how to use the tree to solve some practical problems. In this chapter, we will study the resolution tree. The parsing tree is often used to represent the structure of the real world, such as sentences or mathematical expressions.
from pythonds.basic.stack import Stackfrom pythonds.trees.binaryTree import BinaryTreedef buildParseTree(fpexp): fplist = fpexp.split() pStack = Stack() eTree = BinaryTree('') pStack.push(eTree) currentTree = eTree for i in fplist: if i == '(': currentTree.insertLeft('') pStack.push(currentTree) currentTree = currentTree.getLeftChild() elif i not in ['+', '-', '*', '/', ')']: currentTree.setRootVal(int(i)) parent = pStack.pop() currentTree = parent elif i in ['+', '-', '*', '/']: currentTree.setRootVal(i) currentTree.insertRight('') pStack.push(currentTree) currentTree = currentTree.getRightChild() elif i == ')': currentTree = pStack.pop() else: raise ValueError return eTreept = buildParseTree("( ( 10 + 5 ) * 3 )")pt.postorder() #defined and explained in the next section
These four rules for creating a resolution tree are embodied in fourif
Clause. they are in rows 11,15, 19,24. As mentioned above, you can see the code implementation of the rule in these places, and you need to call someBinaryTree
AndStack
. The only error check in this function iselse
Statement, once the characters we read from the list are unrecognizable, we will reportValueError
. Now we have built a resolution tree. what can we do with it? In the first example, we write a function to calculate the value of the resolution tree and return the calculated numerical result. To implement this function, we need to use the hierarchy of the tree. Let's repeat step 2 and think about how we can replace the original tree with the simplified tree (figure 3 ). This prompts us to write a value that calculates the value of each subtree recursively to calculate the value of the entire resolution tree.
Just as we used to implement recursive algorithms, we will design functions for recursive calculation of expression values from the basis point. The natural basis of this recursive algorithm is to check whether the operator is a leaf node. In the resolution tree, leaf nodes are always operands. Because numeric variables such as integers and floating-point numbers do not require more operations, this evaluate function only needs to simply return the numbers stored in the leaf node. The recursive process of moving a function to the base point is to call the evaluate function to calculate the values of the left and right subtree of the current node. Recursive calls make us move toward the leaf node and drop down along the tree.
To integrate the values of two recursive calls, we only need to simply apply the operators in the parent node to the results returned by the two subnodes. In Figure 3, we can see the values of the two subnodes: 10 and 3. Use the multiplication operation on them to get the final result 30.
The code of the recursive evaluate function is shown in Listing1. the parameters of the left and right subnodes of the current node are obtained. If the values of the left and right subnodes are None, we can know that the current node is a leaf node. This check is performed on 7th rows. If the current node is not a leaf node, search for the operator of the current node and use it to return values of left and right children.
To implement this algorithm, we use a dictionary with the key values'+','-','*'
And'/'
. The value in the dictionary is a function in the Python operand module. This operand module provides many operators for common functions. When we look for an operator in the dictionary, the corresponding operand variable is retrieved. Since it is a function, we can call the function to calculate the formula, as shown in figurefunction(param1,param2)
. So findopers['+'](2,2)
It is equivalentoperator.add(2,2)
.
Listing 1
def evaluate(parseTree): opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truep} leftC = parseTree.getLeftChild() rightC = parseTree.getRightChild() if leftC and rightC: fn = opers[parseTree.getRootVal()] return fn(evaluate(leftC),evaluate(rightC)) else: return parseTree.getRootVal()
Finally, we will traverse and evaluate the value in the resolution tree created in figure 4. When we call the evaluate function for the first time, we pass the resolution tree parameterparseTree
As the root of the entire tree. Then we get the reference of left and right subtree to make sure they exist. Recursive calls are performed on 9th rows. We start by viewing the operators in the root of the tree. this is'+'
. This'+'
Operator foundoperator.add
Function call with two parameters. Generally, for a Python function call, the first thing Python does is calculate the parameter value passed to the function. Through the left-to-right evaluation process, the first recursive call starts from the left. In the first recursive call, The evaluate function is used to calculate the left subtree. We found that this node has no left or right subtree, so we are on a leaf node. When we are on a leaf node, we only return the value stored on this leaf node as the result of the evaluate function. Therefore, we return an integer of 3.
Now, for top-level callsoperator.add
Function. We have calculated one of the parameters, but we have not finished it yet. The parameters are calculated from left to right. now, the evaluate function is called recursively to calculate the right subnode of the root node. We found that this node has both left and right nodes, so we can find the operators stored in this node, which is'*'
Then, call this operand function and use its left and right subnodes as two parameters of the function. Then, call the function on the two nodes, and find that the left and right subnodes of the function are leaves, and return two integers 4 and 5 respectively. After the two parameter values are obtained, we returnoperator.mul(4,5)
. Now, we have calculated the top-level operators.'+'
And all you need to do is to call the function.operator.add(3,20)
You can. The result is the value of the entire expression tree (3 + (4*5), which is 23.
Tree traversal
We have learned about the basic functions of the tree before. now let's look at some application modes. Nodes can be accessed in three modes. These three methods are often used to access tree nodes. The difference between them is that the order of access to each node is different. This access to all nodes is called traversal (traversal
). These three types of traversal are called first-order traversal (preorder
(inorder
) And backward traversal (postorder
). Let's give them a detailed definition, and then take an example to see their application.
First-order traversal
In first-order traversal, we first access the root node, then recursively use first-order traversal to access the left subtree, and then recursively use first-order traversal to access the right subtree.
Sequential traversal
In the middle-order traversal, We recursively use the middle-order traversal to access the left subtree, then access the root node, and then recursively use the middle-order traversal to access the right subtree.
Post-order traversal
In the post-order traversal, We recursively use the post-order traversal to access the left and right subtree, and finally access the root node.
Now we use several examples to illustrate these three different traversal methods. First, let's look at the first sequential traversal. We use a tree to represent a book and look at the method of first-order traversal. The book is the root node of the tree. Each chapter is the child node of the root node, each section is the child node of the chapter, each section is the child node of each chapter, and so on. Figure 5 shows a book that consists of only two chapters. Although the traversal algorithm applies to the tree structure containing any number of sub-trees, we only talk about binary trees so far.
def preorder(tree): if tree: print(tree.getRootVal()) preorder(tree.getLeftChild()) preorder(tree.getRightChild())
We can also use sequential traversalBinaryTree
Class, as shown in Listing 3. Pay attention to the changes caused by this Code moving from the external to the internal. In general, we justtree
Changedself
. But we also need to modify the base point of the code. The built-in method must check whether the left and right subtree exists before performing recursive first-order traversal.
Listing 3
def preorder(self): print(self.key) if self.leftChild: self.leftChild.preorder() if self.rightChild: self.rightChild.preorder()
Which of the following is a better built-in and external method? Generallypreorder
As an external method, it is better because we seldom traverse for traversal. in this process, we always need to do something else. In fact, we will immediately see that the algorithm for post-sequential traversal is very similar to the code for evaluating the expression tree we wrote earlier. However, we will write the traversal code in the form of external functions. The code for subsequent traversal is shown in Listing 4.print
The statement is almost the same as the code for first-order traversal.
Listing 4
def postorder(tree): if tree != None: postorder(tree.getLeftChild()) postorder(tree.getRightChild()) print(tree.getRootVal())
We have seen general applications of post-sequential traversal, that is, evaluate values through the expression tree. Let's take a look at Listing 1. First, calculate the value of the left subtree, then calculate the value of the right subtree, and then connect them together using the operation of the root node. Assume that our binary tree only stores the data of the expression tree. Let's rewrite the evaluate function and try to imitate the code of post-sequential traversal, as shown in Listing 5.
Listing 5
def postordereval(tree): opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truep} res1 = None res2 = None if tree: res1 = postordereval(tree.getLeftChild()) res2 = postordereval(tree.getRightChild()) if res1 and res2: return opers[tree.getRootVal()](res1,res2) else: return tree.getRootVal()
We found that the Listing 5 format is the same as Listing 4. The difference is that we output the key value in Listing 4 and return the key value in Listing 5. This allows us to store the values obtained recursively by rows 6th and 7th. Then we use these stored values and the 9th-row operator to calculate them together.
At the end of this section, let's take a look at the ordinal traversal. In the middle-order traversal, we first access the left subtree, followed by the root node, and finally access the right subtree. Listing 6 provides the code for the middle-order traversal. We found that the code of the three traversal functions only changes the position of the output statement without modifying the recursive statement.
Listing 6
def inorder(tree): if tree != None: inorder(tree.getLeftChild()) print(tree.getRootVal()) inorder(tree.getRightChild())
When we perform a sequential traversal on an parsing tree, we get the original form of the expression without any parentheses. We try to modify the algorithm of the ordinal traversal so that we can get the parentheses expression. Make the following changes: output the left brackets before recursive access to the left subtree, and then output the right parenthesis after access to the right subtree. For the modified code, see Listing 7.
Listing 7
def printexp(tree): sVal = "" if tree: sVal = '(' + printexp(tree.getLeftChild()) sVal = sVal + str(tree.getRootVal()) sVal = sVal + printexp(tree.getRightChild())+')' return sVal
We found thatprintexp
The function also adds parentheses for each number, which obviously do not need to be added.
The above is the detailed description of the Python parsing tree and tree traversal. For more information, see other related articles in the first PHP community!