树专题

1、树基本概念

给定一个无向图G,如果:i)G是连通的;ii)G是无环的,则G是一棵树。
  1. 满二叉树:叶子节点全都在最底层,除了叶子节点之外,每个节点都有左右两个子节点
  2. 完全二叉树:叶子节点都在最底下两层,最后一层的叶子节点都靠左排列,并且除了最后一层,其他层的节点个数都要达到最大
  3. 二叉查找树:Binary Search Tree,二叉查找树要求,在树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,而右子树节点的值都大于这个节点的值
  4. 平衡查找树:二叉树中任意一个节点的左右子树的高度相差不能大于 1
  5. 平衡二叉查找树:平衡二叉查找树不仅满足上面平衡二叉树的定义,还满足二叉查找树的特点

2、模板

树的基本遍历(递归和非递归模板)

class TreeNode:
    def __init__(self,val):
        self.val=val
        self.left=None
        self.right=None
        
def pre(root):
    if not root:
        return []
    
    return [root.val]+pre(root.left)+pre(root.right)


def mid(root):
    if not root:
        return []
    
    return mid(root.left)+[root.val]+mid(root.right)

def post(root):
    if not root:
        return []
    
    return post(root.left)+post(root.right)+[root.val]
    
        
def pre_dfs(root):
    ''' 使用循环的方法实现, 中左右 在这里需要用到栈来保存未遍历的右子树
        
       需要存储结构
       s:栈,依次将左中右进栈
       r:记录访问值,(这个可以不需要,直接打印)
       
       1、左子树边进栈边访问;
       2、当左子树到叶子节点时,向上回溯获取右子树,循环‘中左右’的过程
    
    '''
    s=[]
    r=[]
    
    c=root
    
    while c or len(s)>0:
        while c:
            r.append(c.val)    #遍历根和左子树时就访问当前节点
            s.append(c)
            c=c.left           
        if len(s)>0:           #当访问到左子树的叶子节点,弹出栈 
            c=s.pop().right 
    return r

def in_dfs(root):
    '''左中右
       需要存储结构
       s:栈,依次将左中右进栈
       r:记录访问值,(这个可以不需要,直接打印)
       1、左子树先进栈
       2、左子树为空,s弹出,访问,右子树进栈;循环左子树进栈访问过程
    
    '''
    s=[]
    r=[]
    c=root
    
    while c or len(s)>0:
        while c:
            #左子树进栈
            s.append(c)
            c=c.left
        
        #当c为空时,说明已经到达左子树最下边,这时需要出栈了
        if len(s)>0:
            p=s.pop()
            r.append(p.val)
            # 进入右子树,开始新的一轮左子树遍历
            c=p.right
    return r


def post_dfs(root):
    '''
       左右中
       需要存储结构
       s:栈,依次将左右中进栈
       lastVisit:记录上次被访问的节点
       r:记录访问值,(这个可以不需要,直接打印)
       1、先将所有左子树压入栈,直到叶子节点
       2、弹出栈,当前节点的右子树为空或者上一个访问的节点为其右孩子,则访问当前节点
       3、否则,遍历其右子树(重复1过程,左子树进栈)
    '''
    r=[]
    s=[]
    c=root
    lastVisit=None
    
    while(c):
        s.append(c)
        c=c.left
    
    while(len(s)>0):
        #走到这,c是空,并已经遍历到左子树底端
        c=s.pop()
        #一个根节点被访问的前提是:无右子树或右子树已被访问过
        if(c.right==None or c.right==lastVisit):
            r.append(c.val)
            lastVisit=c
        else:
            #else表示上次访问的节点为左子树且右子树不为空;将当前节点压入栈,遍历其右子树
            s.append(c)
            c=c.right
            while c:
                s.append(c)
                c=c.left

#层次遍历
#声明一队列,循环遍历
def level(root):
    res=[]
    if not root:
        return res
    
    s=[root]
    while (len(s)>0):
        s_len=len(s)
        for i in range(s_len):
            cur=s.pop(0)
            res.append(cur.val)
            if cur.left:
                s.append(s.left)
            if cur.right:
                s.append(s.right)
    return res

