LeetCode日记——【数据结构】树专题(递归)
题1:树的高度(Maximum Depth of Binary Tree)
LeetCode题号:104
难度:easy
链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/description/
题目描述:
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int maxDepth(TreeNode root) { 12 if (root == null) return 0; 13 return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; 14 } 15 }
分析:
maxDepth(root.left)表示计算当前节点左子树的高度,maxDepth(root.right)表示计算当前节点右子树的高度,取二者较大值,再加1(根节点)即为树的最大深度。
题2:平衡树(Balanced Binary Tree)
LeetCode题号:110
难度:easy
链接:https://leetcode-cn.com/problems/balanced-binary-tree/description/
题目描述:
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
示例 1:给定二叉树 [3,9,20,null,null,15,7]
3
/ \
9 20
/ \
15 7
返回 true 。
示例 2:给定二叉树 [1,2,2,3,3,null,null,4,4]
1
/ \
2 2
/ \
3 3
/ \
4 4
返回 false 。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 private boolean result = true; 12 13 public boolean isBalanced(TreeNode root) { 14 maxDepth(root); 15 return result; 16 } 17 18 public int maxDepth(TreeNode root) { 19 if (root == null) return 0; 20 int l = maxDepth(root.left); 21 int r = maxDepth(root.right); 22 if (Math.abs(l - r) > 1) result = false; 23 return 1 + Math.max(l, r); 24 } 25 }
分析:
主判断方法为isBalanced(),其中调用了maxDepth()
maxDepth()方法中,我们通过递归,计算了每个节点的左后子树的深度并进行比较,若差距大于1,则将result置为false(不平衡)
题3:两节点的最长路径(Diameter of Binary Tree)
LeetCode题号:543
难度:easy
链接:https://leetcode-cn.com/problems/diameter-of-binary-tree/description/
题目描述:
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
注意:两结点之间的路径长度是以它们之间边的数目表示。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 int ans; 12 public int diameterOfBinaryTree(TreeNode root) { 13 ans = 1; 14 depth(root); 15 return ans - 1; 16 } 17 public int depth(TreeNode node) { 18 if (node == null) return 0; // 访问到空节点了,返回0 19 int L = depth(node.left); // 左儿子为根的子树的深度 20 int R = depth(node.right); // 右儿子为根的子树的深度 21 ans = Math.max(ans, L+R+1); // 计算d_node即L+R+1 并更新ans 22 return Math.max(L, R) + 1; // 返回该节点为根的子树的深度 23 } 24 }
分析:
主方法中,我们定义一个变量ans用来记录一条路上最多的节点数。最后返回ans-1,即最大长度。在主方法中调用depth()方法。
depth()方法用递归的方式计算当前节点的左子树与右子树的深度,计算出L+R+1即链子的节点数,并用ans=max(ans,L+R+1)将当前最长的链子的节点数赋给ans。
题4:翻转树(Invert Binary Tree)
LeetCode题号:226
难度:easy
链接:https://leetcode-cn.com/problems/invert-binary-tree/description/
题目描述:
翻转一棵二叉树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public TreeNode invertTree(TreeNode root) { 11 if (root == null) { 12 return null; 13 } 14 TreeNode right = invertTree(root.right); 15 TreeNode left = invertTree(root.left); 16 root.left = right; 17 root.right = left; 18 return root; 19 }
分析:
用递归分别翻转当前节点的右节点与左节点,并赋给right与left。最后交换当前节点的左右子节点。
题5:归并两棵树(Merge Two Binary Trees)
LeetCode题号:617
难度:easy
链接:https://leetcode-cn.com/problems/merge-two-binary-trees/description/
题目描述:
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
输入:
Tree 1 Tree 2
1 2
/ \ / \
3 2 1 3
/ \ \
5 4 7
输出:
合并后的树:
3
/ \
4 5
/ \ \
5 4 7
注意: 合并必须从两个树的根节点开始。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { 12 if(t1==null && t2==null) return null; 13 if(t1==null) return t2; 14 if(t2==null) return t1; 15 16 TreeNode root = new TreeNode(t1.val+t2.val); 17 root.left= mergeTrees(t1.left,t2.left); 18 root.right=mergeTrees(t1.right,t2.right); 19 return root; 20 21 } 22 }
分析:
使用递归一定要写好退出的条件。
首先我们定义一个新节点,将当前的t1与t2的值相加,把值赋给新节点。
然后我们用递归,给这个新节点的左子节点和又子节点赋值。
题6:判断路径和是否等于一棵树(Path Sum)
LeetCode题号:112
难度:easy
链接:https://leetcode-cn.com/problems/path-sum/description/
题目描述:
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
示例: 给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ \
7 2 1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public boolean hasPathSum(TreeNode root, int sum) { 12 if(root==null) return false; 13 sum-=root.val; 14 //若为叶子节点,则判断此时sum是否减到0,若是则返回true,不是则返回false 15 if(root.left==null&&root.right==null) return (sum==0); 16 //递归调用其左右子节点,有一边成立就返回true 17 return hasPathSum(root.left, sum) || hasPathSum(root.right, sum); 18 19 } 20 }
分析:
定义一个动态的变量sum,每次都减去当前节点的值。
判断当前节点是否已经是叶子节点,如果是,直接判断sum是否为0,若是则返回true,否则返回false(已经没有机会再加别的节点了)
当前节点的左右节点分别递归调用hasPathSum(),当任意一方为true,就返回true。
题7:统计路径和等于一个数的路径数量(Path Sum III)
LeetCode题号:437
难度:easy
链接:https://leetcode-cn.com/problems/path-sum-iii/description/
题目描述:
给定一个二叉树,它的每个结点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int pathSum(TreeNode root, int sum) { 12 if(root == null) return 0; 13 //当前节点开始的所有满足条件的路径总数 14 int result = countPath(root,sum); 15 //当前节点左子节点开始的满足条件路径总数 16 int a = pathSum(root.left,sum); 17 //当前节点右子节点开始的满足条件路径总数 18 int b = pathSum(root.right,sum); 19 return result+a+b; 20 } 21 public int countPath(TreeNode root,int sum){ 22 if(root == null) return 0; 23 sum = sum - root.val; 24 int result = sum == 0 ? 1:0; 25 //当前节点开始的所有满足条件的路径总数分为左,右两条路径 26 return result + countPath(root.left,sum) + countPath(root.right,sum); 27 } 28 }
分析:
题目要求 路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点) 。这就要求我们只需要去求三部分即可:
以当前节点作为头结点的路径数量
以当前节点的左孩子作为头结点的路径数量
以当前节点的右孩子作为头结点的路径数量
将这三部分之和作为最后结果即可。
最后的问题是:我们应该如何去求以当前节点作为头结点的路径的数量?这里依旧是按照树的遍历方式模板,每到一个节点让sum-root.val,并判断sum是否为0,如果为零的话,则找到满足条件的一条路径。
题8:子树(Subtree of Another Tree)
LeetCode题号:572
难度:easy
链接:https://leetcode-cn.com/problems/subtree-of-another-tree/description/
题目描述:
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
示例 :
给定的树 s:
3
/ \
4 5
/ \
1 2
给定的树 t:
4
/ \
1 2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode() {} 8 * TreeNode(int val) { this.val = val; } 9 * TreeNode(int val, TreeNode left, TreeNode right) { 10 * this.val = val; 11 * this.left = left; 12 * this.right = right; 13 * } 14 * } 15 */ 16 class Solution { 17 public boolean isSubtree(TreeNode s, TreeNode t) { 18 if (s == null) return false; 19 return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t); 20 } 21 private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) { 22 if (t == null && s == null) return true; 23 if (t == null || s == null) return false; 24 //这个函数的核心:若当前两个节点的值不一样,马上返回false 25 if (t.val != s.val) return false; 26 //递归调用,比较左子节点,右子节点 27 return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right); 28 } 29 }
分析:
这道题我没有完全搞明白,下次再回来做几遍!!
题9:树的对称(Symmetric Tree)
LeetCode题号:101
难度:easy
链接:https://leetcode-cn.com/problems/symmetric-tree/description/
题目描述:
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public boolean isSymmetric(TreeNode root) { 12 if(root==null) return true; 13 return isSymmetric(root.left,root.right); 14 } 15 private boolean isSymmetric(TreeNode t1,TreeNode t2){ 16 if(t1==null && t2==null) return true; 17 if(t1==null || t2==null) return false; 18 if(t1.val!=t2.val) return false; 19 return isSymmetric(t1.left,t2.right) && isSymmetric(t2.left,t1.right); 20 } 21 }
分析:
主方法isSymmetric(TreeNode root)中,若root节点为空,则直接返回true,然后调用重载的isSymmetric(root.left,root.right)方法,用来判断当前节点的左右子节点是否对称。
重载的isSymmetric(t1,t2)方法用来判断两个节点的值是否一样。递归调用自身,可以判断所有子节点是否对称。
题10:最小路径(Minimum Depth of Binary Tree)
LeetCode题号:111
难度:easy
链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/description/
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int minDepth(TreeNode root) { 12 //1.若当前节点为空,直接返回0 13 if(root==null) return 0; 14 //2.左孩子和有孩子都为空的情况,说明到达了叶子节点,直接返回1即可 15 if(root.left==null && root.right==null) return 1; 16 //3.如果左孩子和由孩子其中一个为空,说明ldepth1和rdepth有一个必然为0,所以可以返回ldepth + rdepth + 1; 17 int ldepth = minDepth(root.left); 18 int rdepth = minDepth(root.right); 19 if(root.left==null||root.right==null) return ldepth+rdepth+1; 20 //4.最后一种情况,也就是左右孩子都不为空,返回最小深度+1即可 21 return Math.min(ldepth,rdepth)+1; 22 23 } 24 }
分析:
分4种情况讨论。
首先我们可以递归计算出当前节点左节点的最小深度与当前节点右节点的最小深度。
情况1:当前节点为空,直接返回0
情况2:左节点右节点均为0,即当前节点为叶子节点,则返回1
情况3:左右节点有1个及以上为空,那么我们将左右的最小深度相加再+1即可
情况4:左右均不为空,找出左右两边较浅的一边+1即可
题11:统计左叶子节点的和(Sum of Left Leaves)
LeetCode题号:404
难度:easy
链接:https://leetcode-cn.com/problems/sum-of-left-leaves/description/
题目描述:
计算给定二叉树的所有左叶子之和。
示例:
3
/ \
9 20
/ \
15 7
在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int sumOfLeftLeaves(TreeNode root) { 12 //若当前节点为null,直接返回0 13 if(root==null) return 0; 14 //若当前节点的左节点为叶子节点,则返回其左节点的值+右节点的所有左叶子之和 15 if(isLeaf(root.left)) return root.left.val+sumOfLeftLeaves(root.right); 16 //其他情况则返回其左节点的所有左叶子之和+其右节点的所有左叶子之和 17 return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right); 18 } 19 20 //判断该节点是否为叶子节点,若左右子节点均为空,返回true 21 private boolean isLeaf(TreeNode node){ 22 if (node == null) return false; 23 return node.left == null && node.right == null; 24 } 25 }
分析:
先写一个isLeaf()方法来判断当前节点是否为叶子节点。
然后我们分三种情况讨论:
情况1:当前节点为null,则直接返回0
情况2:当前节点的左子节点为叶子节点,那么我们只需要计算该其左子节点+右节点的所有左叶子之和
情况3:其他情况,则计算其左子节点的所有左叶子之和+其右节点的所有左叶子之和
题12:相同节点值的最大路径长度(Longest Univalue Path)
LeetCode题号:687
难度:easy
链接:https://leetcode-cn.com/problems/longest-univalue-path/
题目描述:
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。
示例 1:
输入:
5
/ \
4 5
/ \ \
1 1 5
输出: 2
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 private int path = 0; 12 13 public int longestUnivaluePath(TreeNode root) { 14 dfs(root); 15 return path; 16 } 17 18 private int dfs(TreeNode root){ 19 if (root == null) return 0; 20 int left = dfs(root.left); 21 int right = dfs(root.right); 22 int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0; 23 int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0; 24 path = Math.max(path, leftPath + rightPath); 25 return Math.max(leftPath, rightPath); 26 } 27 }
分析:
dfs()方法中,我们不断更新最长的path(左边+右边),返回左右两边更长的path值。
题13:间隔遍历(House Robber III)
LeetCode题号:337
难度:Medium
链接:https://leetcode-cn.com/problems/house-robber-iii/description/
题目描述:
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1:
输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int rob(TreeNode root) { 12 //若当前节点为null,直接返回0 13 if(root==null) return 0; 14 //计算val1(表示单层数据的和) 15 int val1=root.val; 16 //如果当前节点的左节点不为null,那就将当前节点的值加上左节点的两个子节点的值 17 if(root.left!=null) val1+=rob(root.left.left)+rob(root.left.right); 18 //如果当前节点的右节点不为null,那就将当前节点的值加上右节点的两个子节点的值 19 if(root.right!=null) val1+=rob(root.right.left)+rob(root.right.right); 20 //计算val2(表示双层数据的和) 21 int val2=rob(root.left)+rob(root.right); 22 //返回val1与val2中更大的那个值 23 return Math.max(val1,val2); 24 } 25 }
分析:
总体思路为:分别计算单层数据的和与双层数据的和,返回较大的那个值。
单层数据之和的计算:当前节点的值,加上左子节点的两个子节点的和,再加上右子节点的两个子节点的和。
双层数据之和的计算:当前节点的两个子节点的值之和。
题14:找出二叉树中第二小的节点(Second Minimum Node In a Binary Tree)
LeetCode题号:671
难度:Easy
链接:https://leetcode-cn.com/problems/second-minimum-node-in-a-binary-tree/description/
题目描述:
给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么这个节点的值不大于它的子节点的值。
给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。
示例 1:
输入:
2
/ \
2 5
/ \
5 7
输出: 5
说明: 最小的值是 2 ,第二小的值是 5 。
代码:
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 class Solution { 11 public int findSecondMinimumValue(TreeNode root) { 12 //若当前节点为空,或者当前节点为叶子节点,直接返回-1 13 if(root==null) return -1; 14 if(root.left==null && root.right==null) return -1; 15 //计算当前节点左右子节点的值 16 int leftval= root.left.val; 17 int rightval= root.right.val; 18 //若其左节点的值与其相同,对其左节点继续运算 19 if (leftval == root.val) leftval = findSecondMinimumValue(root.left); 20 //若其右节点的值与其相同,对其右节点继续运算 21 if (rightval == root.val) rightval = findSecondMinimumValue(root.right); 22 //未到叶子节点,返回左右节点中的较小值 23 if (leftval != -1 && rightval != -1) return Math.min(leftval, rightval); 24 //左节点为叶子节点了,就返回左节点的值。右节点为叶子节点了,就返回右节点的值。 25 if (leftval != -1) return leftval; 26 return rightval; 27 } 28 }
分析:
这道题我真的不会!!
树(递归)专题完结撒花~