v12_Python-二叉树

Python-二叉树

目录:

  • Python-二叉树
    • 二叉树
    • 定义
    • 基本形态
    • 特殊类型
      • 1.满二叉树
      • 2.完全二叉树
    • 相关术语
    • 二叉树遍历
    • 线索二叉树
    • 深度优先遍历
    • 广度优先遍历
    • 生成二叉树
    • 线索化存储二叉树
    • 线索化存储二叉树的遍历

二叉树

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分。

二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个结点。

定义

二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。

基本形态

二叉树是递归定义的,其结点有左右子树之分,逻辑上二叉树有五种基本形态:

  1. 空二叉树——如图(a);
  2. 只有一个根结点的二叉树——如图(b);
  3. 只有左子树——如图(c);
  4. 只有右子树——如图(d);
  5. 完全二叉树——如图(e)。
    v12_Python-二叉树_第1张图片

特殊类型

1.满二叉树

如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树

2.完全二叉树

深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1到n的结点一一对应时,称为完全二叉树。

完全二叉树的特点是叶子结点只可能出现在层序最大的两层上,并且某个结点的左分支下子孙的最大层序与右分支下子孙的最大层序相等或大1。

相关术语

  1. 结点:包含一个数据元素及其若干指向子树分支的信息。
  2. 结点的度:一个结点拥有子树的数目称为结点的度。
  3. 叶子结点:也称为终端结点,没有子树的结点或者度为零的结点。
  4. 分支结点:也称为非终端结点,度不为零的结点称为非终端结点。
  5. 树的度:树中所有结点的度的最大值。
  6. 结点的层次:从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个结点位于第L层,则其子节点位于第L+1层。
  7. 树的深度:也称为树的高度,树中所有结点的层次最大值称为树的深度。
  8. 有序树:如果树中各棵子树的次序是有先后次序,则称该树为有序树。
  9. 无序树:如果树中各棵子树没有先后次序,则称该树为无序树。
  10. 森林:由m(m>=0)棵互不相交的树构成一片森林。如果把一棵非空的树的根节点删除,则该树就变成了一片森林,森林中的树有原来根节点的各棵子树构成。

二叉树遍历

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。

线索二叉树

按照某种遍历方式对二叉树进行遍历,可以把二叉树中所有结点排列为一个线性序列。在该序列中,除第一个结点外,每个结点有且仅有一个直接前驱结点;除最后一个结点外,每个结点有且仅有一个直接后继结点。但是,二叉树中每个结点在这个序列中的直接前驱结点和直接后继结点是什么,二叉树的存储结构中并没有反映出来,只能在对二叉树遍历的动态过程中得到这些信息。为了保留结点在某种遍历序列中直接前驱和直接后继的位置信息,可以利用二叉树的二叉链表存储结构中的那些空指针域来指示。这些指向直接前驱结点和指向直接后继结点的指针被称为线索(thread),加了线索的叉树称为线索二叉树。

线索二叉树将为二叉树的遍历提供许多遍历。

深度优先遍历

对于⼀颗⼆叉树,深度优先搜索(Depth First Search)是沿着树的深度遍历树 的节点,尽可能深的搜索树的分⽀。 那么深度遍历有重要的三种⽅法。这三种⽅式常被⽤于访问树的节点,它们 之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做先序遍历 (preorder),中序遍历(inorder)和后序遍历(postorder)。我们来给出 它们的详细定义,然后举例看看它们的应⽤。

  • 先序遍历 在先序遍历中,我们先访问根节点,然后递归使⽤先序遍历访 问左⼦树,再递归使⽤先序遍历访问右⼦树 根节点->左⼦树->右⼦树
  • 中序遍历 在中序遍历中,我们递归使⽤中序遍历访问左⼦树,然后访问 根节点,最后再递归使⽤中序遍历访问右⼦树 左⼦树->根节点->右⼦树
  • 后序遍历 在后序遍历中,我们先递归使⽤后序遍历访问左⼦树和右⼦ 树,最后访问根节点 左⼦树->右⼦树->根节点

广度优先遍历

从树的root开始,从上到下从从左到右遍历整个树的节点

生成二叉树

class Node:
    # 节点类
    def __init__(self, item):
        self.item = item
        self.lchild = None
        self.rchild = None