3、实例

94,144,145可以参考模板解法

94. 二叉树的中序遍历

难度:中等

题目描述

给定一个二叉树,返回它的中序 遍历。

示例 1:

输入: [1,null,2,3]
   1
    \
     2
    /
   3

输出: [1,3,2]

可参见模板中的两种解法

144. 二叉树的前序遍历

难度:中等

题目描述

给定一个二叉树,返回它的 前序 遍历。

示例 1:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3

输出: [1,2,3]

145. 二叉树的后序遍历

难度:困难

题目描述

给定一个二叉树,返回它的 后序 遍历。

示例 1:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,2,1]

98. 验证二叉搜索树

难度:中等

题目描述

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

假设一个二叉搜索树具有如下特征:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

示例 1:

输入:
    2
   / \
  1   3
输出: true

示例 2:

输入:
    5
   / \
  1   4
     / \
    3   6
输出: false
解释: 输入为: [5,1,4,null,null,3,6]。
     根节点的值为 5 ,但是其右子节点值为 4 。

中序遍历时,判断当前节点是否大于中序遍历的前一个节点,如果大于,说明满足 BST,继续遍历;否则直接返回 false。

class TreeNode:
    def __init__(self,v):
        self.left=None
        self.roght=None
        self.val=v
        
#中序遍历,顺序排列,当前节点大于上一个节点
def isValidBST(root):
    pre=float('-inf')
    if root is None:
        return True
    dfs(root)
    
    def dfs(root):
        if root is None:
            return True
        res=dfs(root.left)
        if(root.val<pre):
            return False
        pre=root.val
        dfs(root.right)
        return res
        
 
#通过上下界来判断是否满足条件[upper,lower]
'''
root.val>左子树的最大值(max)
root.val<右子树的最小值(min)
迭代左子树更新最大值
迭代右子树更新最小值
'''

def isValidBST2(root):
    if root is None:
        return True
    res=dfs(root)
    def dfs(root,upper=float('inf'),lower=float('-inf')):
        if root is None:
            return True
        val=root.val
        if(val>upper or val<lower):
            return False
        
        return dfs(rool.left,val,lower) and dfs(root.right,upper,val)
        
    return res
        
class Solution:
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        def helper(node, lower = float('-inf'), upper = float('inf')):
            if not node:
                return True
            
            val = node.val
            if val <= lower or val >= upper:
                return False

            if not helper(node.right, val, upper):
                return False
            if not helper(node.left, lower, val):
                return False
            return True

        return helper(root)

99. 恢复二叉搜索树

难度:困难

题目描述

二叉搜索树中的两个节点被错误地交换。

请在不改变其结构的情况下,恢复这棵树。

示例 1:

输入: [1,3,null,null,2]

   1
  /
 3
  \
   2

输出: [3,1,null,null,2]

   3
  /
 1
  \
   2

示例 2:

输入: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

输出: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3
'''
思路:
1、寻找错误交换的节点
2、交换两节点对应的值

'''
class TreeNode:
    def __init__(self,v):
        self.left=None
        self.roght=None
        self.val=v

def recover(root):
    if root is None:
        return
    
    first=None
    second=None
    pre= TreeNode(float('inf'))
    
    def dfs(root):
        if root is None:
            return 
        dfs(root.left)
        if (pre.val>root.val and not first):
            first=pre
        elif (pre.val>root.val and not second):
            second=root
        pre=root
        dfs(root.right)
    first.val,second.val=second.val,first.val

100. 相同的树

难度:简单

题目描述

给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

示例 1:

输入:       1         1
          / \       / \
         2   3     2   3

        [1,2,3],   [1,2,3]

输出: true
示例 2:

输入:      1          1
          /           \
         2             2

        [1,2],     [1,null,2]

输出: false
示例 3:

输入:       1         1
          / \       / \
         2   1     1   2

        [1,2,1],   [1,1,2]

输出: false
'''
思路:递归遍历两棵树,比较值是否相等
'''

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not p and not q:
            return True
        elif not p or not q:
            return False
        elif p.val != q.val:
            return False
        else:
            return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)

