Python数据结构与算法—树与二叉树

本文深入探讨了树与二叉树的概念,包括树的基本术语、树的存储方式,以及二叉树的定义、遍历方法。特别讨论了二叉搜索树和AVL树,解释了AVL树的平衡因子和插入操作中的旋转修正策略。
摘要由CSDN通过智能技术生成


一、树

1、基础概念

1.定义:
树(Tree)是n(n≥0)个节点的有限集合T,它满足两个条件:有且仅有一个特定的称为根(Root)的节点;其余的节点可以分为m(m≥0)个互不相交的有限集合T1、T2、……、Tm,其中每一个集合又是一棵树,并称为其根的子树(Subtree)。
在这里插入图片描述
2.基本概念:

  • 节点的度:一个节点含有的子树的个数称为该节点的度;

  • 树的度:一棵树中,最大的节点的度称为树的度;

  • 叶节点或终端节点:度为零的节点;

  • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

  • 树的高度或深度:树中节点的最大层次;

  • 森林:由m(m>=0)棵互不相交的树的集合称为森林;

  • 路径:对于一棵子树中的任意两个不同的结点,如果从一个结点出发,按层次自上而下沿着一个个树枝能到达另一结点,称它们之间存在着一条路径

  • 常用树的分类

    • 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;

    • 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;

    • 二叉树:每个节点最多含有两个子树的树称为二叉树;

    • 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;

    • 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;

    • 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);

    • 霍夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;

    • B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

在这里插入图片描述
3.树的储存:
在python中一切皆对象,树也不列外,树在python中可以通过列表和链表来储存。通过列表是将每个节点对象储存,在逻辑上不过形象,基本不用;用的最多的是通过链表构建一个树对象,其基本属性是根节点,根节点的左树属性和右树属性连接不同的节点,依次构建一颗庞大的树。

# __author__: PPPsych
# date: 2021/1/8


class Node(object):
    """节点类"""
    def __init__(self, elem=-1, lchild=None, rchild=None):
        self.elem = elem
        self.lchild = lchild
        self.rchild = rchild


class Tree(object):
    """树类"""
    def __init__(self, root=None):
        self.root = root

2、实例:模拟文件系统

# __author__: PPPsych
# date: 2021/1/8


class Node:
    """节点类"""

    def __init__(self, name, type='dir'):
        self.name = name
        self.type = type
        self.children = []
        self.parent = None
        # 链式存储

    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
        for child in self.now.children:
            if child.name == name:
                self.now = child
                return
        raise ValueError('invalid dir')


if __name__ == '__main__':
    print('——————————测试——————————')
    tree = FileSystemTree()
    tree.mkdir('var/')
    tree.mkdir('bin/')
    tree.mkdir('usr/')
    print(tree.root.children)
    print(tree.ls())
    tree.cd('bin/')
    tree.mkdir('python/')
    print(tree.ls())
    tree.cd('../')
    print(tree.ls())


# ——————————测试——————————
# [var/, bin/, usr/]
# [var/, bin/, usr/]
# [python/]
# [var/, bin/, usr/]



二、二叉树

1、基础概念

**1.定义:**二叉树(Binary Tree)是n(n≥0)个节点的有限集合,它或者是空集(n=0),或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成。二叉树与普通有序树不同,二叉树严格区分左孩子和右孩子,即使只有一个子节点也要区分左右。
在这里插入图片描述
在python中简单二叉树的实现:

# __author__: PPPsych
# date: 2021/1/8


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


if __name__ == '__main__':
    print('——————————测试——————————')
    a = BiTreeNode('A')
    b = BiTreeNode('B')
    c = BiTreeNode('C')
    d = BiTreeNode('D')
    e = BiTreeNode('E')
    f = BiTreeNode('F')
    g = BiTreeNode('G')

    root = e
    e.lchild = a
    e.rchild = g
    a.rchild = c
    c.lchild = b
    c.rchild = d
    g.rchild = f

    print(root.lchild.rchild.data)


# ——————————测试——————————
# C

2.二叉树的特征

  • 二叉树第i(i≥1)层上的节点最多为2^{i-1}2i−1个。
  • 深度为k(k≥1)的二叉树最多有2^k-12k-1个节点。
  • 在任意一棵二叉树中,树叶的数目比度数为2的节点的数目多一。
  • 满二叉树 :深度为k(k≥1)时有2^k-12k-1个节点的二叉树。
  • 完全二叉树 :只有最下面两层有度数小于2的节点,且最下面一层的叶节点集中在最左边的若干位置上。

2、二叉树的遍历

1、遍历 :
沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次。

先序遍历: 先访问树根,再访问左子树,最后访问右子树;
中序遍历: 先访问左子树,再访问树根,最后访问右子树;
后序遍历: 先访问左子树,再访问右子树,最后访问树根;
层次遍历: 从根节点开始,逐层从左向右进行遍历。

在这里插入图片描述
2、递归思想
1.什么是递归?

所谓递归函数是指一个函数的函数体中直接调用或间接调用了该函数自身的函数。这里的直接调用是指一个函数的函数体中含有调用自身的语句,间接调用是指一个函数在函数体里有调用了其它函数,而其它函数又反过来调用了该函数的情况。

2.递归函数调用的执行过程分为两个阶段

递推阶段:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件。
回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解。

3.优点与缺点

优点:递归可以把问题简单化,让思路更为清晰,代码更简洁
缺点:递归因系统环境影响大,当递归深度太大时,可能会得到不可预知的结果
在这里插入图片描述

3、遍历的代码实现

# __author__: PPPsych
# date: 2021/1/8
from collections import deque


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


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):
    """层次遍历-队列实现"""
    q = deque()
    q.append(root)
    while len(q) > 0:  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值