class BinaryTree:
    # 二叉树
    def __init__(self, node=None):
        self.root = node

    def add(self, item):
        # 广度优先遍历方式添节点
        if self.root is None:
            self.root = Node(item)
        else:
            queue = list()
            queue.append(self.root)

            while len(queue) > 0:
                node = queue.pop(0)
                if not node.lchild:
                    node.lchild = Node(item)
                    return
                else:
                    queue.append(node.lchild)
                if not node.rchild:
                    node.rchild = Node(item)
                    return
                else:
                    queue.append(node.rchild)


    def breadh_travel(self):
        # 广度优先遍历
        if self.root is None:
            return
        queue = list()
        queue.append(self.root)
        while len(queue) > 0:
            node = queue.pop(0)
            print(node.item, end=' ')
            if node.lchild:
                queue.append(node.lchild)
            if node.rchild:
                queue.append(node.rchild)

    def preorder_travel(self, root):
        # 先序 根 左 右
        if root:
            print(root.item, end=' ')
            self.preorder_travel(root.lchild)
            self.preorder_travel(root.rchild)

    def inorder_travel(self, root):
        # 中序 左 根 右
        if root:
            self.inorder_travel(root.lchild)
            print(root.item, end=' ')
            self.inorder_travel(root.rchild)

    def postorder_travel(self, root):
        # 后序 左 右 根
        if root:
            self.postorder_travel(root.lchild)
            self.postorder_travel(root.rchild)
            print(root.item, end=' ')


if __name__ == '__main__':
    tree = BinaryTree()
    tree.add(0)
    tree.add(1)
    tree.add(2)
    tree.add(3)
    tree.add(4)
    tree.add(5)
    tree.add(6)
    tree.add(7)
    tree.add(8)
    tree.add(9)
    tree.breadh_travel()
    print('')
    tree.preorder_travel(tree.root)
    print('')
    tree.inorder_travel(tree.root)
    print('')
    tree.postorder_travel(tree.root)
    print('')
    

线索化存储二叉树

class TreeNode(object):
    def __init__(self, val=-1):
        self.val = val
        self.left = None
        self.right = None
        # 新增类型指针
        # 规定
        # 如果left_type==0 表示指向的是左子树,如果是1,则表示指向前驱结点
        # 如果right_type==0 表示指向的是右子树,如果是1,则表示指向后继结点
        self.left_type = 0  # 注意这里必须写0,不能写空值
        self.right_type = 0


class ThreadedBinaryTree(object):
    def __init__(self):
        self.root = None
        # 在递归进行线索化,总是保留前一个结点
        self.pre = None  # 为实现线索化,需要创建给指向当前结点的前驱结点指针

    # 增加结点测试
    def add(self, val):
        node = TreeNode(val)
        if self.root is None:
            self.root = node
            return
        queue = [self.root]
        while queue:
            temp_node = queue.pop(0)
            if temp_node.left is None:
                temp_node.left = node
                return
            else:
                queue.append(temp_node.left)
            if temp_node.right is None:
                temp_node.right = node
                return
            else:
                queue.append(temp_node.right)

    # 中序遍历测试
    def in_order(self, node):
        if node is None:
            return
        self.in_order(node.left)
        print(node.val, end=' ')
        self.in_order(node.right)

    # 二叉树进行中序线索化的方法
    def threaded_node(self, node):  # node:就是当前需要线索化的结点
        if node is None:
            return
        # 先线索化左子树
        self.threaded_node(node.left)

        # 线索化当前结点
        # 处理当前结点的前驱结点
        if node.left is None:  # 如果当前结点左子节点为空
            node.left = self.pre  # 让当前结点的左指针指向前驱结点
            node.left_type = 1  # 修改当前结点的左指针类型为前驱结点
        # 处理当前结点的后继结点
        if self.pre and self.pre.right is None:
            self.pre.right = node  # 让前驱结点的右指针指向当前结点
            self.pre.right_type = 1  # 修改前驱结点的右指针类型
        self.pre = node  # 每处理一个结点后,让当前结点是下一个结点的前驱结点

        # 线索化右子树
        self.threaded_node(node.right)


if __name__ == '__main__':
    t = ThreadedBinaryTree()
    # t.add(1)
    # t.add(3)
    # t.add(6)
    # t.add(8)
    # t.add(10)
    # t.add(14)
    # t.in_order(t.root)
    # t.threaded_node(t.root)

    # 手动创建结点--只是为了更好测试线索化有没有成功
    t1 = TreeNode(1)
    t2 = TreeNode(3)
    t3 = TreeNode(6)
    t4 = TreeNode(8)
    t5 = TreeNode(10)
    t6 = TreeNode(14)
    t1.left = t2
    t1.right = t3
    t2.left = t4
    t2.right = t5
    t3.left = t6
    print("原来的二叉树中序遍历为:")
    t.in_order(t1)
    # 线索化二叉树
    t.threaded_node(t1)
    # 测试:以值为10 的结点来测试
    left_node = t5.left
    print()
    print("10 的前驱结点是:%d" % left_node.val)  # 3
    right_node = t5.right
    print("10 的后继结点是:%d" % right_node.val)  # 1

