二叉树问题思考角度就是站到ROOT节点,思考我要让左右节点返回给我什么信息 。然后思考我有了这个信息后要干什么
数组情况:和为k的子数组
思路:
暴力,以每个节点为起点出发,然后直接遍历到终点。两层for循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution { int res = 0 ; long targetSum = 0 ; public int pathSum (TreeNode root, int _targetSum) { targetSum = (long )_targetSum; t1(root); return res; } public void t1 (TreeNode root) { if (root == null ) return ; traverse(root, 0 ); t1(root.left); t1(root.right); } public void traverse (TreeNode root,long curSum) { if (root == null ){ return ; } curSum+=root.val; if (curSum == targetSum) res++; traverse(root.left, curSum); traverse(root.right, curSum); } }
前缀和+ 两数之和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class Solution { int res = 0 ; long targetSum = 0 ; Map<Long, Integer> preSumCounter = new HashMap <>(); public int pathSum (TreeNode root, int _targetSum) { targetSum = (long ) _targetSum; preSumCounter.put(0L , 1 ); traverse(root, 0 ); return res; } public void traverse (TreeNode root, long curSum) { if (root == null ) { return ; } curSum += root.val; res += preSumCounter.getOrDefault(curSum + (-targetSum), 0 ); preSumCounter.put(curSum, preSumCounter.getOrDefault(curSum, 0 ) + 1 ); traverse(root.left, curSum); traverse(root.right, curSum); preSumCounter.put(curSum, preSumCounter.get(curSum) - 1 ); } }
思路
站在ROOT节点思考左右节点要返回给我什么信息?我希望左子节点返回给我左子树的最大路径和。右节点类似
思考有了这个信息要干什么。无非就是求出最大路径和 四种情况 leftSum + cur.val
rightSum + cur.val
cur.val
cur.val + leftSum + rightSum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { int res = Integer.MIN_VALUE; public int maxPathSum (TreeNode root) { dfs(root); return res; } public int dfs (TreeNode cur) { if (cur == null ) return 0 ; int leftSum = dfs(cur.left); int rightSum = dfs(cur.right); int ret = Math.max(cur.val, Math.max(leftSum,rightSum) + cur.val); res = Math.max(res, Math.max(ret, cur.val + leftSum + rightSum)); return ret; } }
思路:
法一:DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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 ) { return root; } if (root == p || root == q) return root; TreeNode left = dfs(root.left, p, q); TreeNode right = dfs(root.right, p, q); if (left == null ) return right; if (right == null ) return left; if (left == right) return root; return root; } }
法二:Map存子节点->父节点,然后转换成相交链表问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 class Solution { Map<TreeNode, TreeNode> map = new HashMap <>(); public TreeNode lowestCommonAncestor (TreeNode root, TreeNode p, TreeNode q) { dfs(root, null ); Set<TreeNode> qZX = new HashSet <>(); while (q != null ) { qZX.add(q); q = map.get(q); } while (p != null ) { if (qZX.contains(p)) { return p; } p = map.get(p); } return null ; } public void dfs (TreeNode cur, TreeNode par) { if (cur == null ) { return ; } map.put(cur, par); dfs(cur.left, cur); dfs(cur.right, cur); } }
时间复杂度都是O( N )
思路:
1.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public boolean isSymmetric (TreeNode root) { if (root==null ) { return true ; } return dfs(root.left,root.right); } boolean dfs (TreeNode left, TreeNode right) { if (left==null && right==null ) { return true ; } if (left==null || right==null ) { return false ; } if (left.val!=right.val) { return false ; } return dfs(left.left,right.right) && dfs(left.right,right.left); } } 作者:王尼玛 链接:https: 来源:力扣(LeetCode) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思路:
翻转左右子节点就好,然后就交给递归函数了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public TreeNode invertTree (TreeNode root) { return dfs(root); } public TreeNode dfs (TreeNode root) { if (root == null ) return null ; TreeNode left = dfs(root.left); TreeNode right = dfs(root.right); root.left = right; root.right = left; return root; } }
思路(DFS):
if(root.left == null && root.right == null) 结果更新时
思路(BFS):
层序遍历,每过一层 depth++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { int res = Integer.MIN_VALUE; public int maxDepth (TreeNode root) { if (root != null ){ dfs(root,1 ); return res; } return 0 ; } public void dfs (TreeNode root,int cnt) { if (root == null ) { return ; } if (root.left == null && root.right == null ){ res = Math.max(res,cnt); return ; } dfs(root.left,cnt + 1 ); dfs(root.right,cnt + 1 ); } }
BFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class Solution { public int maxDepth (TreeNode root) { ArrayDeque<TreeNode> q = new ArrayDeque (); if (root != null ) q.add(root); int depth = 0 ; while (!q.isEmpty()) { int size = q.size(); while (size > 0 ) { TreeNode cur = q.remove(); if (cur.left != null ) { q.add(cur.left); } if (cur.right != null ) { q.add(cur.right); } size--; } depth++; } return depth; } }
思路
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { int res = 0 ; public int diameterOfBinaryTree (TreeNode root) { dfs(root); return res; } public int dfs (TreeNode root) { if (root == null ) { return 0 ; } int lM = dfs(root.left); int rM = dfs(root.right); res = Math.max(lM + rM, res); return Math.max(lM, rM) + 1 ; } }
思路:
二叉搜索树 中序遍历 为 单调递增数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class Solution { List<Integer> list = new ArrayList (); public boolean isValidBST (TreeNode root) { f1(root); for (int i = 1 ; i < list.size(); i++) { if (list.get(i) <= list.get(i - 1 )) { return false ; } } return true ; } public void f1 (TreeNode root) { if (root == null ) { return ; } f1(root.left); list.add(root.val); f1(root.right); } }
思路:
序列化容易,就是前序遍历,转成字符串,或者层序遍历
反序列化比较难,一种是bfs,一种是dfs
就是你序列化以BFS,你反序列化就是要BFS。DFS同样
题目其实就是给你root,序列化成字符串,然后再根据字符串反序列为树
DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public class Codec { StringBuilder serRes = new StringBuilder (); public String serialize (TreeNode root) { f1(root); return serRes.toString(); } public void f1 (TreeNode root) { if (root == null ){ serRes.append("null," ); return ; } else serRes.append(root.val + "," ); f1(root.left); f1(root.right); } public TreeNode deserialize (String data) { String []datas = data.split("," ); return builder(datas); } int index = 0 ; public TreeNode builder (String[] data) { String curV = data[index]; index++; TreeNode curNode = null ; if (curV.equals("null" )){ return curNode; } else { int curVN = Integer.parseInt(curV); curNode = new TreeNode (curVN); curNode.left = builder(data); curNode.right = builder(data); } return curNode; } }
BFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 public class Codec { StringBuilder serRes = new StringBuilder (); public String serialize (TreeNode root) { Queue<TreeNode> q = new LinkedList <>(); q.add(root); while (!q.isEmpty()) { TreeNode cur = q.poll(); if (cur != null ) { serRes.append(cur.val).append("," ); q.add(cur.left); q.add(cur.right); } else { serRes.append("null," ); } } return serRes.toString(); } public TreeNode deserialize (String data) { String[] datas = data.split("," ); return builder(datas); } public TreeNode builder (String[] data) { if (data.length == 0 || data[0 ].equals("null" )) { return null ; } TreeNode res = new TreeNode (Integer.parseInt(data[0 ])); Queue<TreeNode> q = new LinkedList <>(); q.add(res); int index = 1 ; while (!q.isEmpty()) { TreeNode curNode = q.poll(); if (!data[index].equals("null" )) { curNode.left = new TreeNode (Integer.parseInt(data[index])); q.add(curNode.left); } index++; if (!data[index].equals("null" )) { curNode.right = new TreeNode (Integer.parseInt(data[index])); q.add(curNode.right); } index++; } return res; } }
思路:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { TreeNode pre=null ; public void flatten (TreeNode root) { if (root==null ) return ; if (pre!=null ){ pre.right=root; } TreeNode tmp=root.right; pre=root; flatten(root.left); flatten(tmp); root.left=null ; } }
背诵题
思路:
找规律,先找出 比如 1个节点(n=1),2,….的时候,这时候有多少种二叉搜索树
然后再找 n和n-1的关系,即递归公式
明确它的一个dp数组含义了,即 第i个节点 有多少种二叉搜索树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public int numTrees (int n) { if ( n <= 2 ) return n; int []dp = new int [n+1 ]; dp[0 ] = 1 ; dp[1 ] = 1 ; for (int i = 2 ;i <= n;i++){ for (int k = 0 ;k <= i - 1 ;k++){ dp[i] += dp[k] * dp[i - k - 1 ]; } } return dp[n]; } }
1️⃣ 为什么是 乘法 ? 我们考虑:
如果整数 1 ~ i 中选 k 作为根节点, 左子树可以用 1 ~ k-1 构建,右子树可以用 k+1 ~ i 构建。
假设:
左子树可以构建 L = dp[k] 种不同的 BST
右子树可以构建 R = dp[i - k - 1] 种不同的 BST
总的 BST 数量 = 左子树方案数 × 右子树方案数
为什么是乘法? 因为每一种左子树 都可以和每一种右子树 组合成一个新的 BST。 这是经典的排列组合思想:左 * 右。
比如小明有4个苹果,小红有5个香蕉。他们组合是多少,那肯定是4*5
举个小例子:
k=2,左子树用 1 构建 → 1 种方案
右子树用 3,4 构建 → 2 种方案
总的 BST 数 = 1 * 2 = 2 种。
2️⃣ dp[k] 和 dp[i - k - 1] 分别是左子树和右子树
dp[k] → 左子树 的方案数 因为左子树包含 k 左边的 k 个节点(1 ~ k),所以 dp[k]
dp[i - k - 1] → 右子树 的方案数 因为右子树包含剩下的 i - k - 1 个节点(k+1 ~ i),所以 dp[i - k - 1]
⚠️ 注意下标关系:
i = 当前节点总数
k = 左子树节点数(或根节点下标?在你的循环中,k是左子树节点数)
右子树节点数 = i - k - 1
思路:
构造26叉树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 class Trie { private static class Node { Node[] son = new Node [26 ]; boolean end = false ; } private final Node root = new Node (); public Trie () { } public void insert (String word) { Node cur = root; for (char c : word.toCharArray()) { int index = c - 'a' ; if (cur.son[index] == null ) { cur.son[index] = new Node (); } cur = cur.son[index]; } cur.end = true ; } public boolean search (String word) { return find(word) == 2 ; } public boolean startsWith (String prefix) { return find(prefix) != 0 ; } public int find (String word) { Node cur = root; for (char c : word.toCharArray()) { int index = c - 'a' ; if (cur.son[index] == null ) return 0 ; cur = cur.son[index]; } return cur.end ? 2 : 1 ; } }
思路:
利用二叉搜索树的特性,中序遍历为 有序数组
然后算出中序遍历后的数组的每个的元素的后缀和
然后再次中序遍历更改 树节点的val
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 class Solution { List<Integer> l = new ArrayList (); int index = 0 ; public TreeNode convertBST (TreeNode root) { traverse(root); int sum = 0 ; for (int i = 0 ;i < l.size(); i++){ sum += l.get(i); } int preSum = 0 ; for (int i = 0 ; i < l.size(); i++){ int temp = l.get(i); l.set(i, sum - preSum); preSum += temp; } generate(root); return root; } public void traverse (TreeNode root) { if (root == null ) return ; traverse(root.left); l.add(root.val); traverse(root.right); } public void generate (TreeNode root) { if (root == null ) return ; generate(root.left); root.val = l.get(index); index++; generate(root.right); } }
思路
前序遍历,但这样是错误,在merge时,root1 = root2,改变的不是引用!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution { public TreeNode mergeTrees (TreeNode root1, TreeNode root2) { dfs(root1, root2); return root1; } public void dfs (TreeNode root1,TreeNode root2) { if (root1 == null && root2 == null ){ return ; } else if (root1 == null && root2 != null ){ root1 = root2; return ; } else if (root1 != null && root2 == null ){ return ; } else if (root1 != null && root2 != null ){ root1.val += root2.val; } dfs(root1.left,root2.left); dfs(root1.right,root2.right); } }
正确的做法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Solution { public TreeNode mergeTrees (TreeNode root1, TreeNode root2) { return build(root1, root2); } public TreeNode build (TreeNode root1, TreeNode root2) { if (root1 == null && root2 == null ){ return null ; } if (root1 != null && root2 == null ){ return root1; } if (root1 == null && root2 != null ){ return root2; } TreeNode res = new TreeNode (); if (root1 != null && root2 != null ) res.val = root1.val + root2.val; res.left = build(root1.left, root2.left); res.right = build(root1.right, root2.right); return res; } }