4. Python 树与二叉树

4.1 树

树的一些性质在其它语言的数据结构中学习过就不多赘述了(因为懒)

树的简单实例:模拟文件系统

# 树的实例
class Node:
    def __init__(self, name, type='dir'):
        self.name = name
        self.type = type
        self.children = []
        self.parent = None  # 找回上一层文件夹
# n = Node('hello')
# n2 = Node('world')
# # n2文件夹在n的文件内
# n.children.append(n2)
# n2.parent = n  # 找父母
    # 打印文件名
    def __repr__(self):
        return self.name

class FileSystemTree:
    def __init__(self):
        self.root = Node('/')  # 表示根目录
        self.now = self.root  # 表示当前节点位置

    #创建一个文件夹
    def mkdir(self, name):
        #  name得以 / 结尾
        if name[-1] != '/':
            name += '/'
        node = Node(name)
        self.now.children.append(node)
        node.parent = self.now

    # 展示当前目录下的所有子目录
    def ls(self):
        return self.now.children

    # 切换目录
    def cd(self, name):
        # 相对路径
        if name[-1] != '/':
            name += '/'
        if name == '../':
            self.now = self.now.parent
            return
        # 如果children里面有要找的目录名字,就返回。
        for child in self.now.children:
            if child.name == name:
                self.now = child  # 如果找到了,就切换称它的孩子
                return
        # 如果一直没有找到,就报错
        raise ValueError('invalid dir')


tree = FileSystemTree()
# 在根目录下创建三个目录
tree.mkdir('var/')
tree.mkdir('bin/')
tree.mkdir('usr/')

tree.cd('bin/')
tree.mkdir('python/')
print(tree.ls())  # 跳转到了python/目录下

4.2 二叉树

二叉树的链式存储:将二叉树的节点定义为一个对象,节点之间通过类似链表的链接方式来链接。

节点定义:

class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None
        self.rchild = None

简单构建一个二叉树

class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子


a = BiTreeNode('A')
b = BiTreeNode('B')
c = BiTreeNode('C')
d = BiTreeNode('D')
e = BiTreeNode('E')
f = BiTreeNode('F')
g = BiTreeNode('G')
e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f
root = e
print(root.lchild.rchild.data)

还是上面那个图,二叉树的遍历:

  • 前序遍历:EACBDGF
  • 中序遍历:ABCDEGF
  • 后序遍历:BDCAFGE
  • 层次遍历:EAGCFBD
# 二叉树
from collections import deque
class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子


a = BiTreeNode('A')
b = BiTreeNode('B')
c = BiTreeNode('C')
d = BiTreeNode('D')
e = BiTreeNode('E')
f = BiTreeNode('F')
g = BiTreeNode('G')
e.lchild = a
e.rchild = g
a.rchild = c
c.lchild = b
c.rchild = d
g.rchild = f
root = e
print(root.lchild.rchild.data)


# 前序遍历
def pre_order(root):
    # 如果根节点不为空
    if root:
        # 访问根节点
        print(root.data, end='')
        # 访问左子树
        pre_order(root.lchild)
        # 访问右子树
        pre_order(root.rchild)


# 中序遍历
def in_order(root):
    if root:
        # 先访问左子树
        in_order(root.lchild)
        # 访问根节点
        print(root.data, end='')
        # 再访问右子树
        in_order(root.rchild)


# 后序遍历
def post_order(root):
    if root:
        # 先访问左子树
        post_order(root.lchild)
        # 再访问右子树
        post_order(root.rchild)
        # 最后访问根节点
        print(root.data, end='')


# 层次遍历
def level_order(root):
    queue = deque()
    queue.append(root)
    while len(queue) >0:  # 只要队不空
        node = queue.popleft()
        print(node.data, end='')
        if node.lchild:
            queue.append(node.lchild)
        if node.rchild:
            queue.append(node.rchild)


pre_order(root)
print()
in_order(root)
print()
post_order(root)
print()
level_order(root)

4.3 二叉搜索树

