一、B树基础
- 满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。
- 完全二叉树:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2^(h-1) 个节点。
- 二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树
- 平衡二叉树:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二、存储方式
- 链式存储方式-指针:
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; } }
- 顺序存储方式-数组:若父节点数组下标是 i,那么左子树就是 i * 2 + 1,右子树是 i * 2 + 2;
三、遍历方式
- DFS
class Solution { //1、前序遍历 // 递归法 List<Integer> list = new ArrayList(); public List<Integer> preorderTraversal(TreeNode root) { dfs(root); return list; } public void dfs(TreeNode root) { if (root == null) { return; } list.add(root.val); dfs(root.left); dfs(root.right); } //迭代法 public List<Integer> preorderTraversal(TreeNode root) { if (root == null) { return list; } Deque<TreeNode> deque = new LinkedList<>(); deque.push(root); while (!deque.isEmpty()) { TreeNode curr = deque.pop(); list.add(curr.val); if (null != curr.right) { deque.push(curr.right); } if (null != curr.left) { deque.push(curr.left); } } return list; } //2、中序遍历 //递归法 public List<Integer> inorderTraversal(TreeNode root) { dfs(root); return list; } public void dfs(TreeNode root) { if (root == null) { return; } dfs(root.left); list.add(root.val); dfs(root.right); } //迭代法 public List<Integer> inorderTraversal(TreeNode root) { Deque<TreeNode> deque = new LinkedList<>(); while (!deque.isEmpty() || root != null) { while (root != null) { deque.push(root); root = root.left; } TreeNode pop = deque.pop(); list.add(pop.val); root = pop.right; } return list; } //3、后序遍历 //递归法 public List<Integer> postorderTraversal(TreeNode root) { dfs(root); return list; } public void dfs(TreeNode root) { if (root == null) { return; } dfs(root.left); dfs(root.right); list.add(root.val); } //迭代法 public List<Integer> postorderTraversal(TreeNode root) { if (root == null) { return list; } Deque<TreeNode> deque = new LinkedList<>(); deque.push(root); while (!deque.isEmpty()) { TreeNode curr = deque.pop(); list.add(0, curr.val); if (null != curr.left) { deque.push(curr.left); } if (null != curr.right) { deque.push(curr.right); } } return list; } }
- BFS
class Solution { //层序遍历 public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> lists = new ArrayList<>(); if (root == null) { return lists; } Deque<TreeNode> deque = new LinkedList<>(); deque.offer(root); while (!deque.isEmpty()) { List<Integer> list = new ArrayList<>(); for (int i = deque.size(); i > 0; i--) { TreeNode curr = deque.poll(); list.add(curr.val); if (curr.left != null) { deque.offer(curr.left); } if (curr.right != null) { deque.offer(curr.right); } } lists.add(list); } return lists; } }
四、二叉树的属性
-
对称二叉树
-
题目
- 给你一个二叉树的根节点
root
, 检查它是否轴对称。
- 给你一个二叉树的根节点
-
思路
- 左子树的左与右子树的右相比;
- 左子树的右与右子树的左相比;
- 从下往上递归或者层序遍历
-
代码
class Solution { //递归 public boolean isSymmetric(TreeNode root) { return dfs(root.left, root.right); } public boolean dfs(TreeNode left, TreeNode right) { if (left == null && right == null) { return true; } return left != null && right != null ? left.val == right.val && dfs(left.left, right.right) && dfs(left.right, right.left) : false; } //迭代 public boolean isSymmetric(TreeNode root) { Deque<TreeNode> deque = new LinkedList<>(); deque.offer(root.left); deque.offer(root.right); while (!deque.isEmpty()) { TreeNode left = deque.poll(); TreeNode right = deque.poll(); if (left == null && right == null) { continue; } if (left != null && right == null || left == null && right != null || left.val != right.val) { return false; } deque.offer(left.left); deque.offer(right.right); deque.offer(left.right); deque.offer(right.left); } return true; } }
-
-
二叉树的最大深度
-
题目
-
给定一个二叉树
root
,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
-
-
思路
- 递归-比较左右子树的深度
- 迭代-层数即最大深度
-
代码
class Solution { public int maxDepth(TreeNode root) { return dfs(root); } public int dfs(TreeNode root) { if (root == null) { return 0; } return 1 + Math.max(dfs(root.left), dfs(root.right)); } }
-
-
二叉树的最小深度
-
题目
-
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
-
-
思路
- 左右子节点均存在,则取最小 + 1
- 若有子节点不存在,则取另一子节点的深度 + 1
-
;代码
class Solution { public int minDepth(TreeNode root) { return dfs(root); } public int dfs(TreeNode root) { if (root == null) { return 0; } if (root.left == null) { return 1 + dfs(root.right); } if (root.right == null) { return 1 + dfs(root.left); } return 1 + Math.min(dfs(root.left), dfs(root.right)); } }
-
-
完全二叉树的节点个数
-
题目
- 给你一棵 完全二叉树 的根节点
root
,求出该树的节点个数。
- 给你一棵 完全二叉树 的根节点
-
思路
- 遍历二叉树
- 或者寻找满二叉树(完全二叉树可由满二叉树组成)
-
代码
class Solution { public int countNodes(TreeNode root) { return dfs(root); } public int dfs(TreeNode root) { if (root == null) { return 0; } return 1 + dfs(root.left) + dfs(root.right); } //满二叉树的节点数(2 ^ n) - 1 public int countNodes(TreeNode root) { if (root == null) { return 0; } int leftDp = 0; int rightDp = 0; TreeNode left = root.left; TreeNode right = root.right; while (left != null) { left = left.left; leftDp++; } while (right != null) { right = right.right; rightDp++; } if (rightDp == leftDp) { return (2 << leftDp) - 1; } return 1 + countNodes(root.left) + countNodes(root.right); } }
-
-
平衡二叉树
-
题目
-
给定一个二叉树,判断它是否是高度平衡的二叉树。
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
-
-
思路
- 比较左右子树高度差
- 判断左右子树是否为AVL
-
代码
class Solution { public boolean isBalanced(TreeNode root) { return dfs(root) != -1; } public int dfs(TreeNode root) { if (root == null) { return 0; } int left = dfs(root.left); int right = dfs(root.right); if (left == -1 || right == -1 || Math.abs(left - right) > 1) { return -1; } return 1 + Math.max(left, right); } }
-
-
二叉树的所有路径
-
题目
- 给你一个二叉树的根节点
root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
- 给你一个二叉树的根节点
-
思路
- dfs(回溯)
-
代码
class Solution { List<Integer> sb = new ArrayList<>(); List<String> list = new ArrayList<>(); public List<String> binaryTreePaths(TreeNode root) { dfs(root); return list; } public void dfs(TreeNode root) { if (root.right == null && root.left == null) { StringBuilder temp = new StringBuilder(); for (int i = 0; i < sb.size(); i++) { temp.append(sb.get(i)).append("->"); } temp.append(root.val); list.add(temp.toString()); return; } if (null != root.left) { sb.add(root.val); dfs(root.left); sb.remove(sb.size() - 1); } if (null != root.right) { sb.add(root.val); dfs(root.right); sb.remove(sb.size() - 1); } } }
-
-
左叶子之和
-
题目
- 给定二叉树的根节点
root
,返回所有左叶子之和。
- 给定二叉树的根节点
-
思路:
- 遍历DFS/BFS
- 左节点是叶子结点则记录
-
代码
class Solution { int res = 0; public int sumOfLeftLeaves(TreeNode root) { dfs(root); return res; } public void dfs(TreeNode root) { if (null == root) { return; } dfs(root.left); dfs(root.right); if (root.left != null && root.left.left == null && root.left.right == null) { res += root.left.val; } } }
-
-
找树左下角的值
-
题目
- 给定一个二叉树的 根节点
root
,请找出该二叉树的 最底层 最左边 节点的值。
- 给定一个二叉树的 根节点
-
思路
- BFS最后一层的第一个值
- 或者DFS深度变化时记录节点值
-
代码
class Solution { int deepth = 0; int res = 0; public int findBottomLeftValue(TreeNode root) { res = root.val; dfs(root, 0); return res; } //DFS public void dfs(TreeNode root, int deep) { if (root == null) { return; } if (deep > deepth) { deepth = deep; res = root.val; } dfs(root.left, deep + 1); dfs(root.right, deep + 1); } //BFS public int findBottomLeftValue(TreeNode root) { int res = 0; Deque<TreeNode> deque = new LinkedList<>(); deque.offer(root); List<Integer> list = null; while (!deque.isEmpty()) { list = new ArrayList<>(); for (int i = deque.size(); i > 0; i--) { TreeNode curr = deque.poll(); list.add(curr.val); if (curr.left != null) { deque.offer(curr.left); } if (curr.right != null) { deque.offer(curr.right); } } } return list.get(0); } }
-
-
路径总和
-
题目
- 给你二叉树的根节点
root
和一个表示目标和的整数targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和targetSum
。如果存在,返回true
;否则,返回false
。
- 给你二叉树的根节点
-
思路
- DFS遍历
- 到叶子节点,判断是否满足条件
-
代码
class Solution { public boolean hasPathSum(TreeNode root, int targetSum) { if (root == null) { return false; } if (root.left == null && root.right == null) { return targetSum == root.val; } return hasPathSum(root.left, targetSum - root.val) || hasPathSum(root.right, targetSum - root.val); } }
-
五、二叉树的修改&构造
-
翻转二叉树
-
题目
- 给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。
- 给你一棵二叉树的根节点
-
思路:
- 递归:从下往上,交换左右节点
- 迭代:层序遍历。交换左右节点
-
代码
class Solution { public TreeNode invertTree(TreeNode root) { // 从下往上 dfs(root); return root; } public TreeNode dfs(TreeNode root) { if (null == root) { return null; } TreeNode left = root.left; TreeNode right = root.right; root.right = dfs(left); root.left = dfs(right); return root; } }
-
-
从中序与后序遍历序列构造二叉树
-
题目
- 给定两个整数数组
inorder
和postorder
,其中inorder
是二叉树的中序遍历,postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
- 给定两个整数数组
-
思路:
- 根据后序遍历拆分中序遍历
- 再根据中序遍历拆分后序遍历
- 构建二叉树
-
代码
class Solution { public TreeNode buildTree(int[] inorder, int[] postorder) { return dfs(inorder, 0, inorder.length, postorder, 0, postorder.length); } public TreeNode dfs(int[] inorder, int iStart, int iEnd, int[] postorder, int pStart, int pEnd) { if (iStart >= iEnd || pStart >= pEnd) { return null; } if (iEnd - iStart == 1) { return new TreeNode(inorder[iStart]); } TreeNode root = new TreeNode(postorder[pEnd - 1]); int mid = 0; for (int i = iStart; i < iEnd; i++) { if (inorder[i] == root.val) { mid = i; break; } } root.left = dfs(inorder, iStart, mid, postorder, pStart, pStart + mid - iStart); root.right = dfs(inorder, mid + 1, iEnd, postorder, pStart + mid - iStart, pEnd - 1); return root; } }
-
-
最大二叉树
-
题目
-
给定一个不重复的整数数组
nums
。 最大二叉树 可以用下面的算法从nums
递归地构建:创建一个根节点,其值为nums
中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后缀上 构建右子树。
-
-
思路
- 找寻最大值位置,根据最大值拆分成左右数组
- 分别递归处理左右数组
-
代码
class Solution { public TreeNode constructMaximumBinaryTree(int[] nums) { return dfs(nums, 0, nums.length); } public TreeNode dfs(int[] nums, int start, int end) { if (start >= end) { return null; } if (end - start == 1) { return new TreeNode(nums[start]); } int max = 0; int index = start; for (int i = start; i < end; i++) { if (nums[i] > max) { index = i; max = nums[i]; } } TreeNode root = new TreeNode(max); root.left = dfs(nums, start, index); root.right = dfs(nums, index + 1, end); return root; } }
-
-
合并二叉树
-
题目
-
给你两棵二叉树:
root1
和root2
。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。
-
-
思路
- 递归遍历
- 处理节点
-
代码
class Solution { public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { return dfs(root1, root2); } public TreeNode dfs(TreeNode root1, TreeNode root2) { if (root1 == null) { return root2; } if (root2 == null) { return root1; } TreeNode root = new TreeNode(root1.val + root2.val); root.left = dfs(root1.left, root2.left); root.right = dfs(root1.right, root2.right); return root; } }
-
六、二叉搜索树
-
二叉搜索树中的搜索
-
题目
-
给定二叉搜索树(BST)的根节点
root
和一个整数值val
。你需要在 BST 中找到节点值等于val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回null
。
-
-
思路:
- BST是一个有序树
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉搜索树
- BST是一个有序树
-
代码
class Solution { public TreeNode searchBST(TreeNode root, int val) { return dfs(root, val); } //递归 public TreeNode dfs(TreeNode root, int val) { if (root == null) { return null; } if (root.val == val) { return root; } return root.val > val ? dfs(root.left, val) : dfs(root.right, val); } //迭代 public TreeNode searchBST(TreeNode root, int val) { TreeNode res = null; while (root != null) { if (root.val == val) { res = root; break; } if (root.val > val) { root = root.left; } else { root = root.right; } } return res; } }
-
-
验证二叉搜索树
-
题目
- 给你一个二叉树的根节点
root
,判断其是否是一个有效的二叉搜索树。
- 给你一个二叉树的根节点
-
思路
- 根据BST的定义
- 或者BST的中序遍历是严格升序的
-
代码
class Solution { public boolean isValidBST(TreeNode root) { return dfs(root); } public boolean dfs(TreeNode root) { if (null == root) { return true; } if (root.left != null) { TreeNode curr = root.left; while (curr.right != null) { curr = curr.right; } if (root.val <= curr.val) { return false; } } if (root.right != null) { TreeNode curr = root.right; while (curr.left != null) { curr = curr.left; } if (root.val >= curr.val) { return false; } } return dfs(root.left) && dfs(root.right); } }
-
-
二叉搜索树的最小绝对值
-
题目
-
给你一个二叉搜索树的根节点
root
,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。
-
-
思路
- 中序遍历
-
代码
class Solution { public int getMinimumDifference(TreeNode root) { return dfs(root); } public int dfs(TreeNode root) { int res = Integer.MAX_VALUE; int pre = -1; Deque<TreeNode> deque = new LinkedList<>(); while (!deque.isEmpty() || root != null) { while (root != null) { deque.push(root); root = root.left; } TreeNode curr = deque.pop(); int curVal = curr.val; if (pre != -1) { res = Math.min(res, curVal - pre); } pre = curVal; root = curr.right; } return res; } public int getMinimumDifference(TreeNode root) { return dfs(root); } public int dfs(TreeNode root) { if (null == root) { return Integer.MAX_VALUE; } int left = Integer.MAX_VALUE; int right = Integer.MAX_VALUE; if (root.left != null) { TreeNode curr = root.left; while (curr.right != null) { curr = curr.right; } left = root.val - curr.val; } if (root.right != null) { TreeNode curr = root.right; while (curr.left != null) { curr = curr.left; } right =curr.val - root.val; } return Math.min(Math.min(left, right), Math.min(dfs(root.left), dfs(root.right))); } }
-
-
二叉搜索树中的众数
-
题目
-
给你一个含重复值的二叉搜索树(BST)的根节点
root
,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。如果树中有不止一个众数,可以按 任意顺序 返回。
-
-
思路
- 二叉树遍历
- 或者BST中序遍历
-
代码
class Solution { public int[] findMode(TreeNode root) { int res = 0; Deque<TreeNode> deque = new LinkedList<>(); Map<Integer, Integer> map = new HashMap<>(); while (!deque.isEmpty() || root != null) { while (root != null) { deque.push(root); root = root.left; } TreeNode curr = deque.pop(); map.put(curr.val, map.getOrDefault(curr.val, 0) + 1); res = Math.max(res, map.get(curr.val)); root = curr.right; } List<Integer> list = new ArrayList(); for (Map.Entry<Integer, Integer> entry : map.entrySet()) { if (entry.getValue() == res) { list.add(entry.getKey()); } } return list.stream().mapToInt(i -> i).toArray(); } //BST特性 class Solution { public int[] findMode(TreeNode root) { int res = 0; int max = 0; TreeNode pre = null; List<Integer> list = new ArrayList(); Deque<TreeNode> deque = new LinkedList<>(); while (!deque.isEmpty() || root != null) { while (root != null) { deque.push(root); root = root.left; } TreeNode curr = deque.pop(); if (pre == null || pre.val != curr.val) { res = 1; } else if (pre.val == curr.val) { res++; } pre = curr; if (res > max) { max = res; list.clear(); } if (res == max) { list.add(curr.val); } root = curr.right; } return list.stream().mapToInt(i -> i).toArray(); } } }
-
-
把二叉搜索树转换为累加数
-
题目
- 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点
node
的新值等于原树中大于或等于node.val
的值之和。
- 给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点
-
思路
- 右中左遍历
-
代码
class Solution { int res = 0; public TreeNode convertBST(TreeNode root) { dfs(root); return root; } public void dfs(TreeNode root) { if (null == root) { return; } dfs(root.right); root.val = root.val + res; res = root.val; dfs(root.left); } }
-
七、公共祖先
-
二叉树的最近公共祖先
-
题目
- 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
-
思路
- 后序遍历
-
代码
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { return dfs(root, p, q); } public TreeNode dfs(TreeNode root, TreeNode p, TreeNode q) { if (root == null || root == p || root == q) { return root; } TreeNode left = dfs(root.left, p, q); TreeNode right = dfs(root.right, p, q); if (left != null && right != null) { return root; } if (left == null ) { return right; } return left; } }
-
-
二叉搜索树的最近公共祖先
-
题目
- 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
-
思路
- 二叉搜索树自带方向性,可以方便的从上向下查找目标区间,遇到目标区间内的节点,直接返回。
-
代码
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { TreeNode res = null; while (root != null) { if (root.val >= p.val && root.val <= q.val || root.val >= q.val && root.val <= p.val) { res = root; break; } else if (root.val < p.val && root.val < q.val) { root = root.right; } else if (root.val > p.val && root.val > q.val) { root = root.left; } } return res; } public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { if (root.val < p.val && root.val < q.val) { return lowestCommonAncestor(TreeNode root.right, TreeNode p, TreeNode q); } if (root.val > p.val && root.val > q.val) { return lowestCommonAncestor(TreeNode root.left, TreeNode p, TreeNode q); } return root; } }
-