python_8_二叉树_二叉排序树

递归序:每次递归完,一个节点要经过3次

1 二叉树操作

在普通的二叉树中,插入新节点不需要保持任何排序规则,只需按照层级顺序依次插入到空闲的位置即可。

1.1 非递归完成先序中序后序遍历

遍历方法使用迭代的方式实现,包括中序遍历、前序遍历和后序遍历。采用了栈的辅助结构来模拟递归过程,实现非递归的遍历过程。

class Node:
    def __init__(self,value):
        self.value = value
        self.left = None
        self.right = None

class Binarytree:     # 二叉树
    def __init__(self):
        self.root = None    # 初始化头指针,指向空

    def insert_node(self,value):
        new_node = Node(value)
        if not self.root:   # 如果树为空
            self.root = new_node
        else:               # 如果树不为空,queue只是表示有队列的意思,实际上还是列表
            queue = [self.root]    # 将头结点放入列表(队列)中,只加入头节点,用笔画画很好理解
            while queue:           # 头结点连着的元素
                curr = queue.pop(0)      # 将列表的首元素弹出
                if not curr.left:     # 如果弹出的这个结点左孩子是空
                    curr.left = new_node  # 则添加左孩子
                    return
                else:
                    queue.append(curr.left)    # 如果有左孩子,就先放入列表中
                if not curr.right:
                    curr.right = new_node
                    return
                else:
                    queue.append(curr.right)

    def inorder_traversal(self,root):           # 中序遍历
        if not root:
            return []
        result = []
        stack = []              # 准备一个栈
        curr = root             # 引用头结点的指针
        while curr or stack:    # 如果指针指向元素或栈有元素,继续执行
            while curr:         # 指针指向元素
                stack.append(curr)   # 这不是真的栈,只是模仿栈的定义,加入curr指向的结点
                curr = curr.left     # curr指向左孩子
            curr = stack.pop()       # curr此时指向栈中弹出元素,pop弹出列表最后一个元素
            result.append(curr.value)
            curr = curr.right
            return result

    def preorder_traversa(self,root):    # 先序遍历
        if not root:
            return []
        result = []
        stack = [root]   # 将头结点转为列表,结点都在列表里
        while stack:     # 栈不为空
            curr = stack.pop()    # curr指向栈弹出元素
            result.append(curr.value)
            if curr.right:
                stack.append(curr.right)
            if curr.left:
                stack.append(curr.left)
        return result

    def postorder_traversal(self,root): 		# 后序遍历
        if not root:
            return []
        result = []
        stack = [(root,False)]
        while stack:
            curr,visited = stack.pop()   # 给curr,visited统一指向弹出的结点组合,curr指向结点,visited赋值False或True
            if visited:    # 若结点是True
                result.append(curr.value)   # 就加入到列表中
            else:
                stack.append((curr,True))          # 给当前结点改成True
                if curr.right:          # 结点的右孩子存在,放入栈中
                    stack.append((curr.right,False))
                if curr.left:           # 结点的左孩子存在,放入栈中
                    stack.append((curr.left,False))
        return result

T = Binarytree()
T.insert_node(1)
T.insert_node(2)
T.insert_node(3)
print(T.postorder_traversal(T.root))

stack = [(root,False)]解释

在后序遍历中,对于任意一个节点,需要先遍历它的左子树和右子树,最后再访问该节点本身。为了满足这个顺序,使用栈作为辅助结构。初始时,将根节点和一个False标志位放入栈中。

后序遍历的算法如下:

1.当栈不为空时,从栈中弹出一个节点,并检查该节点的标志位。
2.如果标志位为True,表示该节点的右子树已经被访问过,可以将该节点的值加入结果列表中。
3.如果标志位为False,表示该节点的右子树还未被访问过,需要将该节点的右子树、左子树按照顺序依次放入栈中,并将该节点的标志位设为True。
重复步骤1到步骤3,直到栈为空。


