leetcode hot100 二叉树
8️⃣ 二叉树
94. 二叉树的中序遍历
题解:
-
递归即可
-
public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList<>(); reverse(root, res); return res; } public void reverse(TreeNode root, List<Integer> res){ if(root == null){ return ; } reverse(root.left, res); res.add(root.val); reverse(root.right, res); }
104. 二叉树的最大深度
题解:
-
递归计算深度, 在当前节点比较左右节点深度的最大值即可
-
public int maxDepth(TreeNode root) { if(root == null){ return 0; } int max = reverse(root,0); return max; } public int reverse(TreeNode root, int depth){ if(root == null){ return depth; } return Math.max(reverse(root.left, depth + 1), reverse(root.right, depth + 1)); }
226. 翻转二叉树
题解:
-
递归从上至下依次交换左右节点即可
-
public TreeNode invertTree(TreeNode root) { if(root == null){ return null; } TreeNode temp = root.left; root.left = root.right; root.right = temp; invertTree(root.left); invertTree(root.right); return root; }
101. 对称二叉树
题解:
-
二叉树的题大多都是用递归解决的, 本题也是, 要判断根节点的左右两节点是否对称, 先判断左右两节点, 再判断left.leftright.right&&left.rightright.left
-
递归深度也是判断所需要对称的节点, 结束条件即判断到根节点
-
public boolean isSymmetric(TreeNode root) { if(root == null){ return true; } return reverse(root.left, root.right); } public boolean reverse(TreeNode left, TreeNode right){ if(left == null && right == null){ return true; }else if(left == null || right == null){ return false; } return left.val == right.val && reverse(left.left, right.right) && reverse(left.right, right.left); }
543. 二叉树的直径
题解:
-
本题最重要的就是怎么找到两个节点之间的最长路径
- 通过各种画图可以发现, 两个节点可以在根节点同一侧, 也可以在根节点两侧,
- 如果是在根节点两侧, 那也总会是在某一节点的两侧–最长直径总是大于该二叉树的最大深度的
-
既然总是在一个节点两侧, 那么可以递归计算在存在子节点的节点, 该节点左节点的最大深度+右节点的最大深度, 使用全局变量max来存储并每次比较它的大小, 最终返回最大值即可; 递归调用的返回值即是左右节点的最大深度
-
int max = 0; public int diameterOfBinaryTree(TreeNode root) { if(root == null){ return 0; } dfs(root); return max; } public int dfs(TreeNode root){ if(root.left==null&&root.right==null){ return 0; } int leftSize = root.left==null?0:dfs(root.left)+1; int rightSize = root.right==null?0:dfs(root.right)+1; max = Math.max(max, leftSize+rightSize); return Math.max(leftSize, rightSize); }
102. 二叉树的层序遍历
题解:
-
层序遍历, 本题用不到递归, 递归的本质是自己调用自己, 分解问题, 但本题无法合并情况
-
使用队列来辅助存储, 首先将根节点存入队列, 每弹出一个节点, 将其的左右节点存入队列(若存在), 直至左右节点空即队列空
-
public List<List<Integer>> levelOrder(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); if(root == null) return new ArrayList<>(); queue.add(root); List<List<Integer>> res = new ArrayList<>(); while(!queue.isEmpty()){ int size = queue.size(); List<Integer> list = new ArrayList<>(); while(size-- > 0 ){ TreeNode node = queue.poll(); list.add(node.val); if(node.left != null) queue.offer(node.left); if(node.right != null) queue.offer(node.right); } res.add(list); } return res; }
108. 将有序数组转换为二叉搜索树
题解:
-
平衡二叉树:
-
任意节点的左右子树高度差不超过 1。
-
换句话说,对于树中的任何一个节点,它的 左子树 和 右子树 的高度差(深度差)不会超过 1,否则就不是平衡二叉树。
-
-
二叉搜索树:
- 二叉搜索树的性质(每个节点的左子树值小于根,右子树值大于根)。
-
给出了升序排列的整数数组, 需要的是平衡的二叉搜索树,
-
使用二分递归, 首先将数组中间元素设置为根节点, 递归设置左节点为左半部分的中间元素, 右节点为右半部分的中间元素.
-
public TreeNode sortedArrayToBST(int[] nums) { int len = nums.length; if(len==0) return null; TreeNode root = new TreeNode(nums[len/2]); root.left = sortedArrayToBST(Arrays.copyOfRange(nums, 0, len/2)); root.right = sortedArrayToBST(Arrays.copyOfRange(nums, len/2+1, len)); return root; }
98. 验证二叉搜索树
题解:
-
递归,直到子节点则为true
-
如果父节点比当前已记录最小值小(在root上的最小值)或者比当前已记录最大值大,则为false
-
public boolean isValidBST(TreeNode root) { return validate(root,Long.MIN_VALUE,Long.MAX_VALUE); } public boolean validate(TreeNode node,long min,long max){ if(node == null){ return true; } if(node.val<=min || node.val>=max){ return false; } return validate(node.left,min,node.val)&&validate(node.right,node.val,max); }
230. 二叉搜索树中第 K 小的元素
题解:
-
最基础:二叉搜索树中序递归出来的序列即为有序序列, 获取第k-1个数据即可
-
public class biTree09 { public int kthSmallest(TreeNode root, int k) { List<Integer> res= new ArrayList<>(); mrever(root,res); return res.get(k-1); } public void mrever(TreeNode root,List<Integer> res){ if(root == null){ return; } mrever(root.left, res); res.add(root.val); mrever(root.right, res); } }
-
进阶思路:
-
递归思路,先统计左子树有多少结点, 若root刚好是第k个就直接返回,若左边结点大于等于k个,那所求结点在左子树,往左走。反之则往右走,那么我们右子树找的就是第k-左子树结点数-1个大的结点
-
-
int count(struct TreeNode* root) { if (root == NULL) { return 0; } return 1 + count(root->left) + count(root->right); } int kthSmallest(struct TreeNode* root, int k) { // 检查根节点是否为空 if (root == NULL) { return 0; // 或者其他表示错误的值 int a = count(root->left); if (a == k - 1) { return root->val; } else if (a > k - 1) { return kthSmallest(root->left, k); } else { return kthSmallest(root->right, k - a - 1); } }
199. 二叉树的右视图
题解:
-
层序遍历, 每次获取每层数据的最后一个数据添加到列表即可
-
public List<Integer> rightSideView(TreeNode root) { List<Integer> lst = new ArrayList<>(); if(root==null){ return lst; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while(!queue.isEmpty()){ List<Integer> level = new ArrayList<>(); int size=queue.size(); for(int i=0;i<size;i++){ TreeNode node = queue.poll(); if(node.left!=null){ queue.offer(node.left); } if(node.right!=null){ queue.offer(node.right); } level.add(node.val); } lst.add(level.get(level.size())-1); } return lst; }
114. 二叉树展开为链表
题解:
-
尾插法 将当前右子树放在其左孩子的最右侧节点的右孩子上,然后再将左孩子放到right,left置空,先序遍历即可。
-
public void flatten(TreeNode root) { while(root!=null){ TreeNode move=root.left; while(move!=null&&move.right!=null){ move=move.right; } if(move!=null){ move.right=root.right; root.right=root.left; root.left=null; } root=root.right; } }
105. 从前序与中序遍历序列构造二叉树
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
题解:
-
手动构造就能发现规律, 前序序列的第一个节点就是根节点, 然后构造一个哈希表存储中序序列的key-value,从哈希表中获取root所在的位置, left-root即为根节点的左子树, root-right即为根节点右子树,
-
左子树length可以由上计算得出, 而且两序列的子树长度是一样的, 因此root+left.length就可以在前序序列中找到左子树的范围, 同样的右子树…
-
递归调用函数, 依次找到子树中的’根’节点, 添加进左右子树即可
-
class Solution { private Map<Integer,Integer> indexMap; public TreeNode myBuildeTree(int[] preorder,int[] inorder,int preorder_left,int preorder_right,int inorder_left,int inorder_right){ if(preorder_left>preorder_right){ return null;} int preorder_root=preorder_left; int inorder_root=indexMap.get(preorder[preorder_root]); TreeNode root = new TreeNode(preorder[preorder_root]); int size_left_subtree = inorder_root - inorder_left; root.left = myBuildeTree(preorder,inorder,preorder_left+1,preorder_left+size_left_subtree,inorder_left,inorder_root-1); root.right = myBuildeTree(preorder,inorder,preorder_left+size_left_subtree+1,preorder_right,inorder_root+1,inorder_right); return root; } public TreeNode buildTree(int[] preorder, int[] inorder) { int n = preorder.length; indexMap = new HashMap<Integer,Integer>(); for(int i =0;i<n;i++){ indexMap.put(inorder[i],i); } return myBuildeTree(preorder,inorder,0,n-1,0,n-1); } }
437. 路径总和 III
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
题解:
-
之前做过一道连续数组求连续和为k的题目, 与那道题类似, 本题也是用前缀和, 不过是递归计算前缀和以及当前节点前缀和-targetNum=前面某一结点的前缀和, 如果存在则说明前面某一结点到该节点的和为target
-
使用一个Map来存储前缀和, Map<Long,Integer> Long存储的是前缀和, Integer存储的是该前缀和出现的次数, 因为前面节点的前缀和可能相同
-
要保证当前节点能map到的其他前缀和是该节点前面路径上的节点的前缀和, 使用前序遍历二叉树, 在每次递归前存储当前节点的前缀和, 在当前节点左右子树都递归完毕后移除当前节点的前缀和.
-
递归返回的则是当前节点以下的是否存在前缀和之差为target的数量
-
class Solution { public int pathSum(TreeNode root, int targetSum) { Map<Long,Integer> prefix = new HashMap<>(); prefix.put(0L,1); return dfs(root,prefix,0,targetSum); } public int dfs(TreeNode root,Map<Long,Integer> prefix,long curr,int targetSum){ if(root==null){ return 0; } curr+=root.val; int ret = prefix.getOrDefault(curr-targetSum,0); prefix.put(curr,prefix.getOrDefault(curr,0)+1); ret+=dfs(root.left,prefix,curr,targetSum); ret+=dfs(root.right,prefix,curr,targetSum); prefix.put(curr,prefix.getOrDefault(curr,0)-1); return ret; } }
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
题解:
-
dfs递归判断两个节点p,q :dfs(p,q)p是否为q的祖先
-
主函数先判断一个节点是否为另一个节点的祖先
-
如果二者不为对方的祖先,再判断两函数是否在某一节点的同侧,直至相异
-
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if(dfs(p,q)) return p; if(dfs(q,p)) return q; while(root!=null){ if(dfs(root.left,p)&&dfs(root.left,q)){ root = root.left; }else if(dfs(root.right,p)&&dfs(root.right,q)){ root = root.right; }else{ return root; } } return root; } public boolean dfs(TreeNode p,TreeNode q){ if(p==null) return false; if(p==q) return true; return dfs(p.left,q)||dfs(p.right,q); } }
124. 二叉树中的最大路径和
二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点 root ,返回其 最大路径和 。
题解:
-
要求两个东西, 第一个是节点的左右子树最大值, 第二个是当前节点及其子树的最大路径和,
-
左右子树最大值采用递归计算获得, node==null时返回0, 其余返回node.val+Math.max(leftGain,rightGain)
-
计算当前节点及其子树的最大路径和是要考虑当前路径和即为最大路径和的情况, 定义一个全局变量记录所出现的计算过的路径和的最大值, 最终返回这个值即可: maxSum = Math.max(maxSum,root.val+leftGain+rightGain)
-
class Solution { int maxSum = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { maxGain(root); return maxSum; } public int maxGain(TreeNode root){ if(root==null){ return 0; } int leftGain = Math.max(maxGain(root.left),0); int rightGain = Math.max(maxGain(root.right),0); int priceNewPath = root.val+leftGain+rightGain; maxSum = Math.max(maxSum,priceNewPath); return root.val+Math.max(leftGain,rightGain); } }