【数据结构】(Python语言):树(二叉树、二叉搜索树的实现-添加节点、删除节点、修剪节点、前序/中序/后序/层级遍历)

本文介绍了二叉树的概念,包括二叉搜索树、AVL树、满二叉树和完全二叉树的特点。详细阐述了二叉搜索树的插入、查找和删除操作,以及前序、中序、后序和广度遍历的算法。同时,给出了链表实现的二叉搜索树代码示例。
摘要由CSDN通过智能技术生成

目录

树的简单介绍

二叉树

二叉搜索树:BST(Binary Search Tree)

平衡二叉树:AVL树

满二叉树

完全二叉树

二叉树的遍历

前序遍历

中序遍历

后序遍历

广度遍历/层序遍历

 二叉搜索树的实现(链表)

创建二叉搜索树的节点的类

创建二叉搜索树的类及方法清单

添加节点

查找节点

删除节点

修剪节点

前序遍历(根左右)

中序遍历(左根右)

后序遍历(左右根)

广度遍历/层级遍历(从根节点开始,自上而下,从左到右)

二叉搜索树完整代码:(链表实现)


树的简单介绍

树不是线性的,是层级结构。

  • 树有零个节点(空树),或者一个节点(根节点),或者多个节点;
  • 树中根节点没有父节点,其他节点都有一个父节点;
  • 树的叶子节点没有子节点,其他节点都有一个或多个子节点;
  • 树的左右子树的后代互不相交。

二叉树

 二叉树与普通树的区别:

二叉树的每个节点最多有2个子节点,分别称为左子节点、右子节点。

 二叉树:

1、没有节点(空树)

2、包含根节点、左子树、右子树。每个子树都是一个二叉树。

二叉搜索树:BST(Binary Search Tree)

  • 有序。
  • 每个节点,若有左子树,左子树都比它小,若有右子树,右子树都比它大。
  • 每个子树都是一个二叉搜索树。 
  • 没有值相同的节点。
  • 可以是空树。

平衡二叉树:AVL树

  • 本质上是二叉搜索树。
  • 包含根节点、左子树、右子树。每个子树都是一个平衡二叉树。
  • 平衡条件:每个节点的左右子树的高度之差的绝对值(平衡因子)最多为1。

完美的平衡二叉树:

叶子节点在最后两层。除了叶子节点,其他节点都有2个子节点。

(完美的平衡二叉树,除了最后一层,其余层级是满二叉树)

满二叉树

  • 平衡二叉树的特殊情况。
  • 叶子节点只在最后一层。
  • 除了叶子节点,其他节点都有2个子节点。
  • 若根节点所在层级设为第1层,则第i层共有2^{^{i-1}}个节点。
  • 若根节点所在层级设为第1层,则高度为k的树共有2^{_{k}}-1个节点,共有2^{^{k-1}}个叶子节点。
  • 若根节点所在层级设为第1层,则共有n个节点的树的高度是log_{2}(n+1)

完全二叉树

  • 平衡二叉树的特殊情况。
  • 叶子节点在最后两层。最后一层的节点从左到右连续。
  • 每个节点的左右子树的高度之差的绝对值最多为1。

(完全二叉树,最后一层的节点从左到右连续,其余层级是满二叉树)

完全二叉树和完美的平衡二叉树的区别:

完全二叉树最后一层必须从左到右连续。

二叉树的遍历

前序遍历

根节点 -->左子树 -->右子树 。

中序遍历

左子树 -->根节点 -->右子树 。

后序遍历

左子树 -->右子树 -->根节点。

广度遍历/层序遍历

从根节点开始,从上到下,从左到右访问节点。

 二叉搜索树的实现(链表)

创建二叉搜索树的节点的类

class TreeNode:
    """ 二叉搜索树的节点 """
    def __init__(self,value):
        self.value = value
        self.left_child = None
        self.right_child = None

创建二叉搜索树的类及方法清单