1.2 删除为key值节点(递归)和找到后继节点

  1. 首先,找到要删除的节点。从根节点开始,在二叉树中递归搜索与要删除的节点值匹配的节点。
  2. 找到要删除的节点后,根据其子节点的情况执行不同的操作:
  • 如果要删除的节点是叶子节点(没有左子节点和右子节点),直接将其从其父节点的左子树或右子树中删除。
  • 如果要删除的节点只有一个子节点,将该子节点提升到要删除的节点的位置上。
  • 如果要删除的节点有两个子节点,可以选择用该节点的前驱或后继节点来替代它,然后再递归删除用于替代的节点。
    # 删除值为key的结点
    def delete_node(self,root,key):
        if not root:
            return root

    # 寻找要删除的结点
        if root.value == key:
            # 节点为叶子节点或只有一个子节点
            if not root.left:
                return root.right
            elif not root.right:
                return root.left

            # 节点有两个子节点,用后继节点代替
            successor = self.get_successor(root.right)
            root.value = successor.value
            root.right = self.delete_node(root.right,successor.value)
        else:
            # 继续在左子树和右子树中寻找要删除的节点
            root.left = self.delete_node(root.left,key)
            root.right = self.delete_node(root.right,key)
        return root

    def get_successor(self,node):
        while node.left:
            node = node.left
        return node

1.4 直接删除节点(递归)

在函数中首先判断了树是否为空。如果树不为空,则递归地删除左子树和右子树中的最后一个节点,并通过将其父节点的对应子节点设为 None 来删除。
这个什么树都能用

   def delete_last_node(self,root):
        if not root:
            return root

        # 判断是否为叶子节点
        if not root.left and not root.right:  # 左右孩子都为空
            return None

        # 递归删除左子树和右子树中的最后一个节点
        root.left = self.delete_last_node(root.left)
        root.right = self.delete_last_node(root.right)

        return root

1.5 直接删除节点(非递归)

  1. 首先,判断二叉树是否为空。如果为空,则无需进行删除操作。
  2. 如果不为空,定义一个栈(stack)用于存储要遍历的节点。将根节点压入栈中。
  3. 使用 while 循环,每次弹出栈顶元素,判断其是否为叶子节点。如果是叶子节点,则将其父节点的相应子节点设为 None。如果不是叶子节点,则将其左右子节点依次压入栈中。
  4. 循环直到栈为空为止。

这个什么树都能用

    def delete_last_node(self, root):
        if not root:
            return root
        stack = [root]      # 以栈储存
        while stack:
            node = stack.pop()  # 将首位节点弹出
            if not node.left and not node.right:
                # 如果节点为叶子节点,则删除其父节点对应的相应子节点
                parent = self.find_parent(root,node)      # 找到node的父节点
                if parent:
                    if parent.left == node:
                        parent.left = None
                    else:
                        parent.right = None
            else:
                # 如果节点不是叶子节点,则将其左右子节点压入栈中
                if node.left:
                    stack.append(node.left)
                if node.right:
                    stack.append(node.right)
        return root



    # 找到node节点的父节点
    def find_parent(self,root,node):
        if not root or not node:   # 头结点为空或当前节点为空
            return None
        if root == node:         # 当前节点是父节点
            return None
        if root.left == node or root.right == node:
            return root

        left_parent = self.find_parent(root.left,node)
        if left_parent:
            return left_parent

        right_parent = self.find_parent(root.right,node)
        if right_parent:
            return right_parent

        return None

以上代码实现了 delete_last_node 函数,该函数定义了一个栈来实现循环遍历,并且使用 find_parent 函数来查找当前节点的父节点。在 while 循环中,首先弹出栈顶元素,并判断其是否为叶子节点,如果是叶子节点,则删除其父节点的相应子节点;否则,将其左右子节点压入栈中,以便后续遍历

