通用算法 - [树结构] -二叉树的一些基本操作(二)

本文介绍了二叉树的常见操作,包括递归和非递归遍历、查找二叉树的子结构、求和为特定值的路径、寻找最大路径和以及实现二叉树的镜像转换。通过详细解释和代码示例阐述了这些操作的逻辑和实现方法。
摘要由CSDN通过智能技术生成

1、前言

数组、链表、二叉树、动态规划、栈与队列是面试中常考的知识点,而在这几个知识点中,二叉树属于难度比较大的部分,因此在这里总结一下二叉树常考的操作,包括:
(1)二叉树的递归遍历;
(2)二叉树的非递归遍历;
(3)求二叉树叶子节点的个数;
(4)求二叉树的深度;
(5)二叉树的子结构;
(6)求二叉树中和为某个值的路径;
(7)求二叉树中的最大路径和;
(8)二叉树的镜像;
(9)判断某个序列是否是二叉搜索树的后序遍历序列;
(10)二叉搜索树和双向链表的相互转换;
(11)树中两个结点的最近祖先;
(12)二叉树搜索树的插入与删除;
(13)堆的插入与删除;
(14)判断一棵树是不是平衡二叉树;
(15)平衡二叉树的调整;

2、二叉树的基本操作

(5)二叉树的子结构

基本思想:判断二叉树A中是否存在某个子结构,该子结构和二叉树B相同。
比如树A和树B分别如下所示:

二叉树A:
在这里插入图片描述
二叉树B:
在这里插入图片描述
当要在二叉树 A A A中查找某个子结构 B B B时,需要两步:第一步,在树 A A A中找到与树 B B B的根节点相同的节点 B ′ B' B;第二步,判断以 B ′ B' B为根节点的子树是否与树 B B B相同。

对于第一步,在树 A A A中找到与树 B B B根节点相等的节点 B ′ B' B,我们可以
(1) 先比较 A A A的某个节点 B ′ B' B和树 B B B的根节点值是否相等;如果相等,判断以 B ′ B' B为根节点的子树是否和树 B B B相同,并将结果记为 i s e x i s t isexist isexist
(2)如果 i s e x i s t = F a l s e isexist=False isexist=False,递归地判断 B ′ B' B的左子树是否和 B B B相同,更新 i s e x i s t isexist isexist的值;
(3)如果 i s e x i s t = F a l s e isexist=False isexist=False,递归地判断 B ′ B' B的右子树是否和 B B B相同,再一次更新 i s e x i s t isexist isexist的值。

对于第二步,判断以 B ′ B' B为根节点的子树和树 B B B是否相同,我们可以
如果 B B B为空返回 True;
如果 B ′ B' B为空,返回False;
如果 B B B B ′ B' B的值不相等,返回False;
比较 B B B的左子树 B ′ B' B的左子树是否相同,结果记为left_equal;
比较 B B B的左子树 B ′ B' B的右子树是否相同,结果记为right_equal;
返回left_equal && right_equal;

代码实现

def FindNode(treenodeA,treenodeB):

    isexist = False

    if treenodeA != None and treenodeB != None:

        if treenodeA.val == treenodeB.val:

            isexist = IsEqual(treenodeA,treenodeB)

        if isexist == False:

            isexist = FindNode(treenodeA.left,treenodeB)

        if isexist == False:

            isexist = FindNode(treenodeA.right,treenodeB)

    return isexist
    


def IsEqual(treenode1,treenode2):

    if treenode2 == None:

        return True

    if treenode1 == None:

        return False
    
 
    if treenode1.val != treenode2.val:

        return False
        
    left_equal = IsEqual(treenode1.left,treenode2.left)

    right_equal = IsEqual(treenode1.right,treenode2.right)

    return left_equal and right_equal

(6)求二叉树中和为某个值的路径

基本思路:二叉树的路径指的是从根节点到叶节点所经过节点形成的序列。我们使用一个栈来保存从根节点到当前节点的路径,如果当前节点为叶节点,则栈中的节点构成一条路径。每当首次访问一个节点时,将该节点进栈,当该节点的所有子节点都访问过之后,将该节点从栈中弹出。

代码实现

def PathSumEqualtoN_Recursion(all_path,rootnode,path,number):

    if rootnode == None:

        return

    path.append(rootnode)

    if rootnode.left == None and rootnode.right == None:

        nodeval = [node.val for node in path]

        if sum(nodeval) == number:

            all_path.append(nodeval)

    PathSumEqualtoN_Recursion(all_path,rootnode.left,path,number)

    PathSumEqualtoN_Recursion(all_path,rootnode.right,path,number)

    path.pop()

    return

