树的构成要素:

节点(Node) 边(Edge) 根节点(Root) 路径(Path) 子节点集(Children) 父节点(Parent) 兄弟节点(Sibling)

子树 (Subtree) 叶节点(Leaf Node) 层数(Level) 高度(height)

定义一:树有以下特征:

  • 有一个节点是根节点
  • 除了根节点外的每一个节点n,都通过一条边与另一个节点p相连,p是n的父节点
  • 可以沿着唯一的路径从根节点到达每个节点
  • 如果这个树每个节点都至多有两个子节点,我们称它为二叉树

定义二:

每个树或者为空或者包含一个根节点和零个或多个子树,其中每个子树也符合这样的定义 

通过嵌套列表来实现树

def BinaryTree(r):
    return [r,[],[]]  #初始化树
def insertLeft(root,newBranch):
    t = root.pop(1) 将左子节点拿出
    if len(t)>1:
        root.insert(1,[newBranch,t,[]])
                 插入位置     插入元素
    else:
        root.insert(1,[newBranch,[],[]])
    return root
def insertRight(root,newBranch):
    t = root.pop(2)
    if len(t)>1:
        root.insert(2,[newBranch,[],t])
    else:
        root.insert(2,[newBranch,[],[]])
    return root
def getRootVal(root):
    return root[0]
def setRootVal(root,newVal):
    root[0] = newVal
def getLeftChild(root):
    return root[1]
def getRightChild(root):
    return root[2]

 第二种实现树的方式 使用节点和引用 利用对象的方式

python 节奏制作 python节点_子节点

class BinaryTree:
    def __init__(self,rootObj):
        self.key = rootObj
        self.leftChild = None
        self.rightChild = None
    def insertLeft(self,newNode):
        if self.leftChild ==None:
            self.leftChild = BinaryTree(newNode)
        else:
            t = BinaryTree(newNode)
            t.leftChild = self.leftChild
            self.leftChild=t
    def insertRight(self,newNode):
        if self.rightChild == None:
            self.rightChild = BinaryTree(newNode)
        else:
            t = BinaryTree(newNode)
            t.leftChild = self.leftChild
            self.leftChild = t
    def getRightChild(self):
        return self.rightChild
    def getLeftChild(self):
        return self.leftChild
    def setRootVal(self,obj):
        self.key = obj
    def getRootVal(self):
        return self.key

在插入时我们必须考虑两种情况,第一种情况:当前没有现有左子节点,当没有左子节点时,简单的将新节点添加到树中即可

有左子节点时 我们插入节点时 需要把原有的左子节点进行降级 

 解析树

python 节奏制作 python节点_子节点_02

python 节奏制作 python节点_子树_03

 比如(7+3)*(5-2):如果当前读入字符是‘(’添加一个新的节点作为当前节点的子节点,当前节点下降

如果当前读入的字符在列表 ['+','-','/','*'] 中,将当前节点的根值设置为当前读入的字符。添加一个新的节点(node)作为当前节点的右子节点,当前节点下降。

如果当前读入的字符是一个数字,将当前节点的根值设置为该数字,当前节点变为它的父节点
如果当前读入的字符是 ')' ,当前节点变为其父节点(parent)。

 写一个解析树

from pythonds.basic.stack import Stack
from pythonds.trees.binaryTree import BinaryTree
def buildParseTree(parse):
    fplist = parse.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 eTree
pt  = buildParseTree("( ( 1 + 2 ) * 3 )")
pt.postorder()

 使用堆栈保持对父节点的跟踪,当我们要下降到当前节点的子节点时,我们先将当前节点压入栈中,而当我们想要返回当前节点的父节点时,我们就从堆栈中弹出该父节点 

在解析树中,叶节点总是操作数 我们只需检查一个操作符是否是叶节点,递归调用使我们有效的向叶节点移动

import operator

def evaluate(parseTree):
    opers = {'+':operator.add,'-':operator.sub,'*':operator.mul,'/':operator.truediv}

    leftc = parseTree.getLeftChild()
    rightc = parseTree.getRightChild()

    if leftc and rightc:
        fn = opers[parseTree.getRootVal()]
        return fn(evaluate(leftc),evaluate(rightc))
    else:
        return parseTree.getRootVal()
print(evaluate(pt))

 

 树的遍历

  • 前序遍历 :在前序遍历中,我们先访问根节点,然后递归地前序遍历访问左子树,再递归地前序遍历访问右子树
  • 中序遍历:先访问左子树 再根节点 最后右子树
  • 后序遍历:先访问左子树和右子树 ,最后访问根节点

 

python 节奏制作 python节点_子树_04

从头到尾阅读这本书   前序遍历正好符合这种规则

前序遍历
def preordder(tree):
    if tree:
        print(tree.getRootVal())
        preordder(tree.getLeftChild())
        preordder(tree.getRightChild())


后序遍历
def postorder(tree):
    if tree:
        postorder(tree.getLeftChild())
        postorder(tree.getRightChild())
        print(tree.getRootVal())

中序遍历
def inorder(tree):
    if tree:
        inorder(tree.getLeftChild())
        print(tree.getRootVal())
        inorder(tree.getRightChild())

 用遍历来计算解析树

def evaluate(parseTree):
    opers = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv}
    if parseTree:
        leftc = postorder(parseTree.getLeftChild())
        rightc = postorder(parseTree.getRightChild())

        if leftc and rightc:
            return opers[parseTree.getRootVal()](leftc,rightc)

        else:
            return parseTree.getRootVal()