1.6 实现二叉树按层遍历

  1. 宽度优先遍历,用队列

  2. 可以通过设置flag变量的方式,来发现某一层的结束

  3. 宽度优先遍历,用队列

    # 二叉树按层遍历
    def leve_traversal(self,root):
        if not root:
            return []
        queue = [root]   # 队列放头结点,列表用好了真不错
        result = []
        while queue:
            cur = queue.pop(0)
            result.append(cur.value)
            if cur.left:
                queue.append(cur.left)
            if cur.right:
                queue.append(cur.right)
        return result
  1. 可以通过设置flag变量的方式,来发现某一层的结束
    哪一层的节点最多
    方案一:用map
    在这里插入图片描述
 # 哪一层节点数量最多
    def maxwidthMap(self,root):
        if not root:
            return 0
        queue = [root]
        # key在哪一层,value都存在map里
        levelMap = dict()
        levelMap.update({root:1})   # 头结点在第一层
        curLevel = 1         # 当前你正在统计哪一层的宽度
        curLevelNodes = 0    # 当前层curLevel层,宽度目前是多少,是0是因为一个节点出来后再加到宽度上去
        max = 0
        while queue:
            cur = queue.pop(0)    # 取第0个位置的数,模仿先进先出
            curNodeLevel = levelMap.get(cur)      # 取出当前节点层数
            if cur.left:         # 如果当前节点左孩子不为空
                levelMap.update({cur.left:curNodeLevel+1})     # 将左孩子放入字典,对应层数+1
                queue.append(cur.left)                         # 把左孩子放入队列中
            if cur.right:
                levelMap.update({cur.right:curNodeLevel+1})    #
                queue.append(cur.right)

            # 如果当前节点所在层数和目前统计层数一样,就是当前层环没有结束。如果不一样就是当前层结束了
            if curNodeLevel == curLevel:      # 一样,max不更新
                curLevelNodes = curLevelNodes + 1   # 当前层的节点数++
            else:           # 有最新层才能更新max,最后一层没有更新max,新层到来结算老层
                if max < curLevelNodes:
                    max = curLevelNodes
                curLevel = curLevel + 1         # 当前层数++
                curLevelNodes = 1               # 新层的节点数设置为1

        if max < curLevelNodes:        # 最后一层更新max机制
            max = curLevelNodes
        return max

方案2:用队列
在这里插入图片描述

    # 哪一层节点数量最多
    def maxwidthMap(self,root):
        if not root:
            return 0
        queue = [root]
        # key在哪一层,value都存在map里
        curEnd = root      # 当前层的最右节点是谁
        nextEnd = None     # 下一层的最右节点是谁
        curLevelNodes = 0    # 当前层的节点数
        max = 0
        while queue:
            cur = queue.pop(0)    # 取第0个位置的数,模仿先进先出
            if cur.left:         # 如果当前节点左孩子不为空
                queue.append(cur.left)                         # 把左孩子放入队列中
                nextEnd = cur.left        # nextEnd 总是往右动
            if cur.right:
                queue.append(cur.right)
                nextEnd = cur.right
            curLevelNodes = curLevelNodes + 1   # 当前层的节点数++
            if cur == curEnd:               # 如果当前节点是这一层最右的节点,就结算
                if max < curLevelNodes:
                    max = curLevelNodes
                    curLevelNodes = 0      # 开始下一层的节点数
                    curEnd = nextEnd       # 下一层的最右节点赋值给当前层节点,提前发现下一层的尾部
        return max

1.7 二叉树的序列化和反序列化

序列化(serialization)是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。
在这里插入图片描述

    # 二叉树先序序列化
    def preSerial(self,root):
        queue = []
        self.pers(root,queue)     # 把头结点和队列传过去
        return queue

    def pers(self,root,queue):
        if not root:
            queue.append(None)   # 放空进去
        else:                    # 和先序遍历差不多
            queue.append(str(root.value))      # 将值变成字符串的形式放入队列中,调这个顺序变成中,后遍历
            self.pers(root.left,queue)
            self.pers(root.right,queue)

    # 二叉树反序列化,将树再连好
    def buildByPreQueue(self,prelist):     # prelist是序列化后的列表
        if prelist is None or len(prelist) == 0:
            return None
        return self.preb(prelist)

    def preb(self,prelist):       # 反先序序列化
        value = prelist.pop(0)
        if value is None:
            return None         # 建出空树返回
        head = Node(value)                # 将string类型的值转成节点类型,调这个顺序变成中,后遍历
        head.left = self.preb(prelist)
        head.right = self.preb(prelist)
        return head

1.8 按层序列化

    # 按层序列化,死活都要序列化,但只有不空才去加队列
    def levelSerial(self,root):
        ans = []     # ans放序列化后的结果
        if not root:
            ans.append(None)
        else:
            ans.append(str(root.value))
            queue = [root]          # queue以队列放节点
            while queue:
                root = queue.pop(0)
                if root.left:
                    ans.append(str(root.left.value))   # 又序列化
                    queue.append(root.left)            # 又队列
                else:
                    ans.append(None)      # 左孩子为空,只加序列,不加队列
                if root.right:
                    ans.append(str(root.right.value))
                    queue.append(root.right)
                else:
                    ans.append(None)
        return ans


    # 反水平序列化,节点为空也要建,死活要建节点,不空才加队列
    def buildByLevelQueue(self,levelList):
        if levelList is None or len(levelList) == 0:
            return None

        head = self.generateNode(levelList.pop(0))
        queue = []
        if head:
            queue = [head]

        while queue:
            node = queue.pop(0)
            node.left = self.generateNode(levelList.pop(0))
            node.right = self.generateNode(levelList.pop(0))
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

        return head

    def generateNode(self,value):   # 给字符串,返回一个节点
        if value is None:
            return None
        return Node(value)

