From 2e5f398de6f49c691c23ba7f7bd579d8d5f011fd Mon Sep 17 00:00:00 2001 From: dingjiawen <745518019@qq.com> Date: Tue, 20 Sep 2022 13:42:55 +0800 Subject: [PATCH] =?UTF-8?q?leecode=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/markilue/leecode/tree/LevelOrder.java | 171 +++++++++++ .../leecode/tree/PostOrderTraversal.java | 282 ++++++++++++++++++ .../leecode/tree/StackUnitTraversal.java | 170 +++++++++++ 3 files changed, 623 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/LevelOrder.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/PostOrderTraversal.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/StackUnitTraversal.java diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/LevelOrder.java b/Leecode/src/main/java/com/markilue/leecode/tree/LevelOrder.java new file mode 100644 index 0000000..60ac632 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/LevelOrder.java @@ -0,0 +1,171 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; +import sun.security.krb5.internal.CredentialsUtil; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-20 11:44 + * @Description: TODO 力扣102题:二叉树的层序遍历: + * 给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。 + * @Version: 1.0 + */ +public class LevelOrder { + + @Test + public void test() { + + TreeNode root = new TreeNode(3); + TreeNode TreeNode2 = new TreeNode(9); + TreeNode TreeNode3 = new TreeNode(20); +// TreeNode TreeNode4 = new TreeNode(4); +// TreeNode TreeNode5 = new TreeNode(5); + TreeNode TreeNode6 = new TreeNode(15); + TreeNode TreeNode7 = new TreeNode(7); + + root.setRight(TreeNode3); + root.setLeft(TreeNode2); +// TreeNode2.setLeft(TreeNode4); +// TreeNode2.setRight(TreeNode5); + TreeNode3.setRight(TreeNode7); + TreeNode3.setLeft(TreeNode6); + + System.out.println(levelOrder(root)); + + } + + @Test + public void test1() { + + TreeNode root = new TreeNode(1); + TreeNode TreeNode2 = new TreeNode(9); + TreeNode TreeNode3 = new TreeNode(20); +// TreeNode TreeNode4 = new TreeNode(4); +// TreeNode TreeNode5 = new TreeNode(5); + TreeNode TreeNode6 = new TreeNode(15); + TreeNode TreeNode7 = new TreeNode(7); + +// root.setRight(TreeNode3); +// root.setLeft(TreeNode2); +//// TreeNode2.setLeft(TreeNode4); +//// TreeNode2.setRight(TreeNode5); +// TreeNode3.setRight(TreeNode7); +// TreeNode3.setLeft(TreeNode6); + + System.out.println(levelOrder(root)); + + } + + + /** + * 这里尝试使用之前统一的空指针标记法(将其换成队列):难点在于如何找到下一层的第一个节点 + * 速度超过58.94%,内存超过37.04% + * + * @param root + * @return + */ + public List> levelOrder(TreeNode root) { + + List> result = new ArrayList<>(); + + //使用队列完成 + Deque deque = new LinkedList(); + + if (root != null) { + deque.addLast(root); + } + + List mid = new ArrayList<>(); + TreeNode pre = root; + //是否需要找下一层 + boolean flag = true; + + while (deque.size() > 0) { + TreeNode node = deque.peek(); + + //遍历到了下一层 + if (pre == node && !flag) { + result.add(mid); + mid = new ArrayList(); + //需要找到一个不为0的值了 + flag = true; + } + + + if (node != null) { + //访问过还未处理 + deque.addFirst(null); + + if (node.left != null) { + deque.addLast(node.left); + //记录下层的第一个指针 + if (flag) { + pre = node.left; + flag = false; + } + } + + if (node.right != null) { + deque.addLast(node.right); + //记录下层的第一个指针 + if (flag) { + pre = node.right; + flag = false; + } + } + } else { + //处理访问数据 + deque.poll();//移除空指针 + mid.add(deque.poll().val); + } + } + + if (mid.size() != 0) { + result.add(mid); + } + + return result; + + } + + /** + * 官方队列法:使用了固定的size从而避免了找下一次的第一个值 + * 速度超过100%,内存超过81% + * + * @param root + * @return + */ + public List> levelOrder1(TreeNode root) { + + List> result = new ArrayList<>(); + + //使用队列完成 + Deque deque = new LinkedList(); + + if (root != null) { + deque.addLast(root); + } + while (deque.size() > 0) { + List mid = new ArrayList<>(); + + int size = deque.size(); + + //注意这里必须使用size,而不是deque.size,因为deque的size一直在发生变化 + for (int i = 0; i < size; i++) { + TreeNode poll = deque.poll(); + mid.add(poll.val); + if (poll.left != null) deque.offer(poll.left); + if (poll.right != null) deque.offer(poll.right); + } + result.add(mid); + } + return result; + + } + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/PostOrderTraversal.java b/Leecode/src/main/java/com/markilue/leecode/tree/PostOrderTraversal.java new file mode 100644 index 0000000..41bef86 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/PostOrderTraversal.java @@ -0,0 +1,282 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.*; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-19 09:45 + * @Description: TODO 力扣144题 二叉树的前序遍历: + * 给你二叉树的根节点 root ,返回它节点值的 前序 遍历。 + * @Version: 1.0 + */ +public class PostOrderTraversal { + + + @Test + public void test() { + + TreeNode root = new TreeNode(1); +// TreeNode TreeNode2 = new TreeNode(2); +// TreeNode TreeNode3 = new TreeNode(3); +// TreeNode TreeNode4 = new TreeNode(1); +// TreeNode TreeNode5 = new TreeNode(3); +// TreeNode TreeNode6 = new TreeNode(5); + +// root.setRight(TreeNode2); +// TreeNode2.setLeft(TreeNode3); +// TreeNode2.setLeft(TreeNode4); +// TreeNode2.setRight(TreeNode5); +// TreeNode3.setLeft(TreeNode6); + System.out.println(postorderTraversal1(root)); + } + + @Test + public void test1() { + + TreeNode root = new TreeNode(1); + TreeNode TreeNode2 = new TreeNode(2); + TreeNode TreeNode3 = new TreeNode(3); +// TreeNode TreeNode4 = new TreeNode(1); +// TreeNode TreeNode5 = new TreeNode(3); +// TreeNode TreeNode6 = new TreeNode(5); + + root.setRight(TreeNode2); + TreeNode2.setLeft(TreeNode3); +// TreeNode2.setLeft(TreeNode4); +// TreeNode2.setRight(TreeNode5); +// TreeNode3.setLeft(TreeNode6); + System.out.println(postorderTraversal1(root)); + } + + @Test + public void test2() { + + TreeNode root = new TreeNode(1); + TreeNode TreeNode2 = new TreeNode(2); + TreeNode TreeNode3 = new TreeNode(3); +// TreeNode TreeNode4 = new TreeNode(1); +// TreeNode TreeNode5 = new TreeNode(3); +// TreeNode TreeNode6 = new TreeNode(5); + + root.setRight(TreeNode2); + TreeNode2.setLeft(TreeNode3); +// TreeNode2.setLeft(TreeNode4); +// TreeNode2.setRight(TreeNode5); +// TreeNode3.setLeft(TreeNode6); + System.out.println(postorderTraversal1(null)); + } + + @Test + public void test3() { + + TreeNode root = new TreeNode(1); + TreeNode TreeNode2 = new TreeNode(2); + TreeNode TreeNode3 = new TreeNode(3); + TreeNode TreeNode4 = new TreeNode(4); + TreeNode TreeNode5 = new TreeNode(5); + TreeNode TreeNode6 = new TreeNode(6); + TreeNode TreeNode7 = new TreeNode(7); + + root.setRight(TreeNode2); + root.setLeft(TreeNode3); + TreeNode2.setLeft(TreeNode4); + TreeNode2.setRight(TreeNode5); + TreeNode3.setRight(TreeNode6); + TreeNode3.setLeft(TreeNode7); + System.out.println(postorderTraversal4(root)); + } + + + public List postorderTraversal(TreeNode root) { + List list = new ArrayList<>(); + traversal(root, list); + return list; + } + + /** + * 自己的递归法 + * 速度击败100% ,内存击败44.85% + */ + public void traversal(TreeNode root, List list) { + + if (root == null) { + return; + } + traversal(root.left, list); + traversal(root.right, list); + list.add(root.val); + + } + + /** + * 自己的非递归法:使用一个栈记录上次还需要进一步遍历的节点,等现在的遍历完了,就可以出栈到这里,让上一次的继续去遍历 + * 速度击败100%,内存击败90.34% + * + * @param root + * @return + */ + public List postorderTraversal1(TreeNode root) { + + List result = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode cur = root; + + while (cur != null || stack.size() > 0) { + + //判断是什么条件进来的 + if (cur != null) { + stack.push(cur); + cur = cur.left; + continue; + }else { + TreeNode node = stack.peek(); + //看看他右边还有没有 + if (node.right != null) { + //右边还有,让右边的在继续遍历 + cur = node.right; + //置空防止再次还来遍历 + node.right=null; + } else { + //左右都没有了,直接加入结果 + result.add(node.val); + stack.pop(); + } + } + + + } + return result; + } + + /** + * 代码随想录的非递归法:与前序遍历的非递归法类似,前序是中-左-右;后序是左-右-中,因此可以把前序的顺序稍微变一下变成中-右-左,最后的结果再反转一下即可 + * 速度击败100%,内存击败90.34% + * + * @param root + * @return + */ + public List postorderTraversal3(TreeNode root) { + + ArrayList result = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + + while (stack.size()>0){ + TreeNode node = stack.pop(); + result.add(node.val); + if(node.left!=null){ + stack.push(node.left); + } + if(node.right!=null){ + stack.push(node.right); + } + } + + //反转list + ArrayList realList = new ArrayList<>(); + + for (int i = result.size()-1; i >=0 ; i--) { + realList.add(result.get(i)); + } + + return realList; + } + + + /** + * 官方Morris 遍历:将空间复杂度降到O(1) + * TODO 这里实际上就是将前序遍历的顺序稍微变一下中-右-左 最后的结果再反转 + * + * @param root + * @return + */ + public List postorderTraversal2(TreeNode root) { + List res = new ArrayList(); + if (root == null) { + return res; + } + + TreeNode p1 = root, p2 = null; + + while (p1 != null) { + p2 = p1.left; + if (p2 != null) { + while (p2.right != null && p2.right != p1) { + p2 = p2.right; + } + if (p2.right == null) { + p2.right = p1; + p1 = p1.left; + continue; + } else { + p2.right = null; + addPath(res, p1.left); + } + } + p1 = p1.right; + } + addPath(res, root); + return res; + + } + + + public void addPath(List res, TreeNode node) { + int count = 0; + while (node != null) { + ++count; + res.add(node.val); + node = node.right; + } + //反转左右 + int left = res.size() - count, right = res.size() - 1; + while (left < right) { + int temp = res.get(left); + res.set(left, res.get(right)); + res.set(right, temp); + left++; + right--; + } + } + + + + /** + * 官方不改变指针的非递归法 + * @param root + * @return + */ + public List postorderTraversal4(TreeNode root) { + List res = new ArrayList(); + if (root == null) { + return res; + } + + Deque stack = new LinkedList(); + TreeNode prev = null; + while (root != null || !stack.isEmpty()) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + //第二个条件表示右边的节点是上一次pop出的节点,因为只有pop出的值才会在prev赋值 + if (root.right == null || root.right == prev) { + res.add(root.val); + prev = root; + root = null; + } else { + stack.push(root); + root = root.right; + } + } + return res; + } + + + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/StackUnitTraversal.java b/Leecode/src/main/java/com/markilue/leecode/tree/StackUnitTraversal.java new file mode 100644 index 0000000..72fe515 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/StackUnitTraversal.java @@ -0,0 +1,170 @@ +package com.markilue.leecode.tree; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +/** + * @BelongsProject: Leecode + * @BelongsPackage: com.markilue.leecode.tree + * @Author: dingjiawen + * @CreateTime: 2022-09-20 11:13 + * @Description: + * TODO stack栈法(一种迭代法)来统一解决前中后序遍历:使用空指针标记法来统一前中后遍历的迭代法 + * @Version: 1.0 + */ +public class StackUnitTraversal { + + @Test + public void test(){ + + TreeNode root = new TreeNode(5); + TreeNode TreeNode4 = new TreeNode(4); + TreeNode TreeNode6 = new TreeNode(6); + TreeNode TreeNode1 = new TreeNode(1); + TreeNode TreeNode2 = new TreeNode(2); + root.setRight(TreeNode6); + root.setLeft(TreeNode4); + TreeNode4.setLeft(TreeNode1); + TreeNode4.setRight(TreeNode2); + + + System.out.println(preorderTraversal(root)); + } + + /* + *空指针标记法的中序遍历 + * @param root + * @return + */ + public List inorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + + //使用栈记录需要遍历的root + Stack stack = new Stack<>(); + if (root != null) { + stack.push(root); + } + + //栈中数据:[6,5,null,2,4,null,1,null] + while (stack.size() > 0) { + TreeNode node = stack.peek(); + + if(node!=null){ + //将该节点弹出,避免重复操作,下面再将右、中、左节点添加到栈中 + stack.pop(); + //添加右节点(空节点不入栈) + if(node.right!=null){ + stack.push(node.right); + } + + //添加中节点 + stack.push(node); + //中指针访问过,但没有处理,添加空指针节点作为标记 + stack.push(null); + //添加左节点(空节点不入栈) + if(node.left!=null){ + stack.push(node.left); + } + }else { + //只有遇到空节点的时候,才将下一个节点放入结果集 + stack.pop();//将空指针弹出 + node = stack.pop(); //重新取出栈中元素 + result.add(node.val); + } + } + return result; + } + + /* + *空指针标记法的前序遍历 + * @param root + * @return + */ + public List preorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + + //使用栈记录需要遍历的root + Stack stack = new Stack<>(); + if (root != null) { + stack.push(root); + } + + //栈中数据:[6,4,5,null] + //栈中数据:[6,2,1,4,null] + while (stack.size() > 0) { + TreeNode node = stack.peek(); + + if(node!=null){ + //将该节点弹出,避免重复操作,下面再将右、中、左节点添加到栈中 + stack.pop(); + //添加右节点(空节点不入栈) + if(node.right!=null){ + stack.push(node.right); + } + //添加左节点(空节点不入栈) + if(node.left!=null){ + stack.push(node.left); + } + //添加中节点 + stack.push(node); + //中指针访问过,但没有处理,添加空指针节点作为标记 + stack.push(null); + }else { + //只有遇到空节点的时候,才将下一个节点放入结果集 + stack.pop();//将空指针弹出 + node = stack.pop(); //重新取出栈中元素 + result.add(node.val); + } + } + return result; + } + + /* + *空指针标记法的后序遍历 + * @param root + * @return + */ + public List postorderTraversal(TreeNode root) { + List result = new ArrayList<>(); + + //使用栈记录需要遍历的root + Stack stack = new Stack<>(); + if (root != null) { + stack.push(root); + } + + //栈中数据:[5,null,6,4,null,2,1,null] + while (stack.size() > 0) { + TreeNode node = stack.peek(); + + if(node!=null){ + //将该节点弹出,避免重复操作,下面再将右、中、左节点添加到栈中 + stack.pop(); + //添加中节点 + stack.push(node); + //中指针访问过,但没有处理,添加空指针节点作为标记 + stack.push(null); + //添加右节点(空节点不入栈) + if(node.right!=null){ + stack.push(node.right); + } + //添加左节点(空节点不入栈) + if(node.left!=null){ + stack.push(node.left); + } + + }else { + //只有遇到空节点的时候,才将下一个节点放入结果集 + stack.pop();//将空指针弹出 + node = stack.pop(); //重新取出栈中元素 + result.add(node.val); + } + } + return result; + } + + +}