'''
时间复杂度:
O(min(m,n)),m,n分别是两个二叉树的节点数。对两个二叉树同时进行深度优先搜索,只有当两个二叉树中的对应节点都不为空时才会访问到该节点,因此被访问到的节点数不会超过较小的二叉树的节点数。

空间复杂度:

O(min(m,n)),其中 m,n分别是两个二叉树的节点数。空间复杂度取决于递归调用的层数,递归调用的层数不会超过较小的二叉树的最大高度,最坏情况下,二叉树的高度等于节点数。
'''

101. 对称二叉树

难度:简单

题目描述

给定一个二叉树,检查它是否是镜像对称的。

示例 1:

    1
   / \
  2   2
 / \ / \
3  4 4  3

示例 2:

    1
   / \
  2   2
   \   \
   3    3
def ismirror(root):
    if not root:
        return True
    
    return dfs(root.left,root.right)
    
    def dfs(r1,r2):
        if not r1 and not r2:
            return True
        elif not r1 or not r2:
            return False
        elif r1.val!=r2.val:
            return False
        else :
            return dfs(r1.left,r2.right) and dfs(r1.right,r2.left)

102. 二叉树的层序遍历

难度:中等

题目描述

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)

示例 1:

示例:
二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

见模板

124. 二叉树中的最大路径和

难度:困难

题目描述

给定一个非空二叉树,返回其最大路径和。

本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。

示例 1:

输入: [1,2,3]

       1
      / \
     2   3

输出: 6
示例 2:

输入: [-10,9,20,null,null,15,7]

   -10
   / \
  9  20
    /  \
   15   7

'''
计算二叉树的最大路径和

思路:
1、定义每个节点的最大值和,叶子结点为其本身,非叶子节点为cur.val+max(cur.left,cur.right)
2、设置local_max,golbal_max记入局部和全局最大值

'''

def maxSum(root):
    local_max=float('-inf')
    golbal_max=float('-inf')
    
    if not root:
        return 0
    
    def dfs(root):
        if not root:
            return 0
        
        local_max=root.val+max(dfs(root.left),0)+max(dfs(root.right),0)
        golbal_max=max(golbal_max,local_max)
        return local_max
    
    return golbal_max

236. 二叉树的最近公共祖先

难度:中等

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明

  • 所有节点的值都是唯一的。 p、q 为不同节点且均存在于给定的二叉树中。
'''
236二叉树的最近公共祖先
1、若x为p,q的公共祖先,则满足p且q在x的左右子树中
2、若x为p,q的公共祖先,则满足x=(p or q) 且 p且q在x的左右子树中

'''

def isLowestAncestor(root,p,q):
    ans=None
    if not root:
        return res
    
    def dfs(root,p,q):
        if not root:
            return False
        
        cur=(root.val==p.val or root.val==q.val) and (dfs(root.left,p,q) or dfs(root.right,p,q))
        ncur=dfs(root.left,p,q) or dfs(root.right,p,q)
        if cur or ncur:
            ans=root
        return cur or ncur
    return res

543. 二叉树的直径

难度:困难

题目描述

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

示例 1:

给定二叉树

          1
         / \
        2   3
       / \     
      4   5    
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。

注意

两结点之间的路径长度是以它们之间边的数目表示。

'''
二叉树的最大直径
1、定义每个节点的最大直径,节点的直径等于(左子树的深度+右子树的深度+1)
2、设置local_max,golbal_max记入局部和全局最大值

'''

def maxDilmeter(root):
    if not root:
        return 0
    global_max=float('-inf')
    local_max=float('-inf')
    
    def dfs(root):
        if not root:
            return 0
        l=dfs(root.left)
        r=dfs(root.right)
    
        local_max=l+r+1
        global_max=max(global_max,local_max)
        return max(l,r)+1
    return global_max

https://www.cnblogs.com/jianglinliu/p/11197715.html(树的深度)

https://blog.csdn.net/z_ryan/article/details/80854233

https://zhuanlan.zhihu.com/p/24885173

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值