二叉搜索树是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key ≤ x.key;如果y是x右子树的一个节点,那么y.key ≥ x.key。

二叉搜索树的操作:查询、插入、删除

4.3.1 插入

import random


class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子
        self.parent = None

class BST:
    def __init__(self, li=None):
        self.root = None
        if li:
            for val in li:
                self.insert_no_rec(val)

    # 插入(递归版)
    def insert(self, node, val):
        if not node:
            node = BiTreeNode(val)
        # 递归左孩子
        elif val < node.data:
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node
        # 递归右孩子
        elif val > node.data:
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node
        return node

    # 插入(非递归版)
    def insert_no_rec(self, val):
        p = self.root
        if not p:  # 空树
            self.root = BiTreeNode(val)
            return
        # 不是空树 循环
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:  # 左子树不存在
                    p.lchild = BiTreeNode(val)  # 插入该节点
                    p.lchild.parent = p
                    return
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:  # 右子树不存在
                    p.rchild = BiTreeNode(val)  # 插入该节点
                    p.rchild.parent = p
                    return
            else:  # 等于什么也不干
                return

4.3.2 删除

1.如果要删除的节点是叶子节点:直接删除。

2.如果要删除的节点只有一个孩子:将此节点的父节点与孩子连接,然后删除该节点。

3.如果删除的节点有两个孩子:将其右子树的最小节点(该节点最多有一个右孩子)删除,并替换当前节点。

     # 递归查找
    def query(self, node, val):
        if not node:
            return None
        if node.data < val:
            return self.query(node.rchild, val)
        elif node.data > val:
            return self.query(node.lchild, val)
        else:
            return node

        # 非递归查找
    def query_no_rec(self, val):
        p = self.root
        while p:
            if p.data < val:
                p = p.rchild
            elif p.data > val:
                 p = p.lchild
            else:
                  return p
        return None

    # 情况一:node是叶子节点
    def __remove_node_1(self, node):
        # 如果是根节点
        if not node.parent:
            self.root = None
        if node == node.parent.lchild:  # node是它父亲节点的左孩子
            node.parent.lchild = None
        else:  # node是它父亲节点的右孩子
           node.parent.rchild = None

    # 情况2.1:node只有一个左孩子
    def __remove_node_2l(self, node):
        if not node.parent:  # 根节点
            self.root = node.lchild  # 它变成根了
            node.lchild.parent = None
        elif node == node.parent.lchild:  # 该节点是父节点的左孩子
            node.parent.lchild = node.lchild  # 该节点的父节点与该节点的左孩子连接
            node.lchild.parent = node.parent
        else:  # 该节点是父节点的右孩子
            node.parent.rchild = node.lchild
            node.lchild.parent = node.parent

    # 情况2.2:node只有一个右孩子
    def __remove_node_2r(self, node):
        if not node.parent:  # 根节点
            self.root = node.rchild
        elif node == node.parent.lchild: # 该节点是父节点的左孩子
            node.parent.lchild = node.rchild  # 该节点的父节点与该节点的右孩子
            node.rchild.parent = node.parent
        else:  # 该节点是父节点的右孩子
            node.parent.rchild = node.rchild
            node.rchild.parent = node.parent

    def delete(self, val):
        if self.root:  # 不是空树才能删
            node = self.query_no_rec(val)
            if not node:  # 如果node不存在
                return False
            if not node.lchild and not node.rchild:  # 情况1:叶子节点
                self.__remove_node_1(node)
            elif not node.rchild:  # 情况2.1:没有右孩子只有左孩子
                self.__remove_node_2l(node)
            elif not node.lchild:
                self.__remove_node_2r(node)  # 情况2.2:没有左孩子只有右孩子
            else:  # 情况3:两个孩子都有
                min_node = node.rchild  # 找右孩子的最小节点
                while min_node.lchild:
                    min_node = min_node.lchild  # 一直找最小的那个节点
                node.data = min_node.data
                # 删除min_node 要判断它是情况1还是情况2
                if min_node.rchild:
                    self.__remove_node_2r(min_node)
                else:
                    self.__remove_node_1(min_node)

