美文网首页
COMP9021 Principles of Programmi

COMP9021 Principles of Programmi

作者: Sisyphus235 | 来源:发表于2017-10-06 09:13 被阅读0次

本文的主题是Tree,是算法中应用最广的方法之一。

1. binary tree二叉树

1.1 基本结构

Tree从一个root开始,延伸出枝条,术语通常叫做children。对于binary tree来说每一个node的children有两个。为了保持树状结果的一致性,考虑边界条件——root没有value的tree,设置这样的tree也有两个枝条,两个枝条都是值为None的树状结构。
tree class基本构成如下:

class tree:
    def __init__(self, value = None):
        self.value = value
        #value是每一个节点的值,如果只有一个节点,则这个节点是root
        self.left = None
        self.right = None
        #默认任何tree都有两个children,初始值为None

但是这样的class定义只满足边界条件,还需要增加tree的生长,更新如下:

class tree:
    def __init__(self, value = None):
        self.value = value
        if self.value is None:
            self.left = None
            self.right = None
        else:
            self.left = Tree()
            self.right = Tree()

1.2 应用--expression tree

之前的文章提到过计算机如何实现数学的混合运算,使用的是stack的方法,其核心是如何区分运算的优先级。除了stack前缀后缀的实现方法外,本文的tree也可以实现,因为tree也具有识别优先级的能力,任何一个node和两个children关系最紧密。
例如:3 * (2 + 4),可以用树状图表示为:

   *
3    +
    2  4

使用上文提到的tree class来实现:

def evaluate(expression_tree):
    if expression_tree.value is None:
        return
    if isinstance(expression_tree.value, int):
        return expression_tree.value
    if expression_tree.value == '+':
        return evaluate(expression_tree.left) + evaluate(expression_tree.right)
    if expression_tree.value == '*':
        return evaluate(expression_tree.left) * evaluate(expression_tree.right)
#recursion的思想是很多算法的base,这里的base condition是value为None和value为一个int,recursion condition是value为运算符

1.3 Tree height

树的高度是解决很多复杂问题的基础,实现方法如下(与之前文章约定一样,为了避免冗余,上文实现的class function不再重复,直接继承):

class tree:
    def height(self):
        if self.value is None:
            return 0
        #边界条件,如果一个树没有value,默认高度是0
        return max(self.left._height(), self.right._height())
        #非边界条件时,高度等于左右两个分支高度的最大值
    def _height(self):
        if self.value is None:
            return 0
        return 1 + max(self.left._height(), self.right._height())
        #还是使用recursion来解决问题

看起来在求解树高度的两个函数中,大量代码重复,有冗余的嫌疑,但实际上表达的含义截然不同。
在height的函数中处理的是tree为空和非空两种情况的问题,而_height函数中同样的判断代码却是recursion的base condition

1.4 Tree print

为了后续实现复杂功能做铺垫,先要实现数的可视化。之前简单的问题可以在脑中推导实现方案,但是复杂的问题要debug就一定要先能展现每一步的运行功能。
为了美观,每一个节点的两个children应该占据相同的排版空间。

class Tree:
    def print_tree(self):
        self._print_tree(0, self.height())
        #tree的print排版设计显然要与tree height相关,所以传参进入_print_tree实现具体print
    def _print_tree(self, depth, height):
        if self.value is not None:
            self.left._print_tree(depth + 1, height)
            print('    ' * depth,  self.value, sep ='')
            self.right._print_tree(depth + 1, height)
        else:
            print('\n' * (2 ** (height - depth + 1) - 1))

T = Tree(1)
T1 = Tree(0)
T2 = Tree(2)
T3 = Tree(3)
T4 = Tree(4)
T5 = Tree(5)
T6 = Tree(6)
T.left = T1
T.right = T2
T2.left = T3
T2.right = T4
T1.left = T5
T1.right = T6
T.print_tree()
>>>
        5

    0

        6

1

        3

    2

        4

为什么要用水平生长方式输出tree,而不是最容易被人接受的竖直生长方式?
水平生长的方式每一行都只有一个value或者是空行,可以容易的用recursion和print控制输出;而竖直生长的方式,每一行有多个value,不容易用recursion方式直接print出来。

1.5 Binary Search Tree(BST)

binary search tree的目的是为了提高检索效率,基本思路是输入一组乱序数字,第一个数字放在root中,第二个数字如果比第一个数字小则放在left child中,反之。
例如:一组数3,6,4,1,9,2,5

        3
   1          6
      2    4     9
             5

判断是否是BST的实现方法如下:

class Tree:
    def is_bst(self):
        if self.value is None:
            return True
        if self.left.value is not None:
            tree_on_left = self.left
            while tree_on_left.right.value is not None:
                tree_on_left = tree_on_left.right
            #找到左侧分支最右侧的点的值
            if self.value <= tree_on_left.value:
                return False
        if self.right.value is not None:
            tree_on_right = self.right
            while tree_on_right.left.value is not None:
                tree_on_right = tree_on_right.left
            #找到右侧分支最左侧的点的值
            if self.value >= tree_on_right.value:
                return False
        return self.left.is_bst() and self.right.is_bst()

可以判断BST后,下面的问题就是如何形成BST:

class Tree:
    def insert_in_bst(self, value):
        if self.value is None:
            self.value = value
            self.left = Tree()
            self.right = Tree()
            return True
            #如果BST为空,那么生成一个空tree,返回生成成功
        elif self.value is value:
            return False
            #如果插入的value是当前的value,则不需要插入值,返回失败
        if self.value > value:
            return self.left.insert_in_bst(value)
        return self.right.isnert_in_bst(value)
        #前面base condition设置完成后,这里写的是recursion condition,如果插入值小于当前value,则在左侧recursion,反之

1.6 BST delete

相对而言BST delete是Tree中最复杂的功能,基本思路是:
(1)如果删除的节点是leaf,则直接删除;
(2)如果该节点只有一个child,则把child的后续tree挂到当前child上;
(3)如果删除的节点有两个children,这是最复杂的情况。分析如下:要找一个节点来替代被删除的节点,要求这个替代的节点value比所有右侧的节点value都小,比所有左侧的节点value都大。马上联想到任意数组生成BST的过程,符合这样要求的节点是左侧分支最右下节点的value,所以用这个节点替换被删除的节点。然后处理左侧分支最右下节点左侧child的问题,它符合上面说的(2),把左侧的child直接挂在parent的位置即可。

相关文章

网友评论

      本文标题:COMP9021 Principles of Programmi

      本文链接:https://www.haomeiwen.com/subject/hollyxtx.html