python数据结构——树

一、树的定义及相关概念

树的例子:
生物学的分类树、文件系统、网页

一个节点的所有子节点独立于另一个节点的子节点。
每个叶节点是唯一的。

树的概念
节点、边、根、路径、子节点、父节点、兄弟节点、子树
层数:节点 n 的层数为从根结点到该结点所经过的分支数目。 根节点的层数为零。
高度:树的高度等于树中任何节点的最大层数。

树的定义:
树由一组节点和一组连接节点的边组成。树具有以下属性:
树的一个节点被指定为根节点。
除了根节点之外,每个节点 n 通过一个其他节点 p 的边连接,其中 p 是 n 的父节点。
从根路径遍历到每个节点路径唯一。
如果树中的每个节点最多有两个子节点,我们说该树是一个二叉树。

定义二(递归定义):树是空的,或者由一个根节点和零个或多个子树组成,每个子树也是一棵树。每个子树的根节点通过边连接到父树的根节点。

二、树的表示(python)

1.列表表示

在列表树的列表中,我们将根节点的值存储为列表的第一个元素。列表的第二个元素本身将是一个表示左子树的列表。列表的第三个元素将是表示右子树的另一个列表。

myTree = ['a',   #root
      ['b',  #left subtree
       ['d', [], []],
       ['e', [], []] ],
      ['c',  #right subtree
       ['f', [], []],
       [] ]
     ]

列表方法的另一个很好的特性是它可以推广到一个有许多子树的树。在树超过二叉树的情况下,另一个子树只是另一个列表。

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]

r = BinaryTree(3)
insertLeft(r,4)
insertLeft(r,5)
insertRight(r,6)
insertRight(r,7)
l = getLeftChild(r)
print(l)

setRootVal(l,9)
print(r)
insertLeft(l,11)
print(r)
print(getRightChild(getRightChild(r)))

2.节点和引用表示

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.rightChild = self.rightChild
            self.rightChild = t

    def getRightChild(self):
        return self.rightChild

    def getLeftChild(self):
        return self.leftChild

    def setRootVal(self,obj):
        self.key = obj
        return self.key

    def getRootVal(self):
        return self.key


r = BinaryTree('a')
print(r.getRootVal())        # a
print(r.getLeftChild())      # None
r.insertLeft('b')
print(r.getLeftChild())      # <__main__.BinaryTree object at 0x00000259D3519DA0>
print(r.getLeftChild().getRootVal())          # b
r.insertRight('c')
print(r.getRightChild())     # <__main__.BinaryTree object at 0x00000259D3519D30>
print(r.getRightChild().getRootVal())         # c
r.getRightChild().setRootVal('hello')
print(r.getRightChild().getRootVal())         # hello
print(r.getRightChild())     # <__main__.BinaryTree object at 0x00000259D3519D30>

三、遍历二叉树

1.解析树

解析树常常用于真实世界的结构表示,例如句子或数学表达式。
在分析树中,叶节点将始终是操作数。
定义四条规则:

  1. 如果当前读入的字符是’(’,添加一个新的节点作为当前节点的左子节点,并下降到左子节点处。
  2. 如果当前读入的字符在列表[’+’, ‘-’, ‘/’, ‘*’]中,将当前节点的根值设置为当前读入的字符。添加一个新的节点作为当前节点的右子节点,并下降到右子节点处。
  3. 如果当前读入的字符是一个数字,将当前节点的根值设置为该数字,并返回到它的父节点。
  4. 如果当前读入的字符是’)’,返回当前节点的父节点。

当我们想要下降到当前节点的子节点时,我们先将当前节点压入栈。当我们想要返回当前节点的父节点时,我们从栈中弹出该父节点。

from pythonds.basic.stack import Stack
from pythonds.trees.binaryTree import BinaryTree
import operator
# from stack import Stack
# from tree_node import BinaryTree

def 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 eTree

pt = buildParseTree("( ( 10 + 5 ) * 3 )")

pt.postorder()  #defined and explained in the next section


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()]  # mul

        return fn(evaluate(leftC), evaluate(rightC))
    else:
        return parseTree.getRootVal()


result = evaluate(pt)
print(result)

2.树的遍历

二叉树是一种非线性结构,遍历(traversal)二叉树无法通过简单的循环实现。遍历二叉树就是要让树中所有节点被且仅被访问一次,即按一定规律排列成一个线性队列。二叉树包含3个部分,即根节点、左子树和右子树。根据3个部分的访问次序对二叉树的遍历进行分类,可以将遍历二叉树的方法分为3种类型,分别为先序遍历(preorder),中序遍历(inorder)和后序遍历(postorder)。(先序后序是针对根节点来说)
先序遍历
在先序遍历中,我们先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树。
外置方法:

def preorder(tree):
    if tree:
        print(tree.getRootVal())
        preorder(tree.getLeftChild())
        preorder(tree.getRightChild())

内置方法

def preorder(self):
    print(self.key)
    if self.leftChild:
        self.leftChild.preorder()
    if self.rightChild:
        self.rightChild.preorder()

一般来说preorder作为一个外置方法比较好,原因是,我们很少是单纯地为了遍历而遍历,这个过程中总是要做点其他事情。

中序遍历
在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树。

def inorder(tree):
  if tree != None:
      inorder(tree.getLeftChild())
      print(tree.getRootVal())
      inorder(tree.getRightChild())

当我们对一个解析树作中序遍历时,得到表达式的原来形式,没有任何括号。我们尝试修改中序遍历的算法使我们得到全括号表达式。只要做如下修改:在递归访问左子树之前输出左括号,然后在访问右子树之后输出右括号。

def printexp(tree):
  sVal = ""
  if tree:
      sVal = '(' + printexp(tree.getLeftChild())
      sVal = sVal + str(tree.getRootVal())
      sVal = sVal + printexp(tree.getRightChild())+')'
  return sVal

后序遍历
在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点。

def postorder(tree):
    if tree != None:
        postorder(tree.getLeftChild())
        postorder(tree.getRightChild())
        print(tree.getRootVal())

参考:Python数据结构
https://facert.gitbooks.io/python-data-structure-cn/

你可能感兴趣的:(python,数据结构)