插入、查找、删除的完整代码

#
import random


class BiTreeNode:
    def __init__(self, data):
        self.data = data
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子
        self.parent = None

class BST:
    def __init__(self, li=None):
        self.root = None
        if li:
            for val in li:
                self.insert_no_rec(val)

    # 插入(递归版)
    def insert(self, node, val):
        if not node:
            node = BiTreeNode(val)
        # 递归左孩子
        elif val < node.data:
            node.lchild = self.insert(node.lchild, val)
            node.lchild.parent = node
        # 递归右孩子
        elif val > node.data:
            node.rchild = self.insert(node.rchild, val)
            node.rchild.parent = node
        return node

    # 插入(非递归版)
    def insert_no_rec(self, val):
        p = self.root
        if not p:  # 空树
            self.root = BiTreeNode(val)
            return
        # 不是空树 循环
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:  # 左子树不存在
                    p.lchild = BiTreeNode(val)  # 插入该节点
                    p.lchild.parent = p
                    return
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:  # 右子树不存在
                    p.rchild = BiTreeNode(val)  # 插入该节点
                    p.rchild.parent = p
                    return
            else:  # 等于什么也不干
                return

    # 前序遍历
    def pre_order(self, root):
        # 如果根节点不为空
        if root:
            # 访问根节点
            print(root.data, end=' ')
            # 访问左子树
            self.pre_order(root.lchild)
            # 访问右子树
            self.pre_order(root.rchild)

    # 中序遍历
    def in_order(self, root):
        if root:
            # 先访问左子树
            self.in_order(root.lchild)
            # 访问根节点
            print(root.data, end=' ')
            # 再访问右子树
            self.in_order(root.rchild)

    # 后序遍历
    def post_order(self, root):
        if root:
            # 先访问左子树
            self.post_order(root.lchild)
            # 再访问右子树
            self.post_order(root.rchild)
            # 最后访问根节点
            print(root.data, end=' ')

        # 递归查找
    def query(self, node, val):
        if not node:
            return None
        if node.data < val:
            return self.query(node.rchild, val)
        elif node.data > val:
            return self.query(node.lchild, val)
        else:
            return node

        # 非递归查找
    def query_no_rec(self, val):
        p = self.root
        while p:
            if p.data < val:
                p = p.rchild
            elif p.data > val:
                 p = p.lchild
            else:
                  return p
        return None

    # 情况一:node是叶子节点
    def __remove_node_1(self, node):
        # 如果是根节点
        if not node.parent:
            self.root = None
        if node == node.parent.lchild:  # node是它父亲节点的左孩子
            node.parent.lchild = None
        else:  # node是它父亲节点的右孩子
           node.parent.rchild = None

    # 情况2.1:node只有一个左孩子
    def __remove_node_2l(self, node):
        if not node.parent:  # 根节点
            self.root = node.lchild  # 它变成根了
            node.lchild.parent = None
        elif node == node.parent.lchild:  # 该节点是父节点的左孩子
            node.parent.lchild = node.lchild  # 该节点的父节点与该节点的左孩子连接
            node.lchild.parent = node.parent
        else:  # 该节点是父节点的右孩子
            node.parent.rchild = node.lchild
            node.lchild.parent = node.parent

    # 情况2.2:node只有一个右孩子
    def __remove_node_2r(self, node):
        if not node.parent:  # 根节点
            self.root = node.rchild
        elif node == node.parent.lchild: # 该节点是父节点的左孩子
            node.parent.lchild = node.rchild  # 该节点的父节点与该节点的右孩子
            node.rchild.parent = node.parent
        else:  # 该节点是父节点的右孩子
            node.parent.rchild = node.rchild
            node.rchild.parent = node.parent

    def delete(self, val):
        if self.root:  # 不是空树才能删
            node = self.query_no_rec(val)
            if not node:  # 如果node不存在
                return False
            if not node.lchild and not node.rchild:  # 情况1:叶子节点
                self.__remove_node_1(node)
            elif not node.rchild:  # 情况2.1:没有右孩子只有左孩子
                self.__remove_node_2l(node)
            elif not node.lchild:
                self.__remove_node_2r(node)  # 情况2.2:没有左孩子只有右孩子
            else:  # 情况3:两个孩子都有
                min_node = node.rchild  # 找右孩子的最小节点
                while min_node.lchild:
                    min_node = min_node.lchild  # 一直找最小的那个节点
                node.data = min_node.data
                # 删除min_node 要判断它是情况1还是情况2
                if min_node.rchild:
                    self.__remove_node_2r(min_node)
                else:
                    self.__remove_node_1(min_node)
                    
                    