class BSTree:
    """ 二叉搜索树 """
    def __init__(self):
        self.root = None
        self._size = 0

    def is_empty(self):
        """ 判断是否是空树,返回布尔类型 """
        return self.root == None

    def size(self):
        """ 统计树有多少节点 """
        return self._size

    def clear(self):
        """ 清空树 """
        self.root = None

    def add_node(self,item):
        """ 添加节点 """
        pass

    def find_node(self,item):
        """ 查找节点,返回布尔类型 """
        return

    def remove_node(self,item):
        """ 删除节点 """
        pass

    def trim(self,low,high):
        """ 修剪节点,low<=元素值<=high """
        pass

    def pre_order(self):
        """ 前序遍历,返回迭代器 """
        pass

    def mid_order(self):
        """ 中序遍历,返回迭代器 """
        pass

    def post_order(self):
        """ 后序遍历,返回迭代器 """
        pass

    def breadth_order(self):
        """ 广度遍历/层级遍历,返回迭代器 """
        pass


if __name__ == "__main__":
    # 实例化和方法测试
    tree = BSTree()
    pass

添加节点

1、若为空树,则新元素为根节点。

2、不是空树,从根节点开始,若新元素比当前节点小,查看左子树的节点。若左子节点为空,则新元素为左子节点。

3、不是空树,从根节点开始,若新元素比当前节点大,查看右子树的节点。若右子节点为空,则新元素为右子节点。

注:新插入的节点总是叶子节点。

def add_node(self,item):
    """ 添加节点 """
    def recurse_node(node):
        if item < node.value:
            if not node.left_child: node.left_child = TreeNode(item)
            else: recurse_node(node.left_child)
        elif not node.right_child: node.right_child = TreeNode(item)
        else: recurse_node(node.right_child)

    if self.is_empty(): self.root = TreeNode(item)
    else: recurse_node(self.root)
    self._size += 1
    

查找节点

思路:从根节点开始,比对节点的值。若相等(即找到),返回True,若小于节点,比对左子树;若大于节点,比对右子树,没有找到返回False。

def find_node(self,item):
    """ 查找节点,返回布尔类型 """
    def recurse(node):
        if not node: return False
        elif item == node.value: return True
        elif item > node.value: return recurse(node.right_child)
        else: return recurse(node.left_child)
    return recurse(self.root)

删除节点

1、若空树或者没有找到删除节点,返回None;

2、树只有一个节点,且就是删除的节点,则根节点直接为None;

3、删除的节点是叶子节点,则直接删除该节点;

4、删除的节点只有左/右子节点,则左/右子节点直接替代删除的节点;

5、删除的节点既有左子节点又有右子节点,两种方法:

        第一种,若删除节点del是父节点parent的左子节点,parent的左子节点为del的左子节点left,del的直接前驱max_left的右子节点为del的右子节点right。若删除节点del是父节点parent的右子节点,parent的右子节点为del的左子节点left,del直接前驱max_left的右子节点为del的右子节点right。

        涉及节点:删除的节点del,删除节点的父节点parent,删除节点的左子节点left,删除节点的右子节点right,删除节点的直接前驱(左子树中最大值节点)max_left。

        第二种,找到删除节点del的直接前驱max_left,del由max_left直接替代,再将原max_left删除(原max_left的父节点P的右子节点为原max_left的左子节点L)。或者 找到直接后继min_right,del由min_right直接替代,再将原min_right删除(原min_right的父节点P的左子节点为原min_right的右子节点R)。

        涉及节点:删除的节点del,删除节点的直接前驱(左子树中最大值节点)max_left,直接前驱的父节点P,直接前驱的左子节点L。或者 删除的节点del,删除节点的直接后继(右子树中最小值节点)min_right,直接后继的父节点P,直接后继的右子节点R。