T = Binarytree()
T.insert_node(1)
T.insert_node(2)
T.insert_node(3)
T.insert_node(4)
T.insert_node(5)
T.insert_node(6)
S = T.levelSerial(T.root)
N = T.buildByLevelQueue(S)
print(T.leve_traversal(N))

2 二叉排序树

对于插入节点,我们需要做的是遍历整个二叉树,找到恰当的位置将节点插入。具体实现中,如果当前节点的值小于待插入节点的值,则去左子树继续查找;否则去右子树查找。直到找到一个没有子节点的位置,将新节点插入到这个位置上。

2.1 递归完成前中后序遍历

# 二叉排序树
class TreeNode:
    def __init__(self,value):
        self.value = value
        self.left = None
        self.right = None

class BinaryTree:
    def __init__(self):
        self.root = None

    def insert_node(self,value):
        new_node = TreeNode(value)
        if not self.root:         # 若树没有任何值
            self.root = new_node
        else:                     # 如果树有结点
            curr = self.root      # 指针指向头结点
            while True:
                if value < curr.value:    # 输入的值大于当前结点的值
                    if curr.left:         # 如果做孩子存在
                        curr = curr.left   # 指针指向做孩子
                    else:                      # 如果没有做孩子
                        curr.left = new_node  # 当前结点的左指针指向新结点
                        break
                elif value > curr.value:    # 如果当前值大于当前结点
                    if curr.right:          # 右孩子存在
                        curr = curr.right
                    else:
                        curr.right = new_node
                        break

    def inorder_traversal(self, root):    # 中序遍历
        if not root:
            return []
        result = []
        result += self.inorder_traversal(root.left)
        result.append(root.value)
        result += self.inorder_traversal(root.right)
        return result

    def preorder_traversal(self, root):    # 前序遍历
        if not root:
            return []
        result = []
        result.append(root.value)
        result += self.preorder_traversal(root.left)
        result += self.preorder_traversal(root.right)
        return result

    def postorder_traversal(self, root):    # 后序遍历
        if not root:
            return []
        result = []
        result += self.postorder_traversal(root.left)
        result += self.postorder_traversal(root.right)
        result.append(root.value)
        return result


T = BinaryTree()
T.insert_node(1)
T.insert_node(2)
T.insert_node(3)

print(T.inorder_traversal(T.root))

非递归上面能用

2.2 删除值为key的节点

实现二叉树的删除节点操作:

  • 找到需要删除的节点:从根节点开始,根据要删除的节点的值和当前节点的值进行比较,如果相等则找到了要删除的节点,否则根据比较结果继续向左子树或右子树进行搜索。
  • 根据要删除的节点所处的情况进行不同的处理:
  • 如果要删除的节点是叶子节点(没有左子节点和右子节点),直接删除即可。
  • 如果要删除的节点只有一个子节点,将其子节点提升到要删除的节点的位置上。
  • 如果要删除的节点有两个子节点,可以选择用该节点的前驱或后继节点来替代它,然后再递归删除用于替代的节点。
   # 删除值为key的结点
    def delete_node(self,root,key):   # 所删除结点为key的值
        if not root:
            return root
        if key < root.value:
            root.left = self.delete_node(root.left,key)
        elif key > root.value:
            root.right = self.delete_node(root.right,key)
        else:
            # 结点为叶子结点或只有一个子节点
            if not root.left:
                return root.right
            if not root.right:
                return root.left
            # 结点只有两个子节点,用后继节点替代
            successor = self.get_successor(root.right)
            root.value = successor.value
            root.right = self.delete_node(root.right,successor.value)
        return root

    # 查找结点的后继结点
    def get_successor(self,node):
        while node.left:
            node = node.left
        return node
  • 在上面的代码中,delete_node 方法接收一个参数 key,表示要删除的节点的值。如果树中存在该节点,则通过递归调用删除节点,并根据节点的情况进行相应的操作。
  • get_successor 方法用于查找给定节点的后继节点。后继节点是右子树中的最小节点,它可以用来替代有两个子节点的节点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值