Python数据结构与算法分析(第二版)学习笔记——树

16 阅读4分钟

树从顶部开始,由椭圆和箭头构成,并延伸到底部。树一个节点的所有子节点与其他节点的子节点无关。同时每一个叶子节点都是独一无二的。文件管理系统类似于一个树,html标签也类似于树。

image.png

image.png

树有一个根节点。除此以外,每个节点都与其唯一的父节点相连,从根节点到节点都有且仅有一条路径。若元素最多有两个子节点,称其为二叉树。

image.png

列表方式实现二叉树:

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,newroot):
    root[0] = newroot
def getrootlefthild(root):
    return root[1]
def getrootrighthild(root):
    return root[2]

节点与引用表示法:

# 节点与引用创建树

class binarytree:
    def __init__(self,rootobj):
        self.key = rootobj
        self.leftchild = None
        self.rightchild = None

    def insertleft(self,newnode):
        if len(self.leftchild) == None:
            self.leftchild = binarytree(newnode)
        else:
            t = binarytree(newnode)
            t.leftchild = self.leftchild
            self.leftchild = t

    def insertright(self,newnode):
        if len(self.rightchild) == None:
            self.rightchild = binarytree(newnode)
        else:
            t = binarytree(newnode)
            t.leftchild = self.rightchild
            self.rightchild = t

    def getleft(self):4
        return self.leftchild

    def getright(self):
        return self.rightchild

    def setrootval(self,obj):
        self.key = obj

    def getrootval(self):
        return self.key

解析树

解析树可以用来对句子或某个表达式进行构造,以下是一个数学表达式的解析树。

image.png

解析树可以用来拆解完全括号表达式,在以下代码中,首先创建一个空树,碰上(就添加一个左节点,并来到子节点,碰上数字就给子节点值,而后回到父节点,碰上逻辑运算就添加给父节点,然后创建一个右节点并来到右节点,碰上数字就给右节点赋值并回到父节点,碰上)就返回父节点。为了追踪父节点,可以在前往子节点时将父节点,返回父节点时再取出父节点的值。

from pythonds.basic import Stack
from pythonds.trees import BinaryTree

def buildparsetree(fpexp):
    s = Stack()
    fplist = fpexp.split()

    etree = BinaryTree('')
    s.push(etree)
    currenttree = etree

    for i in fplist:
        if i == "(":
            currenttree.insertLeft("")
            s.push(currenttree)
            currenttree = currenttree.getLeftChild()

        elif i in "0123456789":
            currenttree.setRootVal(eval(i))
            currenttree = s.pop()
        elif i in "+-*/":
            currenttree.setRootVal(i)
            currenttree.insertRight("")
            currenttree = currenttree.getRightChild()
        elif i == ")":
            currenttree = s.pop()
        else:
            raise ValueError("错误")

    return etree

计算二叉解析树的递归函数。利用递归的方法,如果子节点存在就继续元算,否则就返回当前节点的值。function是四则运算的调用方法。

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()

树的遍历分为:前序遍历、中序遍历、后序遍历。前序遍历先访问根节点再访问左树,最后访问右树。中序遍历,先访问左子树,再访问根节点,最后访问右节点。后序遍历先访问左子树、再访问右子树,最后访问根节点。

# 前序遍历算法实现为外部函数

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


    # 前序遍历算法实现为二叉树类
def preord(self):
    print(self.key)
    if self.leftChild:
        self.left.preord()
    if self.rightChild:
        self.right.preord()
# 后续遍历函数
def postorder(tree):
    if tree != None:

        preorder(tree.getLeftChild())
        preorder(tree.getRightChild())
        print(tree.getRootValue())
# 后序求值算法
def postorderevaluate(tree):
    opers = {"+":operator.add,"-":operator.sub,"*":operator.mul,"/":operator.truediv}

    res1 = None
    res2 = None

    if tree:
        res1 = postorderevaluate(tree.getLeftChild())
        res2 = postorderevaluate(tree.getRightChild)
        if res1 and res2:
            return opers[tree.getRootValue](res1,res2)
        else:
            return tree.getrootval()

# 中序便利函数
def inorder(tree):
    if tree != None:

        preorder(tree.getLeftChild())
        print(tree.getRootValue())
        preorder(tree.getRightChild())

def printexp(tree):
    sval = " "
    if tree:
        sval = "(" + printexp(tree.getLeftChild())
        sval = sval + str(tree.getrootval())
        sval = printexp(tree.getRightChild()) + ")"
    return sval

二叉堆的实现,二叉堆用于实现优先级队列,其实现时用一个列表作为表示。其变化有最小堆(最小堆在队首)和最大堆(最大元素在队首),它需要实现队的平衡,因此采用一个完全二叉树来实现平衡。在完全二叉树中,除了最底层,每一层节点都是满的。对于位置p节点来说,其左子节点为2p,右子节点为2p+1。在加入一个新元素的时候,要向上查找,如果父元素比他大就要向上交换。在移除最小元素的过程中,要将最末尾元素移动到首位,再向下比较。

class currentsize:
    def __init__(self):
        self.heaplist = [0]
        self.currentsize = 0

    def percup(self,i):
        while i // 2 > 0 :
            if self.heaplist[i // 2] > self.heaplist[i]:
                temp = self.heaplist[i]
                self.heaplist[i // 2] = self.heaplist[i]
                self.heaplist[i] = temp
            i = i // 2

    def insert(self,x):
        self.heaplist.append(x)
        self.currentsize = self.currentsize +1
        self.percup(self.currentsize)
        
    def percdown(self,i):
        while i * 2 <= self.currentsize:
            mc = self.minchild(i)
            if self.heaplist[i] > self.heaplist[mc]:
                temp = self.heaplist[i]
                self.heaplist[i] = self.heaplist[mc]
                self.heaplist[mc] = temp
            i = mc


    def minchild(self,i):
        if i * 2 + 1 > self.currentsize:
            return i * 2
        else:
            if self.heaplist[i * 2] <self.heaplist[i * 2 +1]:
                return i * 2
            else:
                return i * 2 + 1

    def delmin(self):
        retval = self.heaplist[1]
        self.heaplist[1] = self.heaplist[self.currentsize]
        self.currentsize = self.currentsize - 1
        self.heaplist.pop()
        self.percdown(1)
        return retval
    def buildheap(self,alist):
        i = len(alist) // 2
        self.currentsize = len(alist)
        self.heaplist = [0] + alist[:]
        while (i>0):
            self.percdown(i)
            i -= 1