def PathSumEqualtoN_NoRecursion(all_path,rootnode,number):

    if rootnode == None:

        return

    stack = []

    stack.append(rootnode)

    last_pop = rootnode

    while len(stack) != 0:

        curnode = stack[-1]

        #print("stack={}".format([node.val for node in stack])

        if curnode.left == None and curnode.right == None:

            nodeval = [node.val for node in stack]

            if sum(nodeval) == number:

                all_path.append(nodeval)

        if curnode.left != None and curnode.left != last_pop and curnode.right != last_pop:

            stack.append(curnode.left)

        elif curnode.right != None and curnode.right != last_pop and (curnode.left == None or curnode.left == last_pop):

            stack.append(curnode.right)

        else:

            stack.pop()

            last_pop = curnode

    return 

(7)求二叉树中的最大路径和

基本思路:这里的路径被定义为从二叉树中任意节点出发,到达另一个任意节点的所经过节点构成的序列。

先看几个简单的二叉树:

 (1)第一棵树
   1
  / \
 2   3
 (2)第二棵树
   1
  / \
 -2  3
 (3)第三棵树
   1
  / \
 2   -3
 (4)第四棵树
    1
   / \
- 2   -3

接着我们看从节点1出发到其子树能得到的最大路径max_gain(1),
对于第一棵树,max_gain(1) = 1 + 3 = 4;
对于第二棵树,max_gain(1) = 1 + 3 = 4;
对于第三棵树,max_gain(1) = 1 + 2 = 3;
对于第四棵树,max_gain(1) = 1 + 0 = 1;

综上,我们可以得到从一个节点node出发到其子树所能得到的最大路径=从node的左节点出发到其子树所能得到的最大路径、从node的右节点出发到其子树所能得到的最大路径、0,这三者中的最大值加上节点node的值
即:
m a x _ g a i n ( n o d e ) = n o d e . v a l + M a x { m a x _ g a i n ( n o d e . l e f t ) , m a x _ g a i n ( n o d e . r i g h t ) , 0 } max\_gain(node) = node.val + Max\{max\_gain(node.left),max\_gain(node.right),0\} max_gain(node)=node.val+Max{max_gain(node.left)max_gain(node.right)0}

根据上面得出的结论,我们可以计算出每一个二叉树从根节点出发到其子树所能得到的最大路径max_gain(root),但是max_gain(root)不一定是该二叉树的最大路径,比如(1)第一棵树,它的最大路径是是从root的左子树穿过root到达其右子树的一条路径,路径和为max_gain(root.left) + root.val + max_gain(root.right),

所以,在求出从根节点出发到其子树所能得到的最大路径max_gain(root)之后,还需要计算可能的最大路径prob_gain = max_gain(root.left) + root.val + max_gain(root.right),如果prob_gain > max_gain(root),则最大路径和为prob_gain,否则为max_gain(root)。

代码实现

max_path_sum = -float("inf")

def Max_gain(rootnode):

    if rootnode == None:

        return 0

    left_gain = Max_gain(rootnode.left)

    right_gain = Max_gain(rootnode.right)

    root_gain = rootnode.val + max(left_gain,right_gain,0)

    prob_gain = rootnode.val + left_gain + right_gain

    global max_path_sum

    max_path_sum = max(root_gain,prob_gain,max_path_sum)

    return root_gain

参考:leetcode - [二叉树] -(124)二叉树中的最大路径和

(8)二叉树的镜像
基本思路:二叉树的镜像可以通过递归地交换每个节点的左右子树得到,如下图所示:

(1)原二叉树 
   -10
   / \
  9  20
    /  \
   15   7
 (2)镜像
   -10
   / \
 20   9
/  \
7  15

代码实现

def Mirror_Tree(rootnode):

    if rootnode == None:

        return

    Mirror_Tree(rootnode.left)

    Mirror_Tree(rootnode.right)

    temp = rootnode.left

    rootnode.left = rootnode.right

    rootnode.right = temp

    return 

def Mirror_Tree_Nochange(rootnode):

    if rootnode == None:

        return None

    new_rootnode = TreeNode()

    new_rootnode.val = rootnode.val
    
    new_rootnode.right = Mirror_Tree_Nochange(rootnode.left)

    new_rootnode.left = Mirror_Tree_Nochange(rootnode.right)


    return new_rootnode

>>下一篇:通用算法 - [树结构] -二叉树的一些基本操作(三)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Albert_YuHan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值