浅谈Python数据结构(七)--树

本文介绍了树的基本概念,重点讨论了二叉树的定义、分类(满二叉树和完全二叉树)、性质以及遍历方法。接着详细讲解了二叉搜索树的特性,包括查询、插入和删除操作。二叉搜索树在数据结构中具有重要地位,适合高效查找。最后,文中提到了二叉搜索树的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近学习了python数据结构,做一些必要的笔记,一来是对自己学习的知识的巩固,二来对有同样问题的人有参考作用



一 树

1、基本概念

树是由结点或顶点和边组成的(可能是非线性的)且不存在着任何环的一种数据结构。没有结点的树称为空(null或empty)树。一棵非空的树包括一个根结点,还(很可能)有多个附加结点,所有结点构成一个多级分层结构。

在这里插入图片描述
树是由n个节点组成的集合:

  • 如果n=0,那么这是一颗空树

  • 如果n>0,那么存在一个节点作为树的根节点,其他节点可以分成m个集合,每个集合本身又是一棵树

2、特点

  • 根节点

  • 叶子节点

  • 树的深度

  • 树的度

  • 父节点

  • 孩子节点

二 二叉树

1、定义

二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2i-1个结点;深度为k的二叉树至多有2k-1个结点;对任何一棵二叉树T,如果其终端结点数为n0,度为2的结点数为n2,则n0=n2+1。

在这里插入图片描述

2、分类

(1)满二叉树

定义:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点。也可以这样理解,除叶子结点外的所有结点均有两个子结点。节点数达到最大值,所有叶子结点必须在同一层上。

性质:

  • 一颗树深度为h,最大层数为k,深度与最大层数相同,k=h;

  • 叶子数为2h;

  • 第k层的结点数是:2k-1;

  • 总结点数是:2k-1,且总节点数一定是奇数

在这里插入图片描述

(2)完全二叉树

定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~(h-1)层) 的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。

注:完全二叉树是效率很高的数据结构,堆是一种完全二叉树或者近似完全二叉树,所以效率极高,像十分常用的排序算法、Dijkstra算法、Prim算法等都要用堆才能优化,二叉排序树的效率也要借助平衡性来提高,而平衡性基于完全二叉树。

3、性质

二叉树的性质:

  • 在非空二叉树中,第i层的结点总数不超过2i-1, i>=1;

  • 深度为h的二叉树最多有2h-1个结点(h>=1),最少有h个结点;

  • 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;

  • 具有n个结点的完全二叉树的深度为log2(n+1);

  • 有N个结点的完全二叉树各结点如果用顺序方式存储,则结点之间有如下关系:

    • 若I为结点编号则 如果I>1,则其父结点的编号为I/2;

    • 如果2I<=N,则其左儿子(即左子树的根结点)的编号为2I;若2I>N,则无左儿子;

    • 如果2I+1<=N,则其右儿子的结点编号为2I+1;若2I+1>N,则无右儿子。

  • 给定N个节点,能构成h(N)种不同的二叉树,其中h(N)为卡特兰数的第N项,h(n)=C(2*n, n)/(n+1)。

  • 设有i个枝点,I为所有枝点的道路长度总和,J为叶的道路长度总和J=I+2i

4、二叉树的遍历

在这里插入图片描述
构造树:

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

(1)前序遍历

不同的遍历方法主要是根节点的访问顺序不同。

def pre_order(root):
    if root:
        print(root.data,end=",")
        pre_order(root.lchild)
        pre_order(root.rchild)

(2)中序遍历

def in_order(root):
    if root:
        in_order(root.lchild)
        print(root.data,end=",")
        in_order(root.rchild)

(3)后序遍历

def post_order(root):
    if root:
        post_order(root.lchild)
        post_order(root.rchild)
        print(root.data, end=',')

(4)层次遍历

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)

特别地:我们们要能够根据前序、中序、后序遍历的结果的其中两种,推断出树的结构。

三 二叉搜索树

1、定义

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

在这里插入图片描述

2、二叉搜索树的操作

(1)查询

二叉查找树的性质:对二叉查找树进行中序遍历,即可得到有序的数列。

(2)插入

二叉查找树的插入过程如下:

  • 若当前的二叉查找树为空,则插入的元素为根节点;

  • 若插入的元素值小于根节点值,则将元素插入到左子树中;

  • 若插入的元素值不小于根节点值,则将元素插入到右子树中。

(3)删除

二叉查找树的删除,分三种情况进行处理:

  • p为叶子节点,直接删除该节点,再修改其父节点的指针(注意分是根节点和不是根节点),如图a;

在这里插入图片描述

  • p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可(注意分是根节点和不是根节点),如图b;

在这里插入图片描述

  • p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c:

在这里插入图片描述
(4)代码实现

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 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
        else:
            return None


    def in_order(self, root):
        if root:
            self.in_order(root.lchild)
            print(root.data, end=',')
            self.in_order(root.rchild)

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

    def __remove_node_21(self,node):
        # 情况2.1: node只有一个左孩子
        if not node.parent: # 根节点
            node.lchild.parent = None
            self.root = node.lchild
        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

    def __remove_node_22(self,node):
        # 情况2.2: node只有一个右孩子
        if not node.parent:
            node.rchild.parent = None
            self.root = node.rchild
        if 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:
                return False
            else:
                if not node.lchild and not node.rchild: # 叶子节点
                    self.__remove_node_1(node)
                elif not node.rchild: # 只有一个左孩子
                    self.__remove_node_21(node)
                elif not node.lchild: # 只有一个右孩子
                    self.__remove_node_22(node)
                else: # 既有左孩子也有右孩子
                    min_node = node.rchild
                    while min_node.lchild:
                        min_node = min_node.lchild
                    node.data = min_node.data
                    # 删除 min_node 节点
                    if min_node.rchild:
                        self.__remove_node_22(min_node)
                    else:
                        self.__remove_node_1(min_node)


tree = BST([1,4,2,5,3,8,6,9,7])
tree.in_order(tree.root)
print()
print(tree.query_no_rec(12))
print()
tree.delete(4)
tree.in_order(tree.root)

四 总结

如有错误恳请指正,如有侵权请联系我删除

参考文章: 数据结构中各种树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值