# li = list(range(15))
# random.shuffle(li)
# tree = BST(li)
# tree.pre_order(tree.root)
# print()
# tree.in_order(tree.root)  # 是排好序的
# print()
# tree.post_order(tree.root)
tree = BST([1, 4, 2, 5, 3, 8, 6, 9, 7])
tree.in_order(tree.root)
print()
tree.delete(4)
tree.in_order(tree.root)

二叉搜索树的效率:平均情况下,二叉搜索树进行搜索的时间复杂度为O(lgn)

最坏情况下,二叉搜索树可能非常偏斜

解决方案:随机化插入、AVL树

4.4 AVL树

AVL树是一棵自平衡的二叉搜索树,也叫平衡二叉树,有以下性质:

  • 根的左右子树的高度之差的绝对值不能超过1;
  • 根的左右子树都是平衡二叉树。

4.4.1 插入

  • 插入一个节点可能会破坏AVL树的平衡,可以通过旋转操作来进行修正;
  • 插入一个节点后,只有从插入节点道根节点的路径上的节点的平衡可能被改变。我们需要找出第一个破坏了平衡条件的节点,称之为K。K的两颗子树的高度差2;

不平衡的出现可能有4种情况

1. 不平衡更是由于对K的右孩子的右子树插入导致的:左旋

2. 不平衡是由于对K的左孩子的左子树插入导致的:右旋

 3. 不平衡是由于对K的右孩子的左子树插入导致的:右旋-左旋

  3. 不平衡是由于对K的左孩子的右子树插入导致的:左旋-右旋

from bst import BiTreeNode, BST


class AVLNode(BiTreeNode):
    def __init__(self, data):
        BiTreeNode.__init__(self, data)
        # 平衡度K
        self.bf = 0  # 左边沉是-1,右边沉是1


