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