1,二叉树
101,对称二叉树
题目:给你一个二叉树的根节点
root
, 检查它是否轴对称。class Solution { public boolean isSymmetric(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); List result = new ArrayList<>(); queue.offer(root);//根节点先入队 while (!queue.isEmpty()) { int levelNum = queue.size(); if (levelNum >= 2 && levelNum % 2 != 0) { return false; } ArrayList temp = new ArrayList(); for (int i = 0; i < levelNum; i++) { TreeNode node = queue.poll(); if (node != null) { if (node.left != null) { queue.offer(node.left); temp.add(node.left.val); } else { temp.add(101); } if (node.right != null) { queue.offer(node.right); temp.add(node.right.val); } else { temp.add(101); } } } int left = 0; int right = temp.size() - 1; while (left < right) { if (temp.get(left) != temp.get(right)) { return false; } left++; right--; } } return true; } }
104,二叉树最大深度
题目:给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。说明: 叶子节点是指没有子节点的节点。
class Solution { public int maxDepth(TreeNode root) { if (root == null) return 0; int lh = maxDepth(root.left); //返回左子树的高度 int rh = maxDepth(root.right); //返回右子树的高度 return (lh >= rh) ? lh + 1 : rh + 1; } }
112,路径总和
题目:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
class Solution { boolean ans = false; public boolean hasPathSum(TreeNode root, int sum) { if (root == null) { return false; } dfs(root,sum); return ans; } private void dfs(TreeNode root,int sum) { if (root == null) { return; } if (root.left == null && root.right == null) { if (sum == root.val) { ans = true; } } dfs(root.left,sum - root.val); dfs(root.right,sum - root.val); } }
113,路径总和 II
题目:给你二叉树的根节点
root
和一个整数目标和targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。class Solution { List<List<Integer>> res = new ArrayList<>(); public List<List<Integer>> pathSum(TreeNode root, int targetSum) { dfs(root, new ArrayList<>(),0,targetSum); return res; } private void dfs(TreeNode root,List<Integer> path,int sum,int targetSum){ if(root == null) return; path.add(root.val); if(root.left == null && root.right == null && root.val + sum == targetSum){ res.add(new ArrayList<>(path)); } dfs(root.left,path,sum+root.val,targetSum); dfs(root.right,path,sum+root.val,targetSum); path.remove(path.size()-1); } }
437,路径总和 III
题目:给定一个二叉树的根节点
root
,和一个整数targetSum
,求该二叉树里节点值之和等于targetSum
的 路径 的数目。路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。class Solution { int cnt = 0; public int pathSum(TreeNode root, int targetSum) { if(root == null) { return 0; } dfs(root, targetSum); pathSum(root.left, targetSum); pathSum(root.right, targetSum); return cnt; } private void dfs(TreeNode root, long targetSum) { if(root == null) { return; } if(root.val == targetSum) { cnt++; } dfs(root.left, targetSum - root.val); dfs(root.right, targetSum - root.val); } }
543,二叉树的直径
题目:给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
class Solution { private int ret = Integer.MIN_VALUE; public int diameterOfBinaryTree(TreeNode root) { getMax(root); return ret-1; } private int getMax(TreeNode r) { if (r == null) return 0; int left = Math.max(0, getMax(r.left)); // 如果子树路径和为负则应当置0表示最大路径不包含子树 int right = Math.max(0, getMax(r.right)); ret = Math.max(ret, 1 + left + right); // 判断在该节点包含左右子树的路径和是否大于当前最大路径和 return Math.max(left, right) + 1; } }
124,二叉树中的最大路径和
题目:路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。给你一个二叉树的根节点
root
,返回其 最大路径和 。class Solution { private int ret = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { /** 对于任意一个节点, 如果最大和路径包含该节点, 那么只可能是两种情况: 1. 其左右子树中所构成的和路径值较大的那个加上该节点的值后向父节点回溯构成最大路径 2. 左右子树都在最大路径中, 加上该节点的值构成了最终的最大路径 **/ getMax(root); return ret; } private int getMax(TreeNode r) { if(r == null) return 0; int left = Math.max(0, getMax(r.left)); // 如果子树路径和为负则应当置0表示最大路径不包含子树 int right = Math.max(0, getMax(r.right)); ret = Math.max(ret, r.val + left + right); // 判断在该节点包含左右子树的路径和是否大于当前最大路径和 return Math.max(left, right) + r.val; } }
129,求根节点到叶节点数字之和
题目:给你一个二叉树的根节点
root
,树中每个节点都存放有一个0
到9
之间的数字。每条从根节点到叶节点的路径都代表一个数字:
- 例如,从根节点到叶节点的路径
1 -> 2 -> 3
表示数字123
。计算从根节点到叶节点生成的 所有数字之和 。叶节点 是指没有子节点的节点。
class Solution { public int sumNumbers(TreeNode root) { return dfs(root, 0); } public int dfs(TreeNode root, int prevSum) { if (root == null) { return 0; } int sum = prevSum * 10 + root.val; if (root.left == null && root.right == null) { return sum; } else { return dfs(root.left, sum) + dfs(root.right, sum); } } }
226,翻转二叉树
题目:给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。class Solution { public TreeNode invertTree(TreeNode root) { if (root==null){ return null; } Queue<TreeNode> queue = new LinkedList(); queue.offer(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (node.left != null || node.right != null) { TreeNode a = node.left; node.left = node.right; node.right = a; if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } } return root; } }
236,二叉树的最近公共祖先
题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
class Solution { ArrayList<TreeNode> res = new ArrayList<>(); public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { ArrayList<TreeNode> a = findNode(root, p); ArrayList<TreeNode> b = findNode(root, q); int f = Math.min(a.size(), b.size()); for (int i = 0; i < f; i++) { if (a.get(i).val != b.get(i).val) { return a.get(i - 1); } } return a.get(f - 1); } public ArrayList<TreeNode> findNode(TreeNode root, TreeNode node) { res = new ArrayList<>(); dfs(root, new ArrayList<>(), node); return res; } private void dfs(TreeNode root, List<TreeNode> path, TreeNode node) { if (root == null) return; path.add(root); if (root.val == node.val) { res = new ArrayList<>(path); } dfs(root.left, path, node); dfs(root.right, path, node); path.remove(path.size() - 1); } }
297,二叉树的序列化与反序列化
题目:序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
思路:以标明空子树的先根次序遍历序列建立二叉树(地址)
public class Codec { String a = ""; public String serialize(TreeNode root) { if (root == null) { return ""; } preorderTraversal(root); return a; } public TreeNode deserialize(String data) { if (data.equals("")) { return null; } String[] f = data.split(" "); return create(f); } private int i = 0; private TreeNode create(String[] prelist) { TreeNode p = null; if (i < prelist.length) { String elem = prelist[i]; i++; if (!elem.equals("^")) { //不能elem!="∧",因为T不一定是String p = new TreeNode(Integer.parseInt(elem)); //创建叶子结点 p.left = create(prelist); //创建p的左子树,递归调用,实际参数与形式参数相同 p.right = create(prelist); //创建p的右子树,递归调用,实际参数与形式参数相同 } } return p; } public String preorderTraversal(TreeNode root) { if (root != null) { a += root.val + " "; preorderTraversal(root.left); //按先根次序遍历p的左子树,递归调用,参数为左孩子 preorderTraversal(root.right); //按先根次序遍历p的右子树,递归调用,参数为右孩子 } else { a += "^ "; } return a; } }
404,左叶子之和
题目:给定二叉树的根节点
root
,返回所有左叶子之和。class Solution { int sum = 0; public int sumOfLeftLeaves(TreeNode root) { if (root == null) return 0; if (root.left != null && root.left.left == null && root.left.right == null) { sum += root.left.val; } sumOfLeftLeaves(root.left); sumOfLeftLeaves(root.right); return sum; } }
100,相同的树
题目:给你两棵二叉树的根节点
p
和q
,编写一个函数来检验这两棵树是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。class Solution { public boolean isSameTree(TreeNode p, TreeNode q) { return dfs(p, q); } public boolean dfs(TreeNode s, TreeNode t) { if ((s == null && t != null) || (s != null && t == null)) { return false; } if (s == null && t == null) { return true; } return check(s, t) || dfs(s.left, t) || dfs(s.right, t); } public boolean check(TreeNode s, TreeNode t) { if (s == null && t == null) { return true; } if (s == null || t == null || s.val != t.val) { return false; } return check(s.left, t.left) && check(s.right, t.right); } }
572,另一颗树的子树
题目:给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
思路:深度优先搜索枚举 s 中的每一个节点,判断这个点的子树是否和 t 相等。如何判断一个节点的子树是否和 t 相等呢,我们又需要做一次深度优先搜索来检查,即让两个指针一开始先指向该节点和 t 的根,然后「同步移动」两根指针来「同步遍历」这两棵树,判断对应位置是否相等。
class Solution { public boolean isSubtree(TreeNode s, TreeNode t) { return dfs(s, t); } public boolean dfs(TreeNode s, TreeNode t) { if (s == null) { return false; } return check(s, t) || dfs(s.left, t) || dfs(s.right, t); } public boolean check(TreeNode s, TreeNode t) { if (s == null && t == null) { return true; } if (s == null || t == null || s.val != t.val) { return false; } return check(s.left, t.left) && check(s.right, t.right); } }
子结构:输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构, 即 A中有出现和B相同的结构和节点值。
上图两棵树是子结构,但并非子树。
class Solution { public boolean isSubStructure(TreeNode A, TreeNode B) { if (B == null) { return false; } return dfs(A, B); } public boolean dfs(TreeNode s, TreeNode t) { if (s == null) { return false; } return check(s, t) || dfs(s.left, t) || dfs(s.right, t); } public boolean check(TreeNode s, TreeNode t) { if (t == null) { return true; } if (s == null || s.val != t.val) { return false; } return check(s.left, t.left) && check(s.right, t.right); } }
617,合并二叉树
题目:给你两棵二叉树: root1 和 root2 。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。注意: 合并过程必须从两个树的根节点开始。
class Solution { public static TreeNode mergeTrees(TreeNode t1, TreeNode t2) { if (t1 == null) { return t2; } if (t2 == null) { return t1; } Queue<TreeNode> queue = new LinkedList(); Queue<TreeNode> queue1 = new LinkedList(); Queue<TreeNode> queue2 = new LinkedList(); TreeNode merged = new TreeNode(t1.val + t2.val); queue.offer(merged); queue1.offer(t1); queue2.offer(t2); while (!queue1.isEmpty() && !queue2.isEmpty()) { TreeNode temp = queue.poll(); TreeNode temp1 = queue1.poll(); TreeNode temp2 = queue2.poll(); TreeNode left1 = temp1.left; TreeNode left2 = temp2.left; TreeNode right1 = temp1.right; TreeNode right2 = temp2.right; if (left1 != null || left2 != null) { if (left1 != null && left2 != null) { TreeNode left = new TreeNode(left1.val + left2.val); temp.left = left; queue.offer(left); queue1.offer(left1); queue2.offer(left2); } else if (left1 != null) { temp.left = left1; } else if (left2 != null) { temp.left = left2; } } if (right1 != null || right2 != null) { if (right1 != null && right2 != null) { TreeNode right = new TreeNode(right1.val + right2.val); temp.right = right; queue.offer(right); queue1.offer(right1); queue2.offer(right2); } else if (right1 != null) { temp.right = right1; } else { temp.right = right2; } } } return merged; } }
814,二叉树剪枝
题目:给你二叉树的根结点
root
,此外树的每个结点的值要么是0
,要么是1
。返回移除了所有不包含1
的子树的原二叉树。节点node
的子树为node
本身加上所有node
的后代。class Solution { public TreeNode pruneTree(TreeNode root) { if (root == null) { return null; } root.left = pruneTree(root.left); root.right = pruneTree(root.right); if (root.left == null && root.right == null && root.val == 0) { return null; } return root; } }
968,监控二叉树
题目:给定一个二叉树,我们在树的节点上安装摄像头。节点上的每个摄影头都可以监视其父对象、自身及其直接子对象。计算监控树的所有节点所需的最小摄像头数量。
class Solution { int res = 0; public int minCameraCover(TreeNode root) { /** 情况4:根结点无覆盖状态,放置一个摄像头 (1)只有根节点 (2)左右孩子为有覆盖状态 */ if (minCamera(root) == 0) res++; return res; } /** 后序遍历 结点状态值 0 本结点无覆盖 1 本结点有摄像头 2 本结点有覆盖 */ public int minCamera(TreeNode root) { // 空结点默认有覆盖,避免在叶子结点放摄像头 if (root == null) return 2; // 遍历左右孩子 int left = minCamera(root.left); int right = minCamera(root.right); // 情况1:若左右结点都覆盖,则本结点无覆盖 if (left == 2 && right == 2) return 0; /** 情况2: 若左右结点存在无覆盖,则本结点放一个摄像头 (1)left = 0, right = 0 -> 左右结点均无覆盖 (2)left = 1, right = 0 -> 左结点有摄像头,右结点无覆盖 (3)left = 0, right = 1 -> 左结点无覆盖,右结点有摄像头 (4)left = 2, right = 0 -> 左结点有覆盖,右结点无覆盖 (5)left = 0, right = 2 -> 左结点无覆盖,右结点有覆盖 */ else if (left == 0 || right == 0) { res++; return 1; } /** 情况3: 左右节点至少有一个有摄像头,则本结点是覆盖状态 (1)left = 1, right = 2 -> 左结点有摄像头,右结点有覆盖 (2)left = 2, right = 1 -> 左结点有覆盖,右结点有摄像头 (3)left = 1, right = 1 -> 左右结点均有摄像头 */ // 左右结点存在摄像头,本结点处于覆盖状态 else return 2; } }
1367,二叉树中的列表
题目:给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径。
class Solution { public boolean isSubPath(ListNode head, TreeNode root) { return dfs(root, head); } public boolean dfs(TreeNode s, ListNode t) { if (s == null) { return false; } return check(s, t) || dfs(s.left, t) || dfs(s.right, t); } public boolean check(TreeNode s, ListNode t) { if (t == null) { return true; } if (s == null || t == null || s.val != t.val) { return false; } return check(s.left, t.next) || check(s.right, t.next); } }
2,二叉搜索树
33,二叉搜索树的后序遍历序列
题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回
true
,否则返回false
。假设输入的数组的任意两个数字都互不相同。思路:非递归,利用了性质:从任意一个结点k往左的这个子数组,比k大的都在右边,比k小的都在左边 [ [...<k...] [...>k...] k]
class Solution { public boolean verifyPostorder(int[] postorder) { //思路,从左往右迭代,依次成为当前结点 // 从当前节点往左看,碰到比它小的后,就不能碰到比它大的 boolean sign = false; for (int i = 0; i < postorder.length; i++) { for (int j = i; j >= 0; j--) { if (postorder[j] < postorder[i]) { //出现了比当前小的 sign = true; } //出现了比当前结点大的且之前已经出现了比当前结点小的 if (sign == true && postorder[j] > postorder[i]) return false; } sign = false; } return true; } }
33,数组➡二叉搜索树(二叉搜索树序列)
题目:从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉搜索树
root
,输出所有可能生成此树的数组。class Solution { private List<List<Integer>> ans; public List<List<Integer>> BSTSequences(TreeNode root) { ans = new ArrayList<>(); List<Integer> path = new ArrayList<>(); // 如果 root==null 返回 [[]] if (root == null) { ans.add(path); return ans; } List<TreeNode> queue = new LinkedList<>(); queue.add(root); // 开始进行回溯 bfs(queue, path); return ans; } /** * 回溯法+广度优先遍历. */ private void bfs(List<TreeNode> queue, List<Integer> path) { // queue 为空说明遍历完了,可以返回了 if (queue.isEmpty()) { ans.add(new ArrayList<>(path)); return; } // 将 queue 拷贝一份,用于稍后回溯 List<TreeNode> copy = new ArrayList<>(queue); // 对 queue 进行循环,每循环考虑 “是否 「将当前 cur 节点从 queue 中取出并将其左右子 // 节点加入 queue ,然后将 cur.val 加入到 path 末尾」 ” 的情况进行回溯 for (int i = 0; i < queue.size(); i++) { TreeNode cur = queue.get(i); path.add(cur.val); queue.remove(i); // 将左右子节点加入队列 if (cur.left != null) queue.add(cur.left); if (cur.right != null) queue.add(cur.right); bfs(queue, path); // 恢复 path 和 queue ,进行回溯 path.remove(path.size() - 1); queue = new ArrayList<>(copy); } } }
98,验证二叉搜索树
题目:给你一个二叉树的根节点
root
,判断其是否是一个有效的二叉搜索树。class Solution { List<Integer> a = new ArrayList(); public void inorderTraversal(TreeNode root) { if (root != null) { inorderTraversal(root.left); a.add(root.val); inorderTraversal(root.right); } } public boolean isValidBST(TreeNode root) { inorderTraversal(root); for (int i = 0; i < a.size() - 1; i++) { if (a.get(i) >= a.get(i + 1)) { return false; } } return true; } }
108,二叉搜索树构造(将有序数组转换为二叉搜索树)
题目:给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。
题目:给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。
class Solution { public TreeNode sortedArrayToBST(int[] nums) { return nums == null ? null : buildTree(nums, 0, nums.length - 1); } private TreeNode buildTree(int[] nums, int l, int r) { if (l > r) { return null; } int middle = l + (r - l) / 2; TreeNode root = new TreeNode(nums[middle]); root.left = buildTree(nums, l, middle - 1); root.right = buildTree(nums, middle + 1, r); return root; } }
173,二叉搜索树迭代器
题目:实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
- BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
- boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false 。
- int next()将指针向右移动,然后返回指针处的数字。
注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。
class BSTIterator { List<Integer> a = new ArrayList(); int index = 0; public BSTIterator(TreeNode root) { postorderTraversal(root); } public int next() { int result = a.get(index); index++; return result; } public boolean hasNext() { if (index >= a.size()) { return false; } else { return true; } } public List<Integer> postorderTraversal(TreeNode root) { if (root != null) { postorderTraversal(root.left); a.add(root.val); postorderTraversal(root.right); } return a; } }
230,二叉搜索树中第K小的元素
题目:给定一个二叉搜索树的根节点
root
,和一个整数k
,请你设计一个算法查找其中第k
个最小元素(从 1 开始计数)。class Solution { List<Integer> a = new ArrayList(); public int kthSmallest(TreeNode root, int k) { postorderTraversal(root); return a.get(k - 1); } public List<Integer> postorderTraversal(TreeNode root) { if (root != null) { postorderTraversal(root.left); a.add(root.val); postorderTraversal(root.right); } return a; } }
235,二叉搜索树的最近公共祖先
题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
思路:找到到达两个节点的轨迹,对比轨迹。
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { ArrayList<TreeNode> a = findNode(root, p); ArrayList<TreeNode> b = findNode(root, q); int f = Math.min(a.size(),b.size()); for (int i = 0; i < f; i++) { if (a.get(i).val != b.get(i).val) { return a.get(i - 1); } } return a.get(f - 1); } public ArrayList<TreeNode> findNode(TreeNode root, TreeNode node) { ArrayList result = new ArrayList(); while (root != null) { result.add(root); if (root.val == node.val) { return result; } else if (root.val > node.val) { root = root.left; } else if (root.val < node.val) { root = root.right; } } return null; } }
思路:利用二叉搜索树的特点,如果p、q的值都小于root,说明p q 肯定在root的左子树中;如果p q都大于root,说明肯定在root的右子树中,如果一个在左一个在右 则说明此时的root记为对应的最近公共祖先。
class Solution { public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { while (true) { if (root.val > p.val && root.val > q.val) { root = root.left; } else if (root.val < p.val && root.val < q.val) { root = root.right; } else { break; } } return root; } }
426,二叉搜索树与单/双向链表
题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
class Solution { Node pre, head; public Node treeToDoublyList(Node root) { // 边界值 if(root == null) return null; dfs(root); // 题目要求头尾连接 head.left = pre; pre.right = head; // 返回头节点 return head; } void dfs(Node cur) { // 递归结束条件 if(cur == null) return; dfs(cur.left); // 如果pre为空,就说明是第一个节点,头结点,然后用head保存头结点,用于之后的返回 if (pre == null) head = cur; // 如果不为空,那就说明是中间的节点。并且pre保存的是上一个节点, // 让上一个节点的右指针指向当前节点 else if (pre != null) pre.right = cur; // 再让当前节点的左指针指向父节点,也就连成了双向链表 cur.left = pre; // 保存当前节点,用于下层递归创建 pre = cur; dfs(cur.right); } }
题目:二叉树数据结构
TreeNode
可用来表示单向链表(其中left
置空,right
为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。返回转换后的单向链表的头节点。class Solution { TreeNode pre, head; public TreeNode convertBiNode(TreeNode root) { if (root == null) { return null; } dfs(root); return head; } private void dfs(TreeNode cur) { // 递归结束条件 if (cur == null) return; dfs(cur.left); // 如果pre为空,就说明是第一个节点,头结点,然后用head保存头结点,用于之后的返回 if (pre == null) head = cur; // 如果不为空,那就说明是中间的节点。并且pre保存的是上一个节点, // 让上一个节点的右指针指向当前节点 else if (pre != null) pre.right = cur; // 再让当前节点的左指针指向父节点,也就连成了双向链表 cur.left = null; // 保存当前节点,用于下层递归创建 pre = cur; dfs(cur.right); } }
450,删除二叉树搜索树中的节点
题目:给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
- 首先找到需要删除的节点;
- 如果找到了,删除它。
思路:
- root 为叶子节点,没有子树。此时可以直接将它删除,即返回空。
- root 只有左子树,没有右子树。此时可以将它的左子树作为新的子树,返回它的左子节点。
- root 只有右子树,没有左子树。此时可以将它的右子树作为新的子树,返回它的右子节点。
root 有左右子树,这时可以将 \textit{root}root 的后继节点(比 root 大的最小节点,即它的右子树中的最小节点,记为 successor)作为新的根节点替代 root,并将 successor 从 root 的右子树中删除,使得在保持有序性的情况下合并左右子树。
class Solution { public TreeNode deleteNode(TreeNode root, int key) { if (root == null) { return null; } if (root.val > key) { root.left = deleteNode(root.left, key); return root; } if (root.val < key) { root.right = deleteNode(root.right, key); return root; } if (root.val == key) { if (root.left == null && root.right == null) { return null; } if (root.right == null) { return root.left; } if (root.left == null) { return root.right; } TreeNode successor = root.right; while (successor.left != null) { successor = successor.left; } root.right = deleteNode(root.right, successor.val); successor.right = root.right; successor.left = root.left; return successor; } return root; } }
538,把二叉搜索树转化为累加树
题目:给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点
node
的新值等于原树中大于或等于node.val
的值之和。提醒一下,二叉搜索树满足下列约束条件:
- 节点的左子树仅包含键 小于 节点键的节点。
- 节点的右子树仅包含键 大于 节点键的节点。
- 左右子树也必须是二叉搜索树。
class Solution { int sum = 0; public TreeNode convertBST(TreeNode root) { if (root == null) { return root; } convertBST(root.right); root.val += sum; sum = root.val; convertBST(root.left); return root; } }
653,两数之和IV-输入BST
题目:给定一个二叉搜索树
root
和一个目标结果k
,如果 BST 中存在两个元素且它们的和等于给定的目标结果,则返回true
。class Solution { List<Integer> a = new ArrayList(); public boolean findTarget(TreeNode root, int k) { inorderTraversal(root); int left = 0; int right = a.size() - 1; while (left < right) { int temp = a.get(left) + a.get(right); if (temp > k) { right--; } else if (temp < k) { left++; } else if (temp == k) { return true; } } return false; } public List<Integer> inorderTraversal(TreeNode root) { if (root != null) { inorderTraversal(root.left); a.add(root.val); inorderTraversal(root.right); } return a; } }
700,二叉搜索树中的搜索
题目:给定二叉搜索树(BST)的根节点
root
和一个整数值val
。你需要在 BST 中找到节点值等于val
的节点。 返回以该节点为根的子树。 如果节点不存在,则返回null
。class Solution { public TreeNode searchBST(TreeNode root, int val) { Queue<TreeNode> queue = new LinkedList(); queue.offer(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (node.val == val) { return node; } if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } } return null; } }
701,二叉搜索树中的插入操作
题目:给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
class Solution { public TreeNode insertIntoBST(TreeNode root, int val) { if (root==null){ return new TreeNode(val); } Queue<TreeNode> queue = new LinkedList(); queue.offer(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (val > node.val) { if (node.right != null) { queue.offer(node.right); } else { node.right = new TreeNode(val); } } else { if (node.left != null) { queue.offer(node.left); } else { node.left = new TreeNode(val); } } } return root; } }
1038,从二叉搜索树到更大和树
题目:给定一个二叉搜索树 root (BST),请将它的每个节点的值替换成树中大于或者等于该节点值的所有节点值之和。
class Solution { int sum = 0; public TreeNode bstToGst(TreeNode root) { if (root == null) { return root; } bstToGst(root.right); root.val += sum; sum = root.val; bstToGst(root.left); return root; } }
3,层次遍历
102,二叉树的层序遍历
题目:给你二叉树的根节点
root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。class Solution { public List<List<Integer>> levelOrder(TreeNode root) { if (root == null) { return new ArrayList<>(); } Queue<TreeNode> queue = new LinkedList<>();//队列里面存放结点 List result = new ArrayList<>(); queue.offer(root);//根节点先入队 while (!queue.isEmpty()) { int levelNum = queue.size();//获取当前层的节点数. //遍历当前层结点 ArrayList temp = new ArrayList(); for (int i = 0; i < levelNum; i++) { //队首出队并将value加入子list TreeNode node = queue.poll(); if (node != null) { temp.add(node.val); if (node.left != null) {//如果队首的左结点不为空就把左结点入队 queue.offer(node.left); } if (node.right != null) {//如果队首的右结点不为空就把右结点入队 queue.offer(node.right); } } } result.add(temp); } return result; } }
103,二叉树的锯齿形层次遍历
题目:给你二叉树的根节点
root
,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。class Solution { public List<List<Integer>> zigzagLevelOrder(TreeNode root) { if (root == null) { return new ArrayList<>(); } boolean flag = true; Queue<TreeNode> queue = new LinkedList<>();//队列里面存放结点 List result = new ArrayList<>(); queue.offer(root);//根节点先入队 while (!queue.isEmpty()) { int levelNum = queue.size();//获取当前层的节点数. //遍历当前层结点 ArrayList temp = new ArrayList(); for (int i = 0; i < levelNum; i++) { //队首出队并将value加入子list TreeNode node = queue.poll(); if (node != null) { temp.add(node.val); if (node.left != null) {//如果队首的左结点不为空就把左结点入队 queue.offer(node.left); } if (node.right != null) {//如果队首的右结点不为空就把右结点入队 queue.offer(node.right); } } } if (flag) { flag = false; } else { Collections.reverse(temp); flag = true; } result.add(temp); } return result; } }
116,填充每个节点的下一个右侧节点指针
题目:给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node { int val; Node *left; Node *right; Node *next; }
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
class Solution { public Node connect(Node root) { if (root==null){ return null; } Queue<Node> queue = new LinkedList(); Queue<Node> queue2 = new LinkedList(); queue.offer(root); queue2.offer(root); root.next = null; while (!queue2.isEmpty()){ Node temp = queue2.poll(); Node left = temp.left; Node right = temp.right; if (left!=null){ queue.offer(left); queue.offer(right); queue2.offer(left); queue2.offer(right); } } int middle = 1; int length = queue.size(); for (int i = 1; i <= length; i++) { if (!queue.isEmpty()) { Node temp = queue.poll(); if (i == middle) { temp.next = null; middle = 2 * middle + 1; } else { temp.next = queue.peek(); } } else { break; } } return root; } }
117, 填充每个节点的下一个右侧节点指针II
题目:给定一个 二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。
思路:在116基础上统计出每层最后一位的索引。
class Solution { public Node connect(Node root) { if (root == null) { return null; } Queue<Node> queue = new LinkedList(); Queue<Node> queue2 = new LinkedList(); queue.offer(root); queue2.offer(root); while (!queue2.isEmpty()) { Node temp = queue2.poll(); Node left = temp.left; Node right = temp.right; if (left != null) { queue.offer(left); queue2.offer(left); } if (right != null) { queue.offer(right); queue2.offer(right); } } Queue<Node> queue3 = new LinkedList<>();//队列里面存放结点 List<Integer> result = new ArrayList<Integer>(); queue3.offer(root);//根节点先入队 //只要队列非空就一直循环; while (!queue3.isEmpty()) { int levelNum = queue3.size();//获取当前层的节点数. result.add(levelNum); //遍历当前层结点 for (int i = 0; i < levelNum; i++) { //队首出队并将value加入子list Node node = queue3.poll(); if (node.left != null) {//如果队首的左结点不为空就把左结点入队 queue3.offer(node.left); } if (node.right != null) {//如果队首的右结点不为空就把右结点入队 queue3.offer(node.right); } } } for (int i = 1; i < result.size(); i++) { result.set(i, result.get(i) + result.get(i - 1)); } int length = queue.size(); int j = 0; for (int i = 1; i <= length; i++) { if (!queue.isEmpty()) { Node temp = queue.poll(); if (i == result.get(j)) { j++; temp.next = null; } else { temp.next = queue.peek(); } } else { break; } } return root; } }
199,二叉树的右视图
题目:给定一个二叉树的 根节点
root
,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。思路:层次遍历取最后一个。
class Solution { public List<Integer> rightSideView(TreeNode root) { List result = new LinkedList(); if (root==null){ return result; } Queue<TreeNode> queue = new LinkedList<TreeNode>(); queue.offer(root); int fin = 0; while (!queue.isEmpty()) { int length = queue.size(); for (int i = 0; i < length; i++) { TreeNode temp = queue.poll(); fin = temp.val; if (temp.left != null) { queue.offer(temp.left); } if (temp.right != null) { queue.offer(temp.right); } } result.add(fin); } return result; } }
513,找树左下角的值
题目:给定一个二叉树的 根节点
root
,请找出该二叉树的 最底层 最左边 节点的值。假设二叉树中至少有一个节点。class Solution { public int findBottomLeftValue(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>();//队列里面存放结点 List<List<Integer>> result = new ArrayList<>(); queue.offer(root);//根节点先入队 while (!queue.isEmpty()) { int levelNum = queue.size();//获取当前层的节点数. //遍历当前层结点 ArrayList temp = new ArrayList(); for (int i = 0; i < levelNum; i++) { //队首出队并将value加入子list TreeNode node = queue.poll(); if (node != null) { temp.add(node.val); if (node.left != null) {//如果队首的左结点不为空就把左结点入队 queue.offer(node.left); } if (node.right != null) {//如果队首的右结点不为空就把右结点入队 queue.offer(node.right); } } } result.add(temp); } return result.get(result.size()-1).get(0); } }
515,在每个树行中找最大值
题目:给定一棵二叉树的根节点
root
,请找出该二叉树中每一层的最大值。class Solution { public List<Integer> largestValues(TreeNode root) { if (root == null) { return new ArrayList<>(); } Queue<TreeNode> queue = new LinkedList<>();//队列里面存放结点 List result = new ArrayList<>(); queue.offer(root);//根节点先入队 while (!queue.isEmpty()) { int levelNum = queue.size();//获取当前层的节点数. //遍历当前层结点 int max = Integer.MIN_VALUE; for (int i = 0; i < levelNum; i++) { //队首出队并将value加入子list TreeNode node = queue.poll(); max = Math.max(max, node.val); if (node != null) { if (node.left != null) {//如果队首的左结点不为空就把左结点入队 queue.offer(node.left); } if (node.right != null) {//如果队首的右结点不为空就把右结点入队 queue.offer(node.right); } } } result.add(max); } return result; } }
4,N 叉树
429,N 叉树的层次遍历
题目:给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔。
class Solution { public List<List<Integer>> levelOrder(Node root) { if (root == null) { return new ArrayList<>(); } Queue<Node> queue = new LinkedList<>();//队列里面存放结点 List result = new ArrayList<>(); queue.offer(root);//根节点先入队 while (!queue.isEmpty()) { int levelNum = queue.size();//获取当前层的节点数. //遍历当前层结点 ArrayList temp = new ArrayList(); for (int i = 0; i < levelNum; i++) { //队首出队并将value加入子list Node node = queue.poll(); if (node != null) { temp.add(node.val); ArrayList<Node> child = (ArrayList) node.children; for (int j = 0; j < child.size(); j++) { queue.offer(child.get(j)); } } } result.add(temp); } return result; } }
589,N 叉树的前序遍历
题目:给定一个 n 叉树的根节点 root ,返回 其节点值的 前序遍历 。n 叉树 在输入中按层序遍历进行序列化表示,每组子节点由空值 null 分隔。
/* // Definition for a Node. class Node { public int val; public List<Node> children; public Node() {} public Node(int _val) { val = _val; } public Node(int _val, List<Node> _children) { val = _val; children = _children; } }; */ class Solution { List a = new ArrayList(); public List<Integer> preorder(Node root) { if (root != null) { a.add(root.val); for (int i=0;i<root.children.size();i++){ preorder(root.children.get(i)); } } return a; } }
1376,通知所有员工所需的时间
题目:公司里有 n 名员工,每个员工的 ID 都是独一无二的,编号从 0 到 n - 1。公司的总负责人通过 headID 进行标识。
在 manager 数组中,每个员工都有一个直属负责人,其中 manager[i] 是第 i 名员工的直属负责人。对于总负责人,manager[headID] = -1。题目保证从属关系可以用树结构显示。
公司总负责人想要向公司所有员工通告一条紧急消息。他将会首先通知他的直属下属们,然后由这些下属通知他们的下属,直到所有的员工都得知这条紧急消息。
第 i 名员工需要 informTime[i] 分钟来通知它的所有直属下属(也就是说在 informTime[i] 分钟后,他的所有直属下属都可以开始传播这一消息)。返回通知所有员工这一紧急消息所需要的 分钟数 。
class Solution { int max = 0; public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { // 建图部分代码 // 题中给的条件是树,但是树也是一种特殊的图,直接建图也没毛病 List<Integer>[] graph = new ArrayList[n]; for(int i = 0; i < n; i++){ graph[i] = new ArrayList<>(); } for(int i = 0; i < n; i++){ int from = manager[i]; int to = i; if(from == -1) continue; graph[from].add(to); } // dfs部分代码 dfs(graph, informTime, headID, 0); return max; } // graph:所建的图 // informTime:传播消息时间 // cur:当前节点 // sum:消息传播到自己已经花了多久时间 public void dfs(List<Integer>[] graph, int[] informTime, int cur, int sum){ // 取决于最久的传播时间 max = Math.max(max, sum); // 遍历当前节点的每一个邻居 for(int neb : graph[cur]){ // 递归计算到邻居的邻居的时间 dfs(graph, informTime, neb, sum + informTime[cur]); } } }
5,完全二叉树
919,完全二叉树插入器
题目:完全二叉树是每一层(除最后一层外)都是完全填充(即,节点数达到最大,第
n
层有2n-1
个节点)的,并且所有的节点都尽可能地集中在左侧。设计一个用完全二叉树初始化的数据结构
CBTInserter
,它支持以下几种操作:
CBTInserter(TreeNode root)
使用根节点为root
的给定树初始化该数据结构;CBTInserter.insert(int v)
向树中插入一个新节点,节点类型为TreeNode
,值为v
。使树保持完全二叉树的状态,并返回插入的新节点的父节点的值;CBTInserter.get_root()
将返回树的根节点。思路:首先「从左往右」存储倒数第二层最右侧的节点,再「从左往右」存储最后一层的全部节点。这一步可以使用广度优先搜索来完成,因为广度优先搜索就是按照层优先进行遍历的。
class CBTInserter { Queue<TreeNode> candidate; TreeNode root; public CBTInserter(TreeNode root) { this.candidate = new ArrayDeque<TreeNode>(); this.root = root; Queue<TreeNode> queue = new ArrayDeque<TreeNode>(); queue.offer(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (node.left != null) { queue.offer(node.left); } if (node.right != null) { queue.offer(node.right); } if (!(node.left != null && node.right != null)) { candidate.offer(node); } } } public int insert(int val) { TreeNode child = new TreeNode(val); TreeNode node = candidate.peek(); int ret = node.val; if (node.left == null) { node.left = child; } else { node.right = child; candidate.poll(); } candidate.offer(child); return ret; } public TreeNode get_root() { return root; } }
6,平衡二叉树
110,平衡二叉树
题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
class Solution { boolean flag = true; public boolean isBalanced(TreeNode root) { height(root); return flag; } public int height(TreeNode p) { //返回以p结点为根的子树高度,后根次序遍历 if (p == null) return 0; int lh = height(p.left); //返回左子树的高度 int rh = height(p.right); //返回右子树的高度 if (Math.abs(lh - rh) >= 2) { flag = false; } return (lh >= rh) ? lh + 1 : rh + 1; //当前子树高度为较高子树的高度加1 } }