某节点的直接前驱和直接后继在删除时的区别
某节点的直接前驱某节点的直接后继
节点某节点的左子树中,最大值的节点某节点的右子树中,最小值的节点
节点位置某节点的左子节点的右子节点的右子节点...最右子节点某节点的右子节点的左子节点的左子节点...最左子节点
删除时,其子节点的处理删除原直接前驱时,直接前驱的左子节点则为直接前驱的父节点的右子节点删除原直接后继时,直接后继的右子节点则为直接后继的父节点的左子节点
def remove_node(self,item):
    """ 删除节点 """

    def deleteNode(node):
        """ 递归寻找删除节点。返回子树 """
        # 没有删除的节点,返回None
        if not node: return
        # 找到删除的节点,执行删除函数delete()
        if node.value == item:
            node = delete(node)
        # 当前节点小于删除的节点,递归当前节点的右子节点
        elif node.value < item: node.right_child = deleteNode(node.right_child)
        # 当前节点大于删除的节点,递归当前节点的左子节点
        else: node.left_child = deleteNode(node.left_child)
        return node

    def delete(node):
        """ 删除节点。返回删除后的子树。
            没有子节点,直接删除;有一个子节点时,子节点直接替代删除节点;
            有两个节点时,直接前驱直接替代删除节点,并删除原直接前驱 """
        # 没有左子节点也没有右子节点,删除的节点返回None
        if not node.left_child and not node.right_child: return
        # 有左子节点,删除的节点由其左子节点替代
        if node.left_child and not node.right_child: return node.left_child
        # 有右子节点,删除的节点由其右子节点替代
        if node.right_child and not node.left_child: return node.right_child
        # 既有左子节点又有右子节点,找到删除节点的直接前驱,原删除节点由直接前驱替代,原直接前驱的子节点直接为其父节点的子节点
        parent = node
        curr = node.left_child
        while curr.right_child:
            parent = curr
            curr = curr.right_child
        node.value = curr.value
        if parent != node: parent.right_child = curr.left_child
        else: node.left_child = curr.left_child
        return node
    # 根节点接收删除后的树
    self.root = deleteNode(self.root)

修剪节点

给定范围(最小值low和最大值high),去除范围外的节点,只保留 low<=节点<=high。

1、空树,返回None;

2、小于范围中最小值,左子树剪枝,只递归返回右子树的修剪结果;

3、大于范围中最大值,右子树剪枝,只递归返回左子树的修剪结果;

4、在范围内,递归返回左子树和右子树的修剪结果。

def trim(self,low,high):
    """ 修剪元素,low<=元素值<=high """
    def recurse(node):
        if not node: return
        if node.value < low: return recurse(node.right_child)
        if node.value > high: return recurse(node.left_child)
        node.left_child = recurse(node.left_child)
        node.right_child = recurse(node.right_child)
        return node
    return recurse(self.root)

前序遍历(根左右)

思路:一个栈astore(起始元素根节点)。

从栈顶依次取出一个元素,其值加入迭代器,并将其右子节点、左子节点依次(先右后左)加入栈。直至栈为空。

def pre_order(self):
    """ 前序遍历,返回迭代器 """
    if self.is_empty(): return
    astore = [self.root]
    while astore:
        curr = astore.pop()
        yield curr.value
        if curr.right_child: astore.append(curr.right_child)
        if curr.left_child: astore.append(curr.left_child)

使用递归

def pre_order(self):
    """ 前序遍历,返回迭代器 """
    alist = []
    def recurse(node):
        if not self.is_empty():
            alist.append(node.value)
            if node.left_child: recurse(node.left_child)
            if node.right_child: recurse(node.right_child)
    recurse(self.root)
    return iter(alist)

中序遍历(左根右)

思路:一个栈astore,一个指针curr(起始指向根节点)。

将树左侧的所有左子节点全部加入栈。从栈顶依次取出一个元素,将其值加入迭代器,若有右子节点(以及右子节点的左子节点)则加入栈。直至栈为空。

def mid_order(self):
    """ 中序遍历,返回迭代器 """
    if self.is_empty():
        return
    astore = []
    curr = self.root
    while curr or astore:
        if curr:
            astore.append(curr)
            curr = curr.left_child
        else:
            curr = astore.pop()
            yield curr.value
            curr = curr.right_child
    

