diff --git a/src/main/java/tree/BuildTree.java b/src/main/java/tree/BuildTree.java index 07e7a3c..c077c51 100644 --- a/src/main/java/tree/BuildTree.java +++ b/src/main/java/tree/BuildTree.java @@ -11,7 +11,28 @@ import java.util.Map; */ //思路会 代码不会写 +//二刷会做 public class BuildTree { + int findpos(int a,int[] b,int left,int right){ + for (int i = left; i <= right; i++) { + if(b[i]==a)return i; + } + return -1; + } + TreeNode helper(int preleft,int preright,int inleft,int inright,int[] preorder,int[] inorder){ + if(preleft>preright|| inleft>inright)return null; + int pos=findpos(preorder[preleft],inorder,inleft,inright); + int leftcnt=pos-inleft; + TreeNode root=new TreeNode(inorder[pos]); + root.left=helper(preleft+1,preleft+1+leftcnt-1,inleft,pos-1,preorder,inorder); + root.right=helper(preleft+1+leftcnt,preright,pos+1,inright,preorder,inorder); + return root; + } + public TreeNode buildTree1(int[] preorder, int[] inorder) { + int cnt=preorder.length; + return helper(0,cnt-1,0,cnt-1,preorder,inorder); + } + private Map indexMap; public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left) { diff --git a/src/main/java/tree/Flatten.java b/src/main/java/tree/Flatten.java index 96747d9..d08af07 100644 --- a/src/main/java/tree/Flatten.java +++ b/src/main/java/tree/Flatten.java @@ -10,10 +10,12 @@ import java.util.List; * 描述:给你二叉树的根结点 root ,请你将它展开为一个单链表: * 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。 * 展开后的单链表应该与二叉树 先序遍历 顺序相同。 - + 进阶:你可以使用原地算法(O(1) 额外空间)展开这棵树吗? * 链接:https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/ */ +//二刷会普通方法 不会空间O1的 + //todo:学习一下思路。 public class Flatten { public void inOrderTraversal(TreeNode root,Listlist){ if(root!=null) { @@ -23,10 +25,9 @@ public class Flatten { } } //O(N)空间复杂度 递归前序 - public void flatten(TreeNode root) { + public void flatten2(TreeNode root) { Listlist=new ArrayList<>(); - TreeNode head=new TreeNode(0); - TreeNode tp=head; + TreeNode tp= new TreeNode(0); inOrderTraversal(root,list); for (TreeNode treeNode : list) { tp.left=null; @@ -34,7 +35,7 @@ public class Flatten { tp=tp.right; } } - //迭代前序遍历也要回 + //迭代前序遍历也要会! public void flatten1(TreeNode root) { if (root == null) { return; @@ -58,21 +59,40 @@ public class Flatten { prev = curr; } } - //O(1)空间 - public void flatten2(TreeNode root) { - TreeNode curr = root; + //O(1)空间 很妙 + /** + * 原地展开二叉树为单链表(先序顺序) + * 思路:Morris 遍历的变形 —— 每到一个有左子树的节点,就把 + * ① 左子树插到右边 + * ② 左子树最右节点 (predecessor) 的 right 指针接到原来的右子树 + * ③ 左指针置空 + * 然后继续沿着 curr = curr.right 前进 + * 时间复杂度:O(n) — 每条边最多访问两次 + * 空间复杂度:O(1) — 仅用常数指针 + * https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/solutions/356853/er-cha-shu-zhan-kai-wei-lian-biao-by-leetcode-solu/?envType=study-plan-v2&envId=top-100-liked + */ + public void flatten(TreeNode root) { + TreeNode curr = root; // 当前遍历到的节点 while (curr != null) { - if (curr.left != null) { - TreeNode next = curr.left; + if (curr.left != null) { // 只处理存在左子树的节点 + TreeNode next = curr.left; // 1) 记录左子树的根,稍后会挪到右边 + + // 2) 找到左子树中的最右节点(先序遍历的“前驱”) TreeNode predecessor = next; while (predecessor.right != null) { predecessor = predecessor.right; } + + // 3) 把当前节点原本的右子树接到 predecessor 的右侧 predecessor.right = curr.right; + + // 4) 把左子树搬到右边,左指针清空 curr.left = null; curr.right = next; } + // 5) 向“链表”右侧移动,继续处理下一个节点 curr = curr.right; } } + } diff --git a/src/main/java/tree/PathSum.java b/src/main/java/tree/PathSum.java index 1d19202..578cbec 100644 --- a/src/main/java/tree/PathSum.java +++ b/src/main/java/tree/PathSum.java @@ -12,7 +12,17 @@ import java.util.Map; */ //前缀和想到了,代码不会写 +//二刷不会 public class PathSum { + /** + * prefix 这张哈希表只应该保存“当前递归栈(从根结点到正在访问的那个结点)”这条路径上出现过的前缀和。!!!! + * 当递归函数从一个结点返回到它的父结点时,这个结点及其子树已经遍历完毕,它对应的前缀和在后面的兄弟子树中就再也不会出现在 + * “从父结点往下的路径”里了;如果不把它从 prefix 里去掉,那么后续兄弟子树在查询 prefix.get(curr-target) + * 时就可能误用这条“已经不存在的”前缀和,从而把 跨越两条不同分支 的路径算进去——而题目只允许一直“向下”的路径。 + * @param root + * @param targetSum + * @return + */ // 主函数:用于计算路径和等于 targetSum 的路径个数 public int pathSum(TreeNode root, int targetSum) { // 使用一个哈希表来记录前缀和出现的次数 @@ -25,12 +35,6 @@ public class PathSum { return dfs(root, prefix, 0, targetSum); } - // dfs函数:递归遍历树,并统计满足路径和为 targetSum 的路径个数 - // 参数说明: - // root:当前节点 - // prefix:记录前缀和及其出现次数的哈希表 - // curr:当前节点的累计前缀和(从根节点到当前节点的和) - // targetSum:目标路径和 public int dfs(TreeNode root, Map prefix, long curr, int targetSum) { // 终止条件:当前节点为空,返回0条路径 if (root == null) { @@ -55,6 +59,7 @@ public class PathSum { // 回溯操作:在返回父节点之前,去掉当前节点对前缀和的贡献 // 防止当前路径的前缀和影响到其他分支的计算 + //不会减到负数,因为进来的时候一定+1了。 prefix.put(curr, prefix.getOrDefault(curr, 0) - 1); return ret; diff --git a/src/main/java/tree/RightSideView.java b/src/main/java/tree/RightSideView.java index 45da171..e312ffc 100644 --- a/src/main/java/tree/RightSideView.java +++ b/src/main/java/tree/RightSideView.java @@ -12,8 +12,9 @@ import java.util.Queue; * 链接:https://leetcode.cn/problems/binary-tree-right-side-view/description/ */ +//二刷会做 public class RightSideView { - public List rightSideView(TreeNode root) { + public List rightSideView1(TreeNode root) { Queuequeue=new ArrayDeque<>(); Listres=new ArrayList<>(); if(root==null) @@ -33,4 +34,17 @@ public class RightSideView { } return res; } + + public List rightSideView(TreeNode root) { + List res = new ArrayList<>(); + dfs(root, 0, res); + return res; + } + private void dfs(TreeNode node, int depth, List res) { + if (node == null) return; + if (depth == res.size()) res.add(node.val); // 第一次到此深度 ⇒ 最右 只添加最右侧的节点。 + dfs(node.right, depth + 1, res); // 先右 + dfs(node.left, depth + 1, res); + } + }