Python之树与二叉树

树与树算法

树(tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。

树的特点:

  1. 每个结点有0个或多个子结点
  2. 没有父结点的结点称为根结点
  3. 每一个非根结点有且只有一个父结点
  4. 除了根结点外,每个子结点可以分为多个不相交的子树

树的术语: 在这里插入图片描述
结点的度:一个结点含有的子树的个数称为该结点的度。
树的度:一棵树中,最大的结点的度称为树的度。

二叉树
在这里插入图片描述
二叉树的性质

  • 二叉树中,第 i 层最多有 2i-1 个结点。

  • 如果二叉树的深度为 K,那么此二叉树最多有 2K-1 个结点。

  • 二叉树中,终端结点数(叶子结点数)为 n0,度为 2 的结点数为 n2,则 n0=n2+1。

区分满二叉树完全二叉树
如果二叉树中除了叶子结点,每个结点的度都为 2,则此二叉树称为满二叉树。
在这里插入图片描述
如果二叉树中除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布,
则此二叉树被称为完全二叉树。
在这里插入图片描述
在这里插入图片描述
树的创建:

class Node(object):
    """二叉树节点对象封装的类"""
    def __init__(self,element,lchild=None,rchild=None):
        self.element=element
        self.lchild=lchild
        self.rchild=rchild

    def __str__(self):
        return self.element

    def __repr__(self):
        return self.element


class Tree(object):
    '''二叉树的封装'''
    def __init__(self,root=None):
        self.root=root

    def add(self,item):
        """往二叉树里面添加元素"""
        node=Node(item)
        #如果是空的,就对根结点赋值。
        if self.root == None:
            self.root=node
        else:
            #先找树的根结点,储蓄在变量queue中
            queue=[]
            queue.append(self.root)
            #对已有的节点进行层次遍历
            while queue:
                item=queue.pop(0)
                if not item.lchild:
                    item.lchild=node
                    return
                if not item.rchild:
                    item.rchild=node
                    return
                else:
                    queue.append(item.lchild)
                    queue.append(item.rchild)

二叉树的遍历

遍历是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次,我们把这种对所有节点的访问称为遍历(traversal)。
在这里插入图片描述

  • 广度优先遍历(层次遍历)
    在这里插入图片描述
    def breadth_travel(self):
        """利用队列实现树的层次遍历"""
        if not self.root:
            return
        else:
            queue=[]
            queue.append(self.root)
            while queue:
                node=queue.pop(0)
                print(node.element,end=', ')
                if node.lchild:
                    queue.append(node.lchild)
                if node.rchild:
                    queue.append(node.rchild)
            print()
  • 深度优先遍历在这里插入图片描述

  • 先序遍历:每遇到一个节点,先访问,然后再遍历其左右子树(根左右)

  • 中序遍历:第一次经过时不访问根节点,等遍历完左子树之后再访问,然后遍历右子树(左根右)

  • 后续遍历:第一次和第二次经过时都不访问,等遍历完该节点的左右子树之后,最后访问该节点(左右根)

    def preorder(self, root):
        """先序遍历: 根左右"""
        if root == None:
            return
        print(root.element, end=', ')
        self.preorder(root.lchild)#递归
        self.preorder(root.rchild)

    def inorder(self, root):
        """递归实现中序遍历:左根右"""
        if root == None:
            return
        self.inorder(root.lchild)
        print(root.element, end=', ')
        self.inorder(root.rchild)

    def lastorder(self,root):
        """递归实现后续遍历:左右根"""
        if root == None:
            return
        self.lastorder(root.lchild)
        self.lastorder(root.rchild)
        print(root.element, end=', ')

完整代码:

class Node(object):
    """二叉树节点对象封装的类"""
    def __init__(self,element,lchild=None,rchild=None):
        self.element=element
        self.lchild=lchild
        self.rchild=rchild

    def __str__(self):
        return self.element

    def __repr__(self):
        return self.element


class Tree(object):
    '''二叉树的封装'''
    def __init__(self,root=None):
        self.root=root

    def add(self,item):
        """往二叉树里面添加元素"""
        node=Node(item)
        #如果是空的,就对根结点赋值。
        if self.root == None:
            self.root=node
        else:
            #先找树的根结点,储蓄在变量queue中
            queue=[]
            queue.append(self.root)
            #对已有的节点进行层次遍历
            while queue:
                item=queue.pop(0)
                if not item.lchild:
                    item.lchild=node
                    return
                if not item.rchild:
                    item.rchild=node
                    return
                else:
                    queue.append(item.lchild)
                    queue.append(item.rchild)


    def breadth_travel(self):
        """利用队列实现树的层次遍历"""
        if not self.root:
            return
        else:
            queue=[]
            queue.append(self.root)
            while queue:
                node=queue.pop(0)
                print(node.element,end=', ')
                if node.lchild:
                    queue.append(node.lchild)
                if node.rchild:
                    queue.append(node.rchild)
            print()

    def preorder(self, root):
        """先序遍历: 根左右"""
        if root == None:
            return
        print(root.element, end=', ')
        self.preorder(root.lchild)#递归
        self.preorder(root.rchild)

    def inorder(self, root):
        """递归实现中序遍历:左根右"""
        if root == None:
            return
        self.inorder(root.lchild)
        print(root.element, end=', ')
        self.inorder(root.rchild)

    def lastorder(self,root):
        """递归实现后续遍历:左右根"""
        if root == None:
            return
        self.lastorder(root.lchild)
        self.lastorder(root.rchild)
        print(root.element, end=', ')


if __name__ == '__main__':
    tree = Tree()
    for item in range(10):
        tree.add(item + 1)
    print("创建树成功!")

    #
    print("层次遍历".center(30, '*'))
    tree.breadth_travel()

    print("先序遍历")
    root = tree.root
    tree.preorder(root)

    print("\n中序遍历")
    root = tree.root
    tree.inorder(root)

    print('\n后序遍历')
    root = tree.root
    tree.lastorder(root)
------------------------------------------------------------
树成功!
*************层次遍历*************
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 
先序遍历
1, 2, 4, 8, 9, 5, 10, 3, 6, 7, 
中序遍历
8, 4, 9, 2, 10, 5, 1, 6, 3, 7, 
后序遍历
8, 9, 4, 10, 5, 2, 6, 7, 3, 1, 

二叉排序树

二叉排序树要么是空二叉树要么具有如下特点

  • 二叉排序树中,如果其根结点有左子树,那么左子树上所有结点的值都小于根结点的值;如果其根结点有右子树,那么右子树上所有结点的值都大于根结点的值。
  • 二叉排序树的左右子树也要求都是二叉排序树。

二叉排序树的常用操作

  • 查找:对比节点的值和关键字,相等则表明找到了;小了则往节点的左子树去找,大了则往右子树去找,这么递归下去,最后返回布尔值或找到的节点。
    def search(self,root,key):
        """
        搜索指定元素是否在树里面
        key:用户搜索的元素
        :param root:
        :param key:
        :return:
        """
        if root is None:
            return False
        if root.element == key:
            return root
        elif key<root.element:
            return self.search(root.lchild,key)
        else:
            return self.search(root.rchild,key)
  • 插入:从根节点开始逐个与关键字进行对比,小了去左边,大了去右边,碰到子树为空的情况就将新的节点链接。
"""二叉排序树的封装"""
class BinarySortTree(object):
    def __init__(self):
        self.root=None

    def is_empty(self):#为啥此时要有这一步
        return self.root is None

    def add(self,item):
        """
        树里面出入元素
        1。先判断树是否为空,直接让根结点指向新的结点
        2。如果不为空:
        从根结点开始访问,判断一下item和结点的大小;
        如果大于结点元素,往右孩子结点添加
        如果小于结点元素,往左孩子结点添加
        :param item:
        :return:
        """
        node=Node(item)
        if self.root is None:
            self.root=node#让根结点指向新的结点
        bt=self.root#此时的根结点为bt
        #else???
        while True:
            rootItem=bt.element
            """判断添加的元素是否大于根结点元素"""
            if item>rootItem:
                if bt.rchild is None:
                    bt.rchild=node#如果右子树为空,直接添加结点到右子树
                else:#如果右子树不为空,将右子树结点作为新的根结点,循环继续判断
                    bt=bt.rchild
            elif item < rootItem:
                if bt.lchild is None:
                    bt.lchild=node

                bt=bt.lchild
            else:#如果插入的元素和根结点相等,不做操作
                return
  • 删除:如果要删除的节点是叶子,直接删;如果只有左子树或只有右子树,则删除节点后,将子树链接到父节点即可;如果同时有左右子树,则可以将二叉排序树进行中序遍历,取将要被删除的节点的前驱或者后继节点替代这个被删除的节点的位置。
def searchDetail(self, root, parent, key):
        """
        搜索指定元素是否在树里面, 如果有, 则
        返回3个值:
            1. Bool: 是否找到该元素;
            2. node: 找到元素对应的节点
            3. parent: 找到的元素对应的父节点;
        """
        if root is None:
            return False, root, parent
        # 如果找到节点元素和用户搜索的值相等, 直接返回节点对象
        if root.element == key:
            return True, root, parent
        # 如果找到节点元素大于用户搜索的值, 直接返回节点对象
        elif root.element > key:
            return self.searchDetail(root.lchild, root, key)
        else:
            return self.searchDetail(root.rchild, root, key)

    def delete(self, key):
        """
        删除二叉排序树的节点元素:
            1). 如果要删除的节点是叶子,直接删;
            2). 如果只有左子树或只有右子树,则删除节点后,将子树链接到父节点即可;
        :param key:
        :return:
        """
        # 1. 查找删除元素对应的节点, isExists是否找到该节点元素, node是找到的节点对象, parent:元素节点的父节点
        isExists, node, parent = self.searchDetail(self.root, None, key)

        if not isExists:
            print("要删除的元素%d不存在" % (key))
            return

        # 如果深处的是根节点, 不处理
        if node == self.root:
            print("不能删除根结点")
            return
        # 1). 如果要删除的节点是叶子,直接删;
        if node.lchild is None and node.rchild is None:
            # 判断删除的节点是父节点左孩子还是右孩子
            if parent.lchild == node:
                parent.lchild = None
            else:
                parent.rchild = None

        # 2). 如果只有左子树或只有右子树,则删除节点后,将子树链接到父节点即可;
        # 如果只有左子树
        if node.lchild is not None and node.rchild is None:
            # 如果node是parent的左子树:
            if parent.lchild == node:
                parent.lchild = node.lchild
            else:
                parent.rchild = node.lchild

        # 如果只有右子树
        if node.rchild is not None and node.lchild is None:
            # 如果node是parent的左子树:
            if parent.lchild == node:
                parent.lchild = node.rchild
            else:
                parent.rchild = node.rchild

        # 如果有左右子树
        # 方法一: 令结点 node 的左子树为其双亲结点的左子树;结点 node的右子树为其自身直接前驱结点的右子树
        if node.lchild is not None and node.rchild is not None:
            # 如何找到node的直接前驱节点, 找到后, 将直接前驱节点的右子树指向node的右子树;
            # 当前节点左子树的最右节点
            # 分类讨论, 删除的是父节点的左孩子还是右孩子
            if parent.lchild == node:
                parent.lchild = node.lchild
            else:
                parent.rchild = node.lchild

            prevNode = node.lchild
            while prevNode.rchild:
                prevNode = prevNode.rchild
            # prev指的是node节点的直接前驱(中徐遍历时,node节点前面的节点)
            prevNode.rchild = node.rchild

平衡二叉树

平衡二叉树,又称为 AVL 树。遵循以下两个特点的二叉树:

  • 每棵子树中的左子树和右子树的深度差不能超过 1;
  • 二叉树中每棵子树都要求是平衡二叉树;

总结: 其实就是在二叉树的基础上,若树中每棵子树都满足其左子树和右子树的深度差都不超过 1,则这棵二叉树就是平衡二叉树。

平衡因子:每个结点都有其各自的平衡因子,表示的就是其左子树深度同右子树深度的差。
平衡二叉树中各结点平衡因子的取值只可能是:0、1 和 -1。
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值