序言
关于树和回溯题目汇总,好好干饭,好好努力,好好加油哦!
题目一:二叉树返回所有节点值
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)
二叉树:[3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
输出:
[
[3],
[9,20],
[15,7]
]
思路:
将每一层的数字都录进去,这里需要两个列表来做,
一个stack是录入每一层的数字,然后变空集再录入另一层。
那么怎么进入下一层?? 比如 stack现在只有一个头node,那么 下一层便是 node的左边和右边。 收入
另一个ans 是录入stack的每一层东西,最后返回ans
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
def levelOrder(root: TreeNode):
#如果root是空,则返回[]
if not root: return []
stack = []
stack.append(root)
ans = []
while stack:
#收集stack里面的每一层元素
ans.append(node.val for node in stack)
#把上一层的数字放入tmp里,然后就清空stack,准备循环再次收录下一层
tmp = stack
stack = []
for node in tmp:
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
return ans
题目二:二叉树的右视图
给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
二叉树的右视图
输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:
1 <---
/ \
2 3 <---
\ \
5 4 <---
思路是:
把每一层的最后一个node.val 收录入ans ,做法跟上面差不多,只是在收录的时候只要最后一个
def rightSideView(root):
if not root: return []
stack = []
stack.append(root)
ans = []
while stack:
list_ = [node.val for node in stack]
ans.append(list_[-1])
tmp = stack
stack = []
for node in tmp:
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
return ans
题目三:从后往前录入树的值
lt107: 给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历
给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
输出:
[
[15,7],
[9,20],
[3]
]
思路:跟上面做的题也差不多,只要最后用[::-1] 倒过来即可。
def reverse(root):
if not root: return []
stack = []
stack.append(root)
ans = []
while stack:
ans.append([node.val for node in stack])
tmp = stack
stack = []
for node in tmp:
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
return ans[::-1]
题目四:寻找树中的最大值
lt515: 需要在二叉树的每一行中找到最大的值
输入:
1
/ \
3 2
/ \ \
5 3 9
输出: [1, 3, 9]
思路:这也很简单,跟右视图差不多,返回每一层的最大值即可
def largestValues(root):
if not root: return []
stack = []
stack.append(root)
ans = []
while stack:
list_ = [node.val for node in stack]
ans.append(max(list_))
tmp = stack
stack = []
for node in tmp:
if node.left: stack.append(node.left)
if node.right: stack.append(node.right)
return ans
题目五:N叉树的前序遍历
N叉树的前序遍历
给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。
树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)
输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]
这里区别是N叉树 看看N叉树的定义是
class Node:
def __init__(self, val=None, children=None):
self.val = val
self.children = children
def levelOrder(root):
if not root: return []
stack = []
stack.append(root)
ans = []
while stack:
ans.append([node.val for node in stack])
tmp = stack
stack = []
for node in tmp:
if node.children:
for child in node.children:
stack.append(child)
return ans
题目六:检查是否相同的树
lt100:给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
输入: 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
只有最后q和q都同时变成None的时候,才会返回True 这时候是相同的树
如果有一个先变成None,那也是返回False
def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
if p is None and q is None: return True
if p is None or q is None: return False
if p.val != q.val: return False
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
题目七:检查树木是否对称
题目lt101:给定一个二叉树,检查它是否是镜像对称的
输入:
1
/ \
2 2
/ \ / \
3 4 4 3
输出:
True
输入:
1
/ \
2 2
\ \
3 3
输出:
True
思路是
之前我们比较过来两棵树是否相同,只要定义跟上面差不多即可。
把头节点移动之后,比较分开的两棵树 是否对称
def isSymmetric(self,root):
if not root: return True
def checkSame(p1,p2):
if p1 is None and p2 is None: return True
if p1 is None or p2 is None: return False
if p1.val != p2.val: return False
return checkSame(p1.left, p2.right) and checkSame(p1.right,p2.left)
return checkSame(root.left,root.right)
回忆如何检查两棵树是否相同的code,大同小异。
题目八:树的深度
题目:给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点
3
/ \
9 20
/ \
15 7
输出:
3
最长深度是3
思路是
每一层都会循环一次 看循环多少次 就是我们需要求的深度
并且要max(左边树能到的最深长度,右边树能到的最深长度) + 1 因为还有root那一层
def maxDepth(self, root: TreeNode) -> int:
if not root: return 0
return max(self.maxDepth(root.left)+1, self.maxDepth(root.right) + 1 )
题目九:括号生成
lt22. 括号生成:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合
输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]
输入:n = 1
输出:["()"]
思路:这个只能用树来解决了 ,先画出可能的树 然后我们需要剪裁的是哪些树木。
首先我们要意识到 什么样的组合是错误的
(1)右边的 ) 数量 大于 左边 ( 的数量 比如 ()) —> return false
(2)左边的 ( 数量超过 n —> rerturn false
那么怎么构建树呢? 先不考虑剪枝的问题,如何构建你想要的树
开始是 ‘’ 空集,然后下一层是添加 ( 和 )字符。最后的形状应该像这样子
必须设置一个东西来break 树的建立,否则就会一直建立下去
‘’
/ \
( )
/ \ / \
(( () )( ))
/ \ / \ / \ / \
((( (() ()( ()) )(( )() ))( )))
/ \ / \ / \ / \ / \ / \ / \ / \
(((( ((() (()( (()) ...............................
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if not n: return []
ans = []
def buildTree(node,left,right):
#node 是收集所有的字符
#left是int类型 表示node已经收录了多少个(
#right是int类型,表示node已经收录了多少个)
if right > left: return False
if left > n: return False
if len(node) == 2*n: return ans.append(node)
buildTree(node+'(', left+1, right)
buildTree(node + ')', left, right+1)
buildTree('', 0,0)
return ans
题目十:105 前序+中序
105. 从前序与中序遍历序列构造二叉树
根据一棵树的前序遍历与中序遍历构造二叉树。
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
输出:
3
/ \
9 20
/ \
15 7
前序遍历:根结点 —> 左子树 —> 右子树
中序遍历:左子树—> 根结点 —> 右子树
思路是:
首先,找到preorder的根节点,根据这个 回到inorder 划分 左子树和右子树
比如我们根据preorder = [3,9,20,15,7] 知道根节点是3 于是 在inorder = [9,3,15,20,7] 可以划分 9是左子树 15 20 7 是右子树
左子树中找出 找出preorder和inorder再次循环 ;
右子树中找出 找出preorder和inorder再次循环。
最后返回root
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
if not preorder: return None
#错误的写法root = preorder[0]
root = TreeNode(preorder[0])
index = inorder.index(root.val)
root.left = self.buildTree(preorder[1:index+1],inorder[0:index])
root.right = self.buildTree(preorder[index+1:],inorder[index+1:])
return root
题目十一:反转二叉树
请翻转 最少 的树中节点,使二叉树的 先序遍历 与预期的遍历行程 voyage 相匹配 。
如果可以,则返回 翻转的 所有节点的值的列表。你可以按任何顺序返回答案。如果不能,则返回列表 [-1]
输入:root = [1,2], voyage = [2,1]
输出:[-1]
解释:翻转节点无法令先序遍历匹配预期行程
输入:root = [1,2,3], voyage = [1,3,2]
输出:[1]
解释:交换节点 2 和 3 来翻转节点 1 ,先序遍历可以匹配预期行程。
输入:root = [1,2,3], voyage = [1,2,3]
输出:[]
解释:先序遍历已经匹配预期行程,所以不需要翻转节点。
思路:
相当于比较root和voyage这两棵树 一旦有不一样的时候,就翻转看看能不能,这个太难想了
def flipMatchVoyage(self, root: TreeNode, voyage: List[int]) -> List[int]:
ans = []
i = 0
def dfs(root):
nonlocal i
if not root: return True
if root.val != voyage[i]:return False
i+=1
if root.left and root.left.val != voyage[i]:
ans.append(root.val)
root.left, root.right = root.right, root.left
return dfs(root.left) and dfs(root.right)
return ans if dfs(root) else -1
题目十二:全排列
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
输入:nums = [0,1]
输出:[[0,1],[1,0]]
思路跟括号那差不多,寻找递进规律
[1,2,3]–> [1] + [2,3] 然后 [2,3] --[[2] , [3] 然后 知道 空集 停止
def permute(nums):
if not nums: return []
ans = []
def dfs(nums,lis):
if not nums: return ans.append(lis)
for i in range(len(nums)):
dfs(nums[:i]+nums[i+1:], lis + [nums[i]])
dfs(nums,[])
return ans