使用递归

def mid_order(self):
    """ 中序遍历,返回迭代器 """
    alist = []
    def recurse(node):
        if not self.is_empty():
            if node.left_child: recurse(node.left_child)
            alist.append(node.value)
            if node.right_child: recurse(node.right_child)
    recurse(self.root)
    return iter(alist)

后序遍历(左右根)

思路:两个栈astore(起始元素根节点)、bstore。

从astore依次取出一个元素,将元素加入bstore,将其左右子节点依次加入astore。直至astore为空。

从bstore依次取出一个元素,将其值加入迭代器。

def post_order(self):
    """ 后序遍历,返回迭代器 """
    if self.is_empty(): return
    astore = [self.root]
    bstore = []
    while astore:
        curr = astore.pop()
        bstore.append(curr)
        if curr.left_child: astore.append(curr.left_child)
        if curr.right_child: astore.append(curr.right_child)
    while bstore:
        curr = bstore.pop()
        yield curr.value

使用递归

def post_order(self):
    """ 后序遍历,返回迭代器 """
    alist = []
    def recurse(node):
        if not self.is_empty():
            if node.left_child: recurse(node.left_child)
            if node.right_child: recurse(node.right_child)
            alist.append(node.value)
    recurse(self.root)
    return iter(alist)

广度遍历/层级遍历(从根节点开始,自上而下,从左到右)

思路:一个队列astore(起始元素是根节点)。

从队头取出一个元素,其值加入迭代器,并将其左右子节点依次从队尾加入队列。依次循环直至队列为空。

def breadth_order(self):
    """ 广度遍历/层级遍历,返回迭代器 """
    if self.is_empty(): return
    astore = [self.root]
    while astore:
        curr = astore.pop(0)
        yield curr.value
        if curr.left_child: astore.append(curr.left_child)
        if curr.right_child: astore.append(curr.right_child)       

二叉搜索树完整代码:(链表实现)

class TreeNode:
    """ 二叉搜索树的节点 """
    def __init__(self,value):
        self.value = value
        self.left_child = None
        self.right_child = None


