第四天 二叉树与递归
104. 二叉树的最大深度
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
class Solution {
public int maxDepth(TreeNode root) {
return root == null ? 0 : Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
}
111. 二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
public int minDepth(TreeNode root) {
if(root==null) return 0;
if(root.left==null&&root.right!=null) return minDepth(root.right)+1;
if(root.right==null&&root.left!=null) return minDepth(root.left)+1;
return Math.min(minDepth(root.left),minDepth(root.right))+1;
}
226. 翻转二叉树
翻转一棵二叉树。
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return root;
TreeNode temp=invertTree(root.left);
root.left=invertTree(root.right);
root.right=temp;
return root;
}
}
110. 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为: 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
return Math.abs(height(root.left)-height(root.right))<=1&&isBalanced(root.left)&&isBalanced(root.right) ;
}
public int height(TreeNode root){
if (root == null) return 0;
return Math.max(height(root.left),height(root.right))+1;
}
101. 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return recur(root.left,root.right);
}
public boolean recur(TreeNode A,TreeNode B){
if(A==null&&B==null) return true;
if(A==null||B==null) return false;
if(A.val==B.val)
return recur(A.left,B.right)&&recur(A.right,B.left);
return false;
}
100. 相同的树
给定两个二叉树,编写一个函数来检验它们是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
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);
}
222. 完全二叉树的节点个数
给出一个完全二叉树,求出该树的节点个数。
说明:
完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。
class Solution {
public int countNodes(TreeNode root) {
//分治前序遍历
return root==null ? 0 : countNodes(root.left)+countNodes(root.right)+1;
}
}
113. 路径总和 II
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
List<Integer> list=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
if(root==null) return res;
list.add(root.val);
sum-=root.val;
if(sum==0&&root.left==null&&root.right==null){
res.add(new ArrayList(list));
}
pathSum(root.left,sum);
pathSum(root.right,sum);
list.remove(list.size()-1);
return res;
}
112. 路径总和
给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。
说明: 叶子节点是指没有子节点的节点。
public boolean hasPathSum(TreeNode root, int sum) {
//根节点为空,直接返回false
if (root == null) {
return false;
}
// 到达叶子节点时,递归终止,判断 sum 是否符合条件。
if (root.left == null && root.right == null) {
return root.val == sum;
}
// 递归地判断root节点的左孩子和右孩子。
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}
404. 左叶子之和
计算给定二叉树的所有左叶子之和。
解法1
public int sumOfLeftLeaves(TreeNode root) {
if(root==null) return 0;
int sum=0;
if(root.left!=null&&root.left.right==null&&root.left.left==null) sum+=root.left.val;
return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right)+sum;
}
解法2
public int sumOfLeftLeaves(TreeNode root) {
if(root==null) return 0;
int sum=0;
if(root.left!=null&&root.left.right==null&&root.left.left==null)
return root.left.val+sumOfLeftLeaves(root.right);
return sumOfLeftLeaves(root.left)+sumOfLeftLeaves(root.right);
}
257. 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。说明: 叶子节点是指没有子节点的节点。
思路1
private List<String> res = new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
StringBuilder sb = new StringBuilder();
dfs(root, sb);
return res;
}
public void dfs(TreeNode root, StringBuilder sbr) {
if (root == null) return;
StringBuilder sb = new StringBuilder(sbr);
sb.append(root.val);
if (root.left == null && root.right == null) {
res.add(sb.toString());
return;
}
sb.append("->");
dfs(root.left, sb);
dfs(root.right, sb);
}
思路2
ArrayList<String> res=new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
if(root==null) return res;
dfs(root,"");
return res;
}
public void dfs(TreeNode root,String str){
if(root.left!=null)
dfs(root.left,str+root.val+"->");
if(root.right!=null)
dfs(root.right,str+root.val+"->");
if(root.left==null&&root.right==null)
res.add(str+root.val);
}
129. 求根到叶子节点数字之和
给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。例如,从根到叶子节点路径 1->2->3 代表数字 123。计算从根到叶子节点生成的所有数字之和。说明: 叶子节点是指没有子节点的节点。
class Solution {
int sum=0;
public int sumNumbers(TreeNode root) {
dfs(root,0);
return sum;
}
public void dfs(TreeNode root,int temp){
if(root==null) return;
if(root.left==null&&root.right==null) {
sum += temp * 10 + root.val;
return;
}
dfs(root.left,10*temp+root.val);
dfs(root.right,10*temp+root.val);
}
}
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
//二叉树
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null||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;
}
}
//二叉搜索树
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null) return root;
if(p.val<root.val&&q.val<root.val) {
return lowestCommonAncestor( root.left, p, q);
}
if(p.val>root.val&&q.val>root.val){
return lowestCommonAncestor( root.right, p, q);
}
return root;
}
}
98. 验证二叉搜索树
给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征:
节点的左子树只包含小于当前节点的数。
节点的右子树只包含大于当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
/**
*
* 方法一: 递归
*
* @param root
* @return
*/
public boolean isValidBST(TreeNode root) {
return helper(root, null, null);
}
public boolean helper(TreeNode node, Integer lower, Integer upper) {
if (node == null) return true;
int val = node.val;
if (lower != null && val <= lower) return false;
if (upper != null && val >= upper) return false;
if (! helper(node.right, val, upper)) return false;
if (! helper(node.left, lower, val)) return false;
return true;
}
/**
*
* 方法2 中序遍历 时间复杂度O(N) 空间复杂度O(N)
*
*
*/
public boolean isValidBST2(TreeNode root) {
Stack<TreeNode> stack=new Stack<>();
Integer a=null;
while(!stack.isEmpty()||root!=null) {
if (root != null) {
stack.push(root);
root = root.left;
} else {
TreeNode temp=stack.pop();
if(a!=null&&temp.val<=a) return false;
a=temp.val;
root=temp.right;
}
}
return true;
}
230. 二叉搜索树中第K小的元素
给定一个二叉搜索树,编写一个函数 kthSmallest 来查找其中第 k 个最小的元素。说明:你可以假设 k 总是有效的,1 ≤ k ≤ 二叉搜索树元素个数。
class Solution {
ArrayList<Integer> res=new ArrayList<>();
public int kthSmallest(TreeNode root, int k) {
recur(root);
return res.get(k-1);
}
public void recur(TreeNode root){
if(root==null) return;
if(root.left!=null) recur(root.left);
res.add(root.val);
if(root.right!=null) recur(root.right);
}
}
108. 将有序数组转换为二叉搜索树
将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。
public TreeNode sortedArrayToBST(int[] nums) {
// 左右等分建立左右子树,中间节点作为子树根节点,递归该过程
return nums == null ? null : recur(nums, 0, nums.length - 1);
}
public TreeNode recur(int nums[],int l,int r){
if(l>r) return null;
int mid=(l+r)>>1;
TreeNode root=new TreeNode(nums[mid]);
root.left=recur(nums,0,mid-1);
root.right=recur(nums,mid+1,r);
return root;
}
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
说明: 要求算法时间复杂度为 O(h),h 为树的高度。
437. 路径总和 III
给定一个二叉树,它的每个结点都存放着一个整数值。找出路径和等于给定数值的路径总数。
路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。
剑指 Offer 26. 树的子结构
输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构, 即 A中有出现和B相同的结构和节点值。
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if(A==null&&B==null) return true;
// if(A==null||B==null) return false;
recur(s,t);
}
public boolean recur(TreeNode A,TreeNode B){}{
if(B==null) return true;
if(A==null) return false;
if(A.val==B.val){
return recur(A.left,B.left)&&recur(A.right,B.right);
}
}
}
572. 另一个树的子树
给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
if(s==null&&t==null) return true;
if(s==null||t==null) return false;
//两棵树是否相等,s左子树和t相等,s右子树和t相等
return recur(s,t)||isSubtree(s.left,t)||isSubtree(s.right,t);
}
//判断两棵树是否相等,根节点值相等,左子树和右子树一样
public boolean recur(TreeNode A,TreeNode B){
if(B==null&&A==null) return true;
if(A==null||B==null) return false;
return A.val==B.val&&recur(A.left,B.left)&&recur(A.right,B.right);
}
}
617. 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1==null) return t2;
if(t2==null) return t1;
// 先合并根节点
t1.val += t2.val;
// 再递归合并左右子树
t1.left=mergeTrees(t1.left,t2.left);
t1.right=mergeTrees(t1.right,t2.right);
return t1;
}