经典的二叉树题目,代码都可以提交通过
刷题要分类刷这样效果比较好!
二叉树专题
- 废话少说 ,上号!!
- 01、144. 二叉树的前序遍历
- 02、94. 二叉树的中序遍历
- 03、145. 二叉树的后序遍历
- 04、102. 二叉树的层序遍历
- 05、107. 二叉树的层序遍历 II
- 06、104. 二叉树的最大深度
- 07、543. 二叉树的直径
- 08、110. 平衡二叉树
- 09、111. 二叉树的最小深度
- 10、404. 左叶子之和
- 11、103. 二叉树的锯齿形层序遍历
- 12、515. 在每个树行中找最大值
- 13、199. 二叉树的右视图
- 14、100. 相同的树
- 15、101. 对称二叉树
- 16、662. 二叉树最大宽度
- 17、222. 完全二叉树的节点个数
- 18、114. 二叉树展开为链表
- 19、236. 二叉树的最近公共祖先
- 回顾树的深度优先遍历
- 回顾图的深度优先遍历
- 20.112. 路径总和
- 21.113. 路径总和 II
- 21.257. 二叉树的所有路径
- 22.437. 路径总和 III(难)
- 23.124. 二叉树中的最大路径和
- 24.666. 路径总和 IV
- 总结
废话少说 ,上号!!
01、144. 二叉树的前序遍历
非递归代码
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stk = new Stack<>();
if(root == null) return res;
stk.add(root);
while(!stk.isEmpty()){
TreeNode node = stk.pop();
res.add(node.val);
if(node.right != null) stk.push(node.right);
if(node.left != null) stk.push(node.left);
}
return res;
}
}
递归代码
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
dfs(root,res);
return res;
}
private void dfs(TreeNode node , List<Integer> res){
if(node == null) return;
res.add(node.val);
dfs(node.left , res);
dfs(node.right , res);
}
}
02、94. 二叉树的中序遍历
非递归代码
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
Stack<TreeNode> stk = new Stack<>();
List<Integer> res = new ArrayList<>();
//辅助指针
TreeNode cur = root;
while(cur != null || !stk.isEmpty()){
while(cur != null){
stk.push(cur);
cur = cur.left;
}
cur = stk.pop();
//添加到res
res.add(cur.val);
cur = cur.right;
}
return res;
}
}
递归代码
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
dfs(root,res);
return res;
}
private void dfs(TreeNode node , List<Integer> res){
if(node == null) return;
dfs(node.left , res);
res.add(node.val);
dfs(node.right , res);
}
}
03、145. 二叉树的后序遍历
非递归代码
class Solution {
//根左右 --> 根右左 ---> 左右根
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null) return res;
Stack<TreeNode> stk = new Stack<>();
stk.push(root);
while(!stk.isEmpty()){
TreeNode node = stk.pop();
res.add(node.val);
if(node.left != null) stk.push(node.left);
if(node.right != null) stk.push(node.right);
}
Collections.reverse(res);
return res;
}
}
递归代码
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
dfs(root,res);
return res;
}
private void dfs(TreeNode node , List<Integer> res){
if(node == null) return;
dfs(node.left , res);
dfs(node.right , res);
res.add(node.val);
}
}
04、102. 二叉树的层序遍历
非递归代码
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root == null) return res;
que.add(root);
while(!que.isEmpty()){
int size = que.size();
List<Integer> tmp = new ArrayList<>();
for(int i = 0 ; i < size ; i++){
TreeNode node = que.poll();
tmp.add(node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
//添加的时候记得要 new ArrayList<>()
res.add(new ArrayList<>(tmp));
}
return res;
}
}
05、107. 二叉树的层序遍历 II
非递归代码
class Solution {
public List<List<Integer>> levelOrderBottom(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
if(root != null) que.add(root);
List<List<Integer>> res = new ArrayList<>();
while(!que.isEmpty()){
int size = que.size();
List<Integer> list = new ArrayList<>();
for(int i = 0 ; i < size ; i++){
TreeNode node = que.peek();
que.poll();
list.add(node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
res.add(list);
}
Collections.reverse(res);
return res;
}
}
06、104. 二叉树的最大深度
BFS(层序遍历)
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
if(root != null) que.add(root);
int depth = 0;
while(!que.isEmpty()){
int size = que.size();
for(int i = 0 ; i < size ; i++){
TreeNode node = que.poll();
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
depth ++;
}
return depth;
}
}
DFS(前序遍历)
递归代码
class Solution {
private int res;
public int maxDepth(TreeNode root) {
if(root == null) return 0;
dfs(root,1);
return res;
}
private void dfs(TreeNode node,int depth){
if(node == null) return;
res = Math.max(res,depth);
dfs(node.left,depth+1);
dfs(node.right,depth+1);
}
}
DFS(后序遍历)
递归代码
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int leftMaxDepth = maxDepth(root.left);
int rightMaxDepth = maxDepth(root.right);
return Math.max(leftMaxDepth,rightMaxDepth) + 1;
}
}
07、543. 二叉树的直径
这道题竟然没有马上想出来!!
思路:
1.先转化为求二叉树的最大深度(直径经过根节点)(看104题)
节点左右的最大深度相加就是该节点的最大直径
然后res用于维护每个节点的最大深度
2.不经过根节点怎么办?
那我们转化成求每一个节点的左右子树的最大深度之和的最大值。
DFS(后序)
递归代码
class Solution {
private int res = 0;
public int diameterOfBinaryTree(TreeNode root) {
maxDepth(root);
return res;
}
public int maxDepth(TreeNode root){
if(root == null) return 0;
int leftMaxDepth = maxDepth(root.left);
int rightMaxDepth = maxDepth(root.right);
//求每一个节点的左右子树的最大深度之和的最大值。
res = Math.max(res,leftMaxDepth+rightMaxDepth);
return Math.max(leftMaxDepth,rightMaxDepth) + 1;
}
}
08、110. 平衡二叉树
DFS(后序)
思路和最大深度那题很类似
都是自低向上的思想
递归代码
class Solution {
public boolean isBalanced(TreeNode root) {
//如果根节点返回的最大深度不是-1,那么他是平衡二叉树
return maxDepth(root) >= 0;
}
public int maxDepth(TreeNode root){
if(root == null) return 0;
int leftMaxDepth = maxDepth(root.left);
int rightMaxDepth = maxDepth(root.right);
//如果左边或者右边已经有不是平衡二叉树的子树了我们直接返回-1
if(leftMaxDepth == -1 || rightMaxDepth == -1) return -1;
//如果左深度和右深度的绝对值大于1返回-1作为标记
if(Math.abs(leftMaxDepth-rightMaxDepth) > 1) return -1;
return Math.max(leftMaxDepth,rightMaxDepth) + 1;
}
}
递归代码
代码优化 (使用三元运算符)
class Solution {
public boolean isBalanced(TreeNode root) {
//如果根节点返回的最大深度不是-1,那么他是平衡二叉树
return maxDepth(root) >= 0;
}
public int maxDepth(TreeNode root){
if(root == null) return 0;
int leftMaxDepth = maxDepth(root.left);
int rightMaxDepth = maxDepth(root.right);
return (leftMaxDepth == -1 || rightMaxDepth == -1 || Math.abs(leftMaxDepth-rightMaxDepth) > 1) ? -1 : Math.max(leftMaxDepth,rightMaxDepth) + 1;
}
}
09、111. 二叉树的最小深度
111. 二叉树的最小深度
BFS代码
//推荐写法
class Solution {
public int minDepth(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
int depth = 0;
if(root != null) que.add(root);
while(!que.isEmpty()){
int size = que.size();
depth ++;
for(int i = 0 ; i < size ; i++){
TreeNode node = que.poll();
//遇到第一个叶子节点我们就要跳出循环了
if(node.left == null && node.right == null) return depth;
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
}
return depth;
}
}
DFS(后序遍历)
class Solution {
public int minDepth(TreeNode root) {
if(root == null) return 0;
//叶子节点返回1
if(root.left == null && root.right == null) return 1;
int leftMinDepth = minDepth(root.left);
int rightMinDepth = minDepth(root.right);
//一边为null,我们取另外一边。两边都不为null取两边最小值
if(root.left == null) return rightMinDepth + 1;
else if(root.right == null) return leftMinDepth + 1;
else return Math.min(leftMinDepth,rightMinDepth) + 1;
}
}
10、404. 左叶子之和
404. 左叶子之和
DFS(后序)
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
//结点为null,左叶子结点之和为0
if(root == null) return 0;
//求出root左子树的左叶子结点之和
int leftValue = sumOfLeftLeaves(root.left);
//求出root右子树的左叶子结点之和
int rightValue = sumOfLeftLeaves(root.right);
//求root结点基础上左叶子结点之和
int midValue = 0;
//成为左叶子的条件
if(root.left != null && root.left.left == null && root.left.right == null)
midValue = root.left.val;
//返回 sum = midValue + leftValue + rightValue
return midValue + leftValue + rightValue;
}
}
DFS(后序)
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
return dfs(root,root);
}
public int dfs(TreeNode node,TreeNode parent){
if(node == null) return 0;
if(node.left == null && node.right == null && parent.left == node){
return node.val;
}
int leftSum = dfs(node.left,node);
int rightSum = dfs(node.right,node);
return leftSum + rightSum;
}
}
BFS(层序遍历)
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root == null) return 0;
Queue<TreeNode> que = new LinkedList<>();
que.add(root);
int res = 0;
while(!que.isEmpty()){
TreeNode node = que.poll();
//有左节点
if(node.left != null){
//判断左节点是不是叶子节点
if(isLeafNode(node.left)){//是
res += node.left.val;
}else{//不是
//加入到对列
que.add(node.left);
}
}
//对于右节点,如果右节点不是null并且右节点不是叶子节点我们才将他加入到对列中
if(node.right != null && !isLeafNode(node.right))
que.add(node.right);
}
return res;
}
//判断一个节点是否为叶子节点的方法
public boolean isLeafNode(TreeNode node){
return node.left == null && node.right == null;
}
}
11、103. 二叉树的锯齿形层序遍历
BFS(层序遍历)
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
if(root == null) return res;
que.add(root);
//level用于表示层数,初始化为1
int level = 1;
while(!que.isEmpty()){
List<Integer> tmp = new ArrayList<>();
int size = que.size();
for(int i = 0 ; i < size ; i++){
TreeNode node = que.poll();
tmp.add(node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
//对于偶数层,我们进行数组反转操作
if(level % 2 == 0) Collections.reverse(tmp);
res.add(new ArrayList<>(tmp));
//没遍历完一层我们层数+1
level++;
}
return res;
}
}
12、515. 在每个树行中找最大值
BFS(层)
class Solution {
public List<Integer> largestValues(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
if(root != null) que.add(root);
List<Integer> res = new ArrayList<>();
while(!que.isEmpty()){
int max = Integer.MIN_VALUE;
int size = que.size();
for(int i = 0 ; i < size ; i++){
TreeNode node = que.poll();
max = Math.max(max,node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
res.add(max);
}
return res;
}
}
13、199. 二叉树的右视图
class Solution {
public List<Integer> rightSideView(TreeNode root) {
Queue<TreeNode> que = new LinkedList<>();
if(root != null) que.add(root);
List<Integer> res = new ArrayList<>();
while(!que.isEmpty()){
int size = que.size();
List<Integer> list = new ArrayList<>();
for(int i = 0 ; i < size ; i++){
TreeNode node = que.poll();
//处理右视图的结点
if(i == (size-1)) res.add(node.val);
if(node.left != null) que.add(node.left);
if(node.right != null) que.add(node.right);
}
}
return res;
}
}
14、100. 相同的树
DFS(先序遍历)
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
//递归终止条件:先比较两棵树的根节点
if(p == null && q == null) return true;
if(p == null || q == null) return false;
if(p.val != q.val) return false;
//比较两棵树的左右子树是否相同
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
BFS(层序遍历)
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null) return true;
if(p == null || q == null) return false;
//使用两个队列
Queue<TreeNode> queue1 = new LinkedList<>();
Queue<TreeNode> queue2 = new LinkedList<>();
//两个队列加入根节点
queue1.add(p);
queue2.add(q);
while(!queue1.isEmpty() && !queue2.isEmpty()){
TreeNode node1 = queue1.poll();
TreeNode node2 = queue2.poll();
//如果值不同 return false
if(node1.val != node2.val) return false;
//分别取出node1结点 和 node2结点的左右子结点
TreeNode left1 = node1.left;
TreeNode right1 = node1.right;
TreeNode left2 = node2.left;
TreeNode right2 = node2.right;
//进行异或操作:其中有一个为null 就 return false
if(left1 == null ^ left2 == null) return false;
if(right1 == null ^ right2 == null) return false;
//加入队列
if(node1.left != null) queue1.add(node1.left);
if(node1.right != null) queue1.add(node1.right);
if(node2.left != null) queue2.add(node2.left);
if(node2.right != null) queue2.add(node2.right);
}
//两个队列是否都为空
return queue1.isEmpty() && queue2.isEmpty();
}
}
15、101. 对称二叉树
判断二叉树是否对称:
我们只需要判断该二叉树的左右子树是否镜像对称
DFS
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
return isSymmetric(root.left,root.right);
}
//判断两棵树是否镜像对称
public boolean isMirror(TreeNode left , TreeNode right){
if(left == null && right == null) return true;
if(left == null || right == null) return false;
if(left.val != right.val) return false;
//左边的左子树和右边的右子树是否为镜像。
//左边的右子树和右边的左子树是否为镜像
return isMirror(left.left,right.right) && isMirror(left.right,right.left);
}
}
BFS
//不推荐
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
queue.add(root);
while(!queue.isEmpty()){
TreeNode t1 = queue.poll();
TreeNode t2 = queue.poll();
if(t1 == null && t2 == null) continue;
if(t1 == null || t2 == null) return false;
if(t1.val != t2.val) return false;
//t1 != null && t2 != null && t1.val == t2.val 时添加元素到队列
//注意添加顺序
queue.offer(t1.left);
queue.offer(t2.right);
queue.offer(t1.right);
queue.offer(t2.left);
}
return true;
}
}
16、662. 二叉树最大宽度
BFS
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
//时间o(n)
//空间o(n)
class Solution {
private class Node{
TreeNode node;
int seqNo;
Node(TreeNode node,int seqNo){
this.node = node;
this.seqNo = seqNo;
}
}
public int widthOfBinaryTree(TreeNode root) {
if(root == null) return 0;
Queue<Node> queue = new LinkedList<>();
queue.add(new Node(root,1));
int maxWidth = 0;
while(!queue.isEmpty()){
int size = queue.size();
int startSeqNo = 0;
int endSeqNo = 0;
for(int i = 0 ; i < size ; i++){
Node curr = queue.poll();
TreeNode node = curr.node;
int seqNo = curr.seqNo;
if(i == 0) startSeqNo = seqNo;
if(i == size - 1) endSeqNo = seqNo;
if(node.left != null){
queue.add(new Node(node.left,2*seqNo));
}
if(node.right != null){
queue.add(new Node(node.right,2*seqNo+1));
}
}
maxWidth = Math.max(maxWidth,endSeqNo-startSeqNo + 1);
}
return maxWidth;
}
}
17、222. 完全二叉树的节点个数
DFS(后序)
class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
int leftCountNodes = countNodes(root.left);
int rightCountNodes = countNodes(root.right);
return leftCountNodes + rightCountNodes + 1;
}
}
进阶:遍历树来统计节点是一种时间复杂度为 O(n) 的简单解决方案。你可以设计一个更快的算法吗?
二分查找:
o(logn)
class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
//1.计算完全二叉树的最大层数
int level = 0;
TreeNode node = root;
while(node.left != null){
level++;
node = node.left;
}
//完全二叉树的节点范围:[2 ^ level , 2 ^ (level + 1) - 1]
int low = 1 << level;
int high = (1 << (level + 1)) - 1;
while(low < high){
int mid = low + (high - low + 1) / 2;
if(exists(root,level,mid)){
low = mid;
}else{
high = mid - 1;
}
}
return low;
}
private boolean exists(TreeNode root , int level , int mid){
int mask = 1 << (level - 1);// level = 4 , 01000
TreeNode node = root;
while(node!=null && mask > 0){
if((mask & mid) == 0){
node = node.left;
}else{
node = node.right;
}
mask >>= 1;
}
return node != null;
}
}
18、114. 二叉树展开为链表
代码1:使用辅助空间List集合存储结点,然后重建链表
时间:o(n)
空间:o(n)
class Solution {
public void flatten(TreeNode root) {
List<TreeNode> list = new ArrayList<TreeNode>();
preOrder(root,list);
for(int i = 1 ; i < list.size() ; i++){
TreeNode prev = list.get(i-1);
TreeNode curr = list.get(i);
prev.left = null;
prev.right = curr;
}
}
public void preOrder(TreeNode root , List<TreeNode> list){
if(root == null) return;
list.add(root);
preOrder(root.left,list);
preOrder(root.right,list);
}
}
边遍历边串联
class Solution {
public void flatten(TreeNode root) {
if(root == null) return;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode prev = null;
while(!stack.isEmpty()){
TreeNode curr = stack.pop();
if(prev != null){
prev.left = null;
prev.right = curr;
}
TreeNode left = curr.left;
TreeNode right = curr.right;
if(right != null) stack.push(right);
if(left != null) stack.push(left);
prev = curr;
}
}
}
进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗?
最优代码:原地操作
class Solution {
public void flatten(TreeNode root) {
if(root == null) return;
TreeNode curr = root;
while(curr != null){
if(curr.left != null){
TreeNode left = curr.left;
TreeNode pre = left;
while(pre.right != null){
pre = pre.right;
}
pre.right = curr.right;
curr.left = null;
curr.right = left;
curr = curr.right;
}else{
curr = curr.right;
}
}
}
}
19、236. 二叉树的最近公共祖先
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
//1.维护子节点和其对应父亲结点的关系
Map<Integer,TreeNode> parent = new HashMap<>();
dfs(root,parent);
//2.从结点p开始访问他的祖先
Set<Integer> visited = new HashSet<>();
while(p != null){
visited.add(p.val);
p = parent.get(p.val);
}
//3.访问q的所有祖先
while(q != null){
if(visited.contains(q.val)) return q;
q = parent.get(q.val);
}
return null;
}
private void dfs(TreeNode node , Map<Integer,TreeNode> parent){
if(node == null) return;
if(node.left != null) parent.put(node.left.val,node);
if(node.right != null) parent.put(node.right.val,node);
dfs(node.left,parent);
dfs(node.right,parent);
}
}
DFS后序
推荐!
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return null;
if(root == p || root == q) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(left == null) return right;
if(right == null) return left;
return root;
}
}
回顾树的深度优先遍历
所有回溯问题都可以抽象成树的深度优先遍历
public class TreeDFS {
private static class TreeNode{
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
public List<TreeNode> preOrder(TreeNode root){
List<TreeNode> res = new ArrayList<>();
dfs(root,res);
return res;
}
private void dfs(TreeNode node , List<TreeNode> res){
if(node == null) return;
res.add(node);
dfs(node.left,res);
dfs(node.right,res);
}
}
回顾图的深度优先遍历
//Graph图的接口
public interface Graph {
/**
* 获取图的边数
* @return
*/
int getE();
/**
* 获取图的顶点数
* @return
*/
int getV();
/**
* 判断两个指定的顶点之间是否有边
* @param v
* @param w
* @return
*/
boolean hasEdge(int v, int w);
/**
* 获取指定顶点所有相邻的顶点
* @param v
* @return
*/
Collection<Integer> adj(int v);
/**
* 获取指定顶点的度数
* @param v
* @return
*/
int degree(int v);
}
//
public class GraphDFS {
public List<Integer> dfs(Graph g){
List<Integer> res = new ArrayList<>();
boolean[] visited = new boolean[g.getV()];
for(int v = 0 ; v > g.getV() ; v++){
if(!visited[v]){
dfs(g,v,res,visited);
}
}
return res;
}
private void dfs(Graph g , int v , List<Integer> res , boolean[] visited){
res.add(v);
visited[v] = true;
for(int w : g.adj(v)){
if(!visited[w]){
dfs(g,w,res,visited);
}
}
}
}
20.112. 路径总和
回溯思想
代码1:穷举所有的路径
public class PathSum1 {
private List<List<Integer>> res;
public List<List<Integer>> allPath(TreeNode root){
res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,path);
return res;
}
private void dfs(TreeNode node , List<Integer> path){
if(node == null) return;
path.add(node.val);
if(node.left == null && node.right == null){
res.add(new ArrayList<Integer>(path));
}
dfs(node.left,path);
dfs(node.right,path);
//回溯过程中,将当前节点从path中删除
path.remove(path.size()-1);
}
}
//或者res作为方法参数
public class PathSum1 {
public List<List<Integer>> allPath(TreeNode root){
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,path,res);
return res;
}
private void dfs(TreeNode node , List<Integer> path,List<List<Integer>> res){
if(node == null) return;
path.add(node.val);
if(node.left == null && node.right == null){
res.add(new ArrayList<Integer>(path));
}
dfs(node.left,path,res);
dfs(node.right,path,res);
//回溯过程中,将当前节点从path中删除
path.remove(path.size()-1);
}
}
代码2:判断路径中是否存在目标和
public class PathSum2 {
public boolean hasPathSum(TreeNode root,int target){
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,path,res);
for(List<Integer> onPath : res){
int sum = 0;
for(int val : onPath){
sum += val;
}
if(sum == target) return true;
}
return false;
}
private void dfs(TreeNode node , List<Integer> path,List<List<Integer>> res){
if(node == null) return;
path.add(node.val);
if(node.left == null && node.right == null){
res.add(new ArrayList<Integer>(path));
}
dfs(node.left,path,res);
dfs(node.right,path,res);
//回溯过程中,将当前节点从path中删除
path.remove(path.size()-1);
}
}
代码3:计算每个节点的路径和
public class PathSum3 {
public boolean hasPathSum(TreeNode root,int target){
List<Integer> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,root.val, res);
for(int onPathSum : res){
if(onPathSum == target) return true;
}
return false;
}
private void dfs(TreeNode node , int parentNodePathSum , List<Integer> res){
if(node == null) return;
int currNodePathSum = parentNodePathSum + node.val;
if(node.left == null && node.right == null){
res.add(currNodePathSum);
}
dfs(node.left,currNodePathSum,res);
dfs(node.right,currNodePathSum,res);
}
}
计算每个节点的目标和
public class PathSum4 {
public boolean hasPathSum(TreeNode root,int target){
List<Integer> res = new ArrayList<>();
dfs(root,target, res);
for(int val : res){
if(val == 0) return true;
}
return false;
}
private void dfs(TreeNode node , int parentNodeTarget , List<Integer> res){//父亲结点的目标和
if(node == null) return;
int currNodeTarget = parentNodeTarget - node.val;
if(node.left == null && node.right == null){
res.add(currNodeTarget);
}
dfs(node.left,currNodeTarget,res);
dfs(node.right,currNodeTarget,res);
}
}
最优的代码:提前返回!
class Solution {
public boolean hasPathSum(TreeNode root, int target) {
return dfs(root,target);
}
private boolean dfs(TreeNode node , int parentNodeTarget){//父亲结点的目标和
if(node == null) return false;
int currNodeTarget = parentNodeTarget - node.val;
if(node.left == null && node.right == null){
return currNodeTarget == 0;
}
boolean isLeftHasPathSum = dfs(node.left,currNodeTarget);
boolean isRightHasPathSum = dfs(node.right,currNodeTarget);
return isLeftHasPathSum || isRightHasPathSum;
}
}
//或者:加入剪枝
class Solution {
public boolean hasPathSum(TreeNode root, int target) {
return dfs(root,target);
}
private boolean dfs(TreeNode node , int parentNodeTarget){//父亲结点的目标和
if(node == null) return false;
int currNodeTarget = parentNodeTarget - node.val;
if(node.left == null && node.right == null){
return currNodeTarget == 0;
}
boolean isLeftHasPathSum = dfs(node.left,currNodeTarget);
if(isLeftHasPathSum) return true;//剪枝,提前退出
boolean isRightHasPathSum = dfs(node.right,currNodeTarget);
return isLeftHasPathSum || isRightHasPathSum;
}
}
21.113. 路径总和 II
小改一下PathSum1
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,path,res,target);
return res;
}
private void dfs(TreeNode node , List<Integer> path,List<List<Integer>> res , int target){
if(node == null) return;
path.add(node.val);
if(node.left == null && node.right == null){
int sum = 0;
for(Integer val : path){
sum += val;
}
if(sum == target) res.add(new ArrayList<Integer>(path));
}
dfs(node.left,path,res,target);
dfs(node.right,path,res,target);
//回溯过程中,将当前节点从path中删除
path.remove(path.size()-1);
}
}
最优代码:
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,target,path,res);
return res;
}
private void dfs(TreeNode node , int parentNodeTarget , List<Integer> path,List<List<Integer>> res ){
if(node == null) return;
path.add(node.val);
int currNodeTarget = parentNodeTarget - node.val;
if(node.left == null && node.right == null && currNodeTarget == 0){
res.add(new ArrayList<Integer>(path));
}
dfs(node.left , currNodeTarget , path , res);
dfs(node.right , currNodeTarget , path , res);
//回溯过程中,将当前节点从path中删除
path.remove(path.size()-1);
}
}
21.257. 二叉树的所有路径
很容易写出:
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
dfs(root,res,path);
return res;
}
public void dfs(TreeNode node , List<String> res , List<Integer> path){
if(node == null) return;
path.add(node.val);
if(node.left == null && node.right == null){
StringBuilder sb = new StringBuilder();
for(int i = 0 ; i < path.size() ; i++){
sb.append(path.get(i));
if(i != path.size() - 1) sb.append("->");
}
res.add(sb.toString());
}
dfs(node.left,res,path);
dfs(node.right,res,path);
path.remove(path.size()-1);
}
}
22.437. 路径总和 III(难)
代码1:计算所有节点的所有路径和
时间复杂度:o(nlongn)
复杂!
class Solution {
public int pathSum(TreeNode root, int targetSum) {
return dfs(root,new ArrayList<>(),targetSum);
}
private int dfs(TreeNode node , List<Integer> parentPathSumList , int targetSum){
if(node == null) return 0;
List<Integer> tmp = new ArrayList<>();
int cnt = 0;
for(int i = 0 ; i < parentPathSumList.size() ; i++){
int num = parentPathSumList.get(i) + node.val;
tmp.add(num);
if(num == targetSum) cnt++;
}
tmp.add(node.val);
if(node.val == targetSum) cnt++;
int leftCnt = dfs(node.left,tmp,targetSum);
int rightCnt = dfs(node.right,tmp,targetSum);
return cnt + leftCnt + rightCnt;
}
}
优化:
前缀和+DFS
//o(n):前缀和+DFS(前序遍历)
class Solution {
//计算结果
private int res;
public int pathSum(TreeNode root, int targetSum) {
Map<Integer,Integer> prefixSumMap = new HashMap<>();
prefixSumMap.put(0,1);
dfs(root,0,targetSum,prefixSumMap);
return res;
}
private void dfs(TreeNode node , int currSum , int targetSum , Map<Integer,Integer> prefixSumMap){
if(node == null) return;
currSum += node.val;
res += prefixSumMap.getOrDefault(currSum-targetSum,0);
prefixSumMap.put(currSum,prefixSumMap.getOrDefault(currSum,0) + 1);
dfs(node.left,currSum,targetSum,prefixSumMap);
dfs(node.right,currSum,targetSum,prefixSumMap);
prefixSumMap.put(currSum,prefixSumMap.get(currSum) - 1);
}
//在一个数组中求连续子数组【区间】等于targetSum的连续子数组的个数
public int pathSum(int[] nums , int targetSum){
int ans = 0;
Map<Integer,Integer> prefixSumMap = new HashMap<>();
prefixSumMap.put(0,1);
int currSum = 0;
for(int i = 0 ; i < nums.length ; i++){
currSum += nums[i];
ans += prefixSumMap.getOrDefault(currSum - targetSum,0);
prefixSumMap.put(currSum,prefixSumMap.getOrDefault(currSum,0) + 1);
}
return ans;
}
}
23.124. 二叉树中的最大路径和
class Solution {
private int maxSum = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxGain(root);
return maxSum;
}
//返回以node为根节点的子树的最大贡献值
private int maxGain(TreeNode node){
if(node == null) return 0;
// 递归计算左右子节点的最大贡献值
// 只有在最大贡献值大于 0 时,才会选取对应子节点
int leftGain = Math.max(maxGain(node.left),0);
int rightGain = Math.max(maxGain(node.right),0);
// 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
int gain = leftGain+rightGain+node.val;
maxSum = Math.max(maxSum,gain);
// 返回节点的最大贡献值
return Math.max(leftGain,rightGain) + node.val;
}
}
24.666. 路径总和 IV
class Solution {
private int ans = 0;
public int pathSum(int[] nums) {
//构建二叉树,用数组存储
Integer[] tree = new Integer[15];
for(int num : nums){
int bai = num / 100;
int shi = num % 100 / 10;
int ge = num % 10;
int index = ((1 << (bai - 1)) - 1) + shi - 1;
tree[index] = ge;
}
//DFS遍历树
dfs(tree,0,0);
return ans;
}
private void dfs(Integer[] tree , int i , int currPathSum){
if(tree[i] == null) return;
currPathSum += tree[i];
if(i >= 7 || (tree[2*i+1] == null && tree[2*i+2] == null)){
ans += currPathSum;
return;
}
dfs(tree,2*i+1,currPathSum);
dfs(tree,2*i+2,currPathSum);
currPathSum -= tree[i];
}
}