class BSTree:
    """ 二叉搜索树 """
    def __init__(self):
        self.root = None
        self._size = 0

    def is_empty(self):
        """ 判断是否是空树,返回布尔类型 """
        return self.root == None

    def size(self):
        """ 统计树有多少节点 """
        return self._size

    def clear(self):
        """ 清空树 """
        self.root = None

    def add_node(self,item):
        """ 添加节点 """
        def recurse_node(node):
            if item < node.value:
                if not node.left_child: node.left_child = TreeNode(item)
                else: recurse_node(node.left_child)
            elif not node.right_child: node.right_child = TreeNode(item)
            else: recurse_node(node.right_child)

        if self.is_empty(): self.root = TreeNode(item)
        else: recurse_node(self.root)
        self._size += 1

    def find_node(self,item):
        """ 查找节点,返回布尔类型 """
        def recurse(node):
            if not node: return False
            elif item == node.value: return True
            elif item > node.value: return recurse(node.right_child)
            else: return recurse(node.left_child)
        return recurse(self.root)
    

    def remove_node(self,item):
        """ 删除节点 """

        def deleteNode(node):
            """ 递归寻找删除节点。返回子树 """
            # 没有删除的节点,返回None
            if not node: return
            # 找到删除的节点,执行删除函数delete()
            if node.value == item:
                node = delete(node)
            # 当前节点小于删除的节点,递归当前节点的右子节点
            elif node.value < item: node.right_child = deleteNode(node.right_child)
            # 当前节点大于删除的节点,递归当前节点的左子节点
            else: node.left_child = deleteNode(node.left_child)
            return node

        def delete(node):
            """ 删除节点。返回删除后的子树。
                没有子节点,直接删除;有一个子节点时,子节点直接替代删除节点;
                有两个节点时,直接前驱直接替代删除节点,并删除原直接前驱 """
            # 没有左子节点也没有右子节点,删除的节点返回None
            if not node.left_child and not node.right_child: return
            # 有左子节点,删除的节点由其左子节点替代
            if node.left_child and not node.right_child: return node.left_child
            # 有右子节点,删除的节点由其右子节点替代
            if node.right_child and not node.left_child: return node.right_child
            # 既有左子节点又有右子节点,找到删除节点的直接前驱,原删除节点由直接前驱替代,原直接前驱的子节点直接为其父节点的子节点
            parent = node
            curr = node.left_child
            while curr.right_child:
                parent = curr
                curr = curr.right_child
            node.value = curr.value
            if parent != node: parent.right_child = curr.left_child
            else: node.left_child = curr.left_child
            return node
        # 根节点接收删除后的树
        self.root = deleteNode(self.root)

    def trim(self,low,high):
        """ 修剪元素,low<=元素值<=high """
        def recurse(node):
            if not node: return
            if node.value < low: return recurse(node.right_child)
            if node.value > high: return recurse(node.left_child)
            node.left_child = recurse(node.left_child)
            node.right_child = recurse(node.right_child)
            return node
        return recurse(self.root)
    
    def pre_order(self):
        """ 前序遍历,返回迭代器 """
        alist = []
        def recurse(node):
            if not self.is_empty():
                alist.append(node.value)
                if node.left_child: recurse(node.left_child)
                if node.right_child: recurse(node.right_child)
        recurse(self.root)
        return iter(alist)

    def mid_order(self):
        """ 中序遍历,返回迭代器 """
        alist = []
        def recurse(node):
            if not self.is_empty():
                if node.left_child: recurse(node.left_child)
                alist.append(node.value)
                if node.right_child: recurse(node.right_child)
        recurse(self.root)
        return iter(alist)
    

    def post_order(self):
        """ 后序遍历,返回迭代器 """
        alist = []
        def recurse(node):
            if not self.is_empty():
                if node.left_child: recurse(node.left_child)
                if node.right_child: recurse(node.right_child)
                alist.append(node.value)
        recurse(self.root)
        return iter(alist)
    

    def breadth_order(self):
        """ 广度遍历/层级遍历,返回迭代器 """
        if self.is_empty(): return
        astore = [self.root]
        while astore:
            curr = astore.pop(0)
            yield curr.value
            if curr.left_child: astore.append(curr.left_child)
            if curr.right_child: astore.append(curr.right_child)  
    

if __name__ == "__main__":
    tree = BSTree()
    print("空树:",tree.is_empty())
    print("节点数:",tree.size())
    
    tree.add_node("H")
    tree.add_node("D")
    tree.add_node("F")
    tree.add_node("A")
    tree.add_node("E")
    tree.add_node("Q")
    tree.add_node("M")
    tree.add_node("O")
    print("空树:",tree.is_empty())
    print("节点数:",tree.size())

    for x in tree.pre_order():
        print(x,end=" ")
    print("前序遍历")
    for y in tree.mid_order():
        print(y,end=" ")
    print("中序遍历")
    for z in tree.post_order():
        print(z,end=" ")
    print("后序遍历")
    for k in tree.breadth_order():
        print(k,end=" ")
    print("广度遍历/层级遍历")

    # tree.trim("F","O")
    print("查找节点",tree.find_node("H"))
    tree.remove_node("H")
    for x in tree.pre_order():
        print(x,end=" ")
    print("前序遍历(修剪或删除节点后)")
    for y in tree.mid_order():
        print(y,end=" ")
    print("中序遍历(修剪或删除节点后)")
    for z in tree.post_order():
        print(z,end=" ")
    print("后序遍历(修剪或删除节点后)")
    for k in tree.breadth_order():
        print(k,end=" ")
    print("广度遍历/层级遍历(修剪或删除节点后)")
    tree.clear()
    print("空树(清空树后):",tree.is_empty())

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值