线索化存储二叉树的遍历

class TreeNode(object):
    def __init__(self, val=-1):
        self.val = val
        self.left = None
        self.right = None
        # 新增类型指针
        # 规定:
        # 如果left_type==0 表示指向的是左子树,如果是1 则表示指向前驱结点
        # 如果right_type==0 表示指向的是右子树,如果是1 怎表示指向后继结点
        self.left_type = 0
        self.right_type = 0


class ThreadedBinaryTree(object):
    def __init__(self):
        self.root = None
        # 在递归进行线索化,总是保留前一个结点
        self.pre = None  # 为实现线索化,需要创建给指向当前结点的前驱结点指针

    # 添加结点测试
    def add(self, val):
        node = TreeNode(val)
        if self.root is None:
            self.root = node
            return
        queue = [self.root]
        while queue:
            temp_node = queue.pop(0)
            if temp_node.left is None:
                temp_node.left = node
                return
            else:
                queue.append(temp_node.left)
            if temp_node.right is None:
                temp_node.right = node
                return
            else:
                queue.append(temp_node.right)

    # 中序遍历测试
    def in_order(self, node):
        if node is None:
            return
        self.in_order(node.left)
        print(node.val, end=' ')
        self.in_order(node.right)

    # 中序遍历线索化二叉树
    def threaded_in_order(self, node):
        if node is None:
            return
        temp_node = node
        while temp_node:
            # 循环的找到left_type=1的结点,第一个找到就是值为8的结点
            # 后面随着遍历而变化,因为当left_type=1时,说明该结点是按照线索化处理后的有效结点
            while temp_node.left_type == 0:  # 从根结点开始向左找,找到第一个1停止
                temp_node = temp_node.left
            # 打印当前这个结点
            print(temp_node.val, end=" ")
            # 如果当前结点的右指针指向的是后继结点,就一直输出
            while temp_node.right_type == 1:
                # 获取到当前结点的后继结点
                temp_node = temp_node.right
                print(temp_node.val, end=" ")
            # 如果不等于1了,就替换这个遍历的结点
            temp_node = temp_node.right

    # 二叉树进行中序线索化的方法
    def threaded_node(self, node):  # node: 就是当前需要线索化的结点
        if node is None:
            return
        # 先线索化左子树
        self.threaded_node(node.left)
        # 线索化当前结点

        # 处理当前结点的前驱结点
        if node.left is None:  # 如果当前结点左子结点为空
            node.left = self.pre  # 让当前结点的左指针指向前驱结点
            node.left_type = 1  # 修改当前结点的左指针类型 为 前驱结点

        # 处理当前结点的后继结点
        if self.pre and self.pre.right is None:
            self.pre.right = node  # 让前驱结点的右指针指向当前结点
            self.pre.right_type = 1  # 修改前驱结点的右指针类型
        self.pre = node  # 每处理一个结点后,让当前结点是下一个结点的前驱结点
        # 线索化右子树
        self.threaded_node(node.right)


if __name__ == '__main__':
    # 调用add 自动创建结点
    t = ThreadedBinaryTree()
    '''
    t.add(1)
    t.add(3)
    t.add(6)
    t.add(8)
    t.add(10)
    t.add(14)
    t.in_order(t.root)
    t.threaded_node(t.root)
    print()
    t.threaded_in_order(t.root)
    '''

    # 手动创建结点--只是为了更好测试线索化有没有成功
    t1 = TreeNode(1)
    t2 = TreeNode(3)
    t3 = TreeNode(6)
    t4 = TreeNode(8)
    t5 = TreeNode(10)
    t6 = TreeNode(14)
    t1.left = t2
    t1.right = t3
    t2.left = t4
    t2.right = t5
    t3.left = t6
    print("原来的二叉树中序遍历为:")
    t.in_order(t1)
    # 线索化二叉树
    t.threaded_node(t1)
    # 测试:以值为10 的结点来测试
    left_node = t5.left
    print()
    print("10 的前驱结点是:%d" % left_node.val)  # 3
    right_node = t5.right
    print("10 的后继结点是:%d" % right_node.val)  # 1
    print("线索化二叉树的中序遍历结果为:")
    t.threaded_in_order(t1)

你可能感兴趣的:(算法,二叉树,数据结构,python)