class AVLTree(BST):
    def __init__(self, li=None):
        BST.__init__(self, li)

    # 左旋
    def rotate_left(self, p, c):
        s2 = c.lchild
        p.rchild = s2
        if s2:
            s2.parent = p
        c.lchild = p
        p.parent = c
        p.bf = 0
        c.bf = 0
        return c

    # 右旋
    def rotate_right(self, p, c):
        s2 = c.rchild
        p.lchild = s2
        if s2:
            s2.parent = p
        c.rchild = p
        p.parent = c
        p.bf = 0
        c.bf = 0
        return c

    # 右旋-左旋
    def rotate_right_left(self, p, c):
        # 右旋
        g = c.lchild
        s3 = g.rchild
        c.lchild = s3
        if s3:
            s3.paren = c
        g.rchild = c
        c.parent = g
        # 左旋
        s2 = g.lchild
        p.rchild = s2
        if s2:
            s2.parent = p
        g.lchild = p
        p.parent = g
        # 更新bf
        if g.bf > 0:
            # 元素插入到s3上
            p.bf = -1
            c.bf = 0
        elif g.bf < 0:
            # 元素插入到s2上
            p.bf = 0
            c.bf = 1
        else:  # 插入的元如是g
            p.bf = 0
            c.bf = 0
        g.bf = 0
        return g

    # 左旋-右旋
    def rotate_left_right(self, p, c):
        # 左旋
        g = c.rchild
        s2 = g.lchild
        c.rchild = s2
        if s2:
            s2.parent = c
        g.lchild = c
        c.parent = g
        # 右旋
        s3 = g.rchild
        p.lchild = s3
        if s3:
            s3.parent = p
        g.rchild = p
        p.parent = g
        # 更新bf
        if g.bf < 0:  # 元素插入到s2
            p.bf = 1
            c.bf = 0
        elif g.bf > 0:  # 元素插入到s3
            p.bf = 0
            c.bf = -1
        else:  # 插入的元素其实是g
            p.bf = 0
            c.bf = 0
        g.bf = 0
        return g

    def insert_no_rec(self, val):
        # 和BST一样,先插入再判断
        p = self.root
        if not p:  # 空树
            self.root = AVLNode(val)
            return
        # 不是空树 循环
        while True:
            if val < p.data:
                if p.lchild:
                    p = p.lchild
                else:  # 左子树不存在
                    p.lchild = AVLNode(val)  # 插入该节点
                    p.lchild.parent = p
                    node = p.lchild  # node存储的就是插入的节点
                    break
            elif val > p.data:
                if p.rchild:
                    p = p.rchild
                else:  # 右子树不存在
                    p.rchild = AVLNode(val)  # 插入该节点
                    p.rchild.parent = p
                    node = p.rchild
                    break
            else:  # val == p.data
                return
        # 更新balance factor(bf)
        while node.parent:  # node.parent不空
            if node.parent.lchild == node:  # 传递是从左子树来的,左子树更沉
                # 更新node.parent的bf -=1
                if node.parent.bf < 0:  # 原来node.parent.bf == -1,更新后变成-2->>>就要进行旋转
                    # 做旋转
                    # 看node哪边沉
                    g = node.parent.parent  # 为了连接旋转之后的子树用
                    x = node.parent  # 记一下旋转之前子树的根
                    if node.bf > 0:  # 右边沉
                        n = self.rotate_left_right(node.parent, node)
                    else:  # 左边沉
                        n = self.rotate_right(node.parent, node)
                    # 记得把n和g连起来
                elif node.parent.bf > 0:  # 原来原来node.parent.bf == 1,更新后变成0
                    node.parent.bf = 0
                    # 不再传递
                    break
                else:  # # 原来原来node.parent.bf == 0,更新后变成-1
                    node.parent.bf = -1
                    node = node.parent
                    # 继续传递
                    continue
            else:  # 传递是从右子树来的,右子树更沉
                # 更新后的node.parent.bf +=1
                if node.parent.bf > 0:  # 原来node.parent.bf == 1,更新后变成2
                    # 左旋转,看node哪边沉
                    g = node.parent.parent  # 为了连接旋转之后的子树用
                    x = node.parent  # 记一下旋转之前子树的根
                    if node.bf < 0:  # node.bf = -1
                        n = self.rotate_right_left(node.parent, node)
                    else:  # node.bf = 1
                        n = self.rotate_left(node.parent, node)
                    # 记得连起来
                elif node.parent.bf < 0:  # 原来node.parent.bf == -1,更新后变成0
                    node.parent.bf = 0
                    break
                else: # 原来node.parent.bf == 0,更新后变成1
                    node.parent.bf = 1
                    node = node.parent
                    # 继续传递
                    continue
            # 连接旋转后的子树->>>就是连接g和n
            n.parent = g
            if g:  # g不空
                if x == g.lchild:  # g原来的左孩子是node.parent
                    g.lchild = n
                else:  # g原来的右孩子是node.parent
                    g.rchild = n
                break
            else:  # g空
                self.root = n
                break


tree = AVLTree([9, 8, 7, 6, 5, 4, 3, 2, 1])
tree.pre_order(tree.root)
print()
tree.in_order(tree.root)

AVL树的扩展:B树,B树是一颗自平衡的多路搜索树。常用于数据库的索引。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃头少女Emily

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值