From 28e1a9c674e9f30af4c750f41e827fab8125215e Mon Sep 17 00:00:00 2001 From: markilue <745518019@qq.com> Date: Fri, 6 Jan 2023 15:17:43 +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 --- .../markilue/leecode/tree/second/NNode.java | 27 ++++ .../markilue/leecode/tree/second/Node.java | 30 ++++ .../tree/second/T042_RightSideView.java | 77 ++++++++++ .../tree/second/T043_averageOfLevels.java | 130 +++++++++++++++++ .../leecode/tree/second/T044_LevelOrder.java | 79 +++++++++++ .../tree/second/T045_LargestValues.java | 83 +++++++++++ .../leecode/tree/second/T046_Connect.java | 85 +++++++++++ .../leecode/tree/second/T047_Connect.java | 132 ++++++++++++++++++ .../leecode/tree/second/T048_MaxDepth.java | 72 ++++++++++ .../leecode/tree/second/T049_MinDepth.java | 93 ++++++++++++ 10 files changed, 808 insertions(+) create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/NNode.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/Node.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T042_RightSideView.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T043_averageOfLevels.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T044_LevelOrder.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T045_LargestValues.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T046_Connect.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T047_Connect.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T048_MaxDepth.java create mode 100644 Leecode/src/main/java/com/markilue/leecode/tree/second/T049_MinDepth.java diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/NNode.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/NNode.java new file mode 100644 index 0000000..f820b4a --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/NNode.java @@ -0,0 +1,27 @@ +package com.markilue.leecode.tree.second; + +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 10:55 + *@Description: TODO N叉树节点 + *@Version: 1.0 + */ +public class NNode { + public int val; + public List children; + + public NNode() {} + + public NNode(int _val) { + val = _val; + } + + public NNode(int _val, List _children) { + val = _val; + children = _children; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/Node.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/Node.java new file mode 100644 index 0000000..295c206 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/Node.java @@ -0,0 +1,30 @@ +package com.markilue.leecode.tree.second; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 11:36 + *@Description: TODO 完美二叉树 + *@Version: 1.0 + */ +public class Node { + + public int val; + public Node left; + public Node right; + public Node next; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, Node _left, Node _right, Node _next) { + val = _val; + left = _left; + right = _right; + next = _next; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T042_RightSideView.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T042_RightSideView.java new file mode 100644 index 0000000..d602304 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T042_RightSideView.java @@ -0,0 +1,77 @@ +package com.markilue.leecode.tree.second; + +import com.markilue.leecode.tree.TreeNode; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 09:55 + *@Description: + * TODO leecode199题 二叉树的右视图: + * 给定一个二叉树的 根节点 root,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。 + *@Version: 1.0 + */ +public class T042_RightSideView { + + /** + * 思路:就是返回他所有最右侧的节点,层序遍历确实可以解决,返回每一层最后的节点 + * 速度击败82.5%,内存击败35.82% + * @param root + * @return + */ + public List rightSideView(TreeNode root) { + List result = new ArrayList<>(); + if(root==null){ + return result; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()){ + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if(i==size-1){//每层的最右一个,就是最右节点 + result.add(node.val); + } + if(node.left!=null)queue.offer(node.left); + if(node.right!=null)queue.offer(node.right); + } + } + + return result; + } + + + /** + * 思路:尝试dfs实现层序遍历,返回每一层最后的节点 + * 速度击败100%,内存击败27.1% + * @param root + * @return + */ + List result = new ArrayList<>(); + public List rightSideView1(TreeNode root) { + + + dfs(root,0); + return result; + } + //核心在于:如何判断他是一层最后一个,官方思路:如果先遍历右子树,那么就是那一层第一个 + public void dfs(TreeNode node,int depth){ + if(node==null){ + return; + } + if(result.size()==depth){ + result.add(node.val); + } + dfs(node.right,depth+1); + dfs(node.left,depth+1); + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T043_averageOfLevels.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T043_averageOfLevels.java new file mode 100644 index 0000000..f1c2c9e --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T043_averageOfLevels.java @@ -0,0 +1,130 @@ +package com.markilue.leecode.tree.second; + +import com.markilue.leecode.tree.TreeNode; +import com.markilue.leecode.tree.TreeUtils; +import org.junit.Test; + +import java.util.*; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 10:23 + *@Description: + * TODO leecode637题 二叉树的层平均值: + * 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 + *@Version: 1.0 + */ +public class T043_averageOfLevels { + + @Test + public void test() { + ArrayList list = new ArrayList<>(Arrays.asList(3,9,20,null,null,15,7)); + TreeNode root = TreeUtils.structureTree(list, 0); + System.out.println(averageOfLevels1(root)); + + } + + /** + * 思路:BFS层平均值,求出总和之后除以个数;所以需要知道本层所有节点 + * 速度击败93.66%,内存击败52.84% 2ms + * @param root + * @return + */ + public List averageOfLevels(TreeNode root) { + List result = new ArrayList<>(); + if(root==null){ + return result; + } + + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()){ + int size = queue.size(); + Double sum=0d; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + sum+=node.val; + if(node.left!=null)queue.offer(node.left); + if(node.right!=null)queue.offer(node.right); + } + result.add(sum/size); + } + + return result; + + } + + + /** + * 思路:尝试dfs,获取每层总和和每个节点个数 + * 速度击败93.66%,内存击败21.8% 2ms + * @param root + * @return + */ + List result = new ArrayList<>(); + List depthList = new ArrayList<>();//存储每层个数 + public List averageOfLevels1(TreeNode root) { + + dfs(root,0); + + for (int i = 0; i < result.size(); i++) { + result.set(i,result.get(i)/depthList.get(i));//求平均值 + } + + return result; + + } + public void dfs(TreeNode node,int depth) { + if(node==null){ + return; + } + if(result.size()==depth){ + result.add(0d); + } + if(depthList.size()==depth){ + depthList.add(0); + } + dfs(node.left,depth+1); + dfs(node.right,depth+1); + result.set(depth,result.get(depth)+node.val); + depthList.set(depth,depthList.get(depth)+1); + + } + + + /** + * 官方最快:本质上和本人的dfs无差异,比本人少设一次0值 + * 速度击败100%,内存击败26.25% 1ms + * @param root + * @return + */ + public List averageOfLevels2(TreeNode root) { + List counts = new ArrayList<>(); + List sum = new ArrayList<>(); + dfs2(root, 0, counts, sum); + List result = new ArrayList<>(); + for (int i = 0; i < counts.size(); i++) { + result.add(sum.get(i) / counts.get(i)); + } + return result; + } + + private void dfs2(TreeNode root, int level,List counts, List sum) { + if (root == null) { + return; + } + + if (level < counts.size()) { + counts.set(level, counts.get(level) + 1); + sum.set(level, sum.get(level) + root.val); + } else { + counts.add(1); + sum.add(1.0 * root.val); + } + + dfs2(root.left, level + 1, counts, sum); + dfs2(root.right, level +1, counts, sum); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T044_LevelOrder.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T044_LevelOrder.java new file mode 100644 index 0000000..c506e87 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T044_LevelOrder.java @@ -0,0 +1,79 @@ +package com.markilue.leecode.tree.second; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 10:54 + *@Description: + * TODO 力扣429题 N 叉树的层序遍历: + * 给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。 + * 树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。 + *@Version: 1.0 + */ +public class T044_LevelOrder { + + /** + * 思路:N叉树的层序遍历本质上和二叉树无异BFS层序遍历版 + * 速度击败84.2%,内存击败10.36% 3ms + * @param root + * @return + */ + public List> levelOrder(NNode root) { + List> result = new ArrayList<>(); + if(root==null){ + return result; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()){ + int size = queue.size(); + List cur = new ArrayList<>(); + for (int i = 0; i < size; i++) { + NNode NNode = queue.poll(); + cur.add(NNode.val); + for (NNode child : NNode.children) { + if(child!=null){ + queue.offer(child); + } + } + } + result.add(cur); + } + return result; + + } + + + /** + * 思路:N叉树的层序遍历 尝试dfs版 + * 速度击败100%,内存击败27.66% 0ms + * @param root + * @return + */ + List> result = new ArrayList<>(); + public List> levelOrder1(NNode root) { + + dfs(root,0); + return result; + + } + + public void dfs(NNode NNode, int depth) { + if(NNode ==null){ + return; + } + if(result.size()==depth){ + result.add(new ArrayList()); + } + for (NNode child : NNode.children) { + dfs(child,depth+1); + } + result.get(depth).add(NNode.val); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T045_LargestValues.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T045_LargestValues.java new file mode 100644 index 0000000..6d11de1 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T045_LargestValues.java @@ -0,0 +1,83 @@ +package com.markilue.leecode.tree.second; + +import com.markilue.leecode.tree.TreeNode; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 11:18 + *@Description: + * TODO 力扣515题 在每个树行中找最大值: + * 给定一棵二叉树的根节点 root ,请找出该二叉树中每一层的最大值。 + *@Version: 1.0 + */ +public class T045_LargestValues { + + + /** + * 思路:仍然是每行的最大值,层序遍历的题 BFS + * 速度击败84.54%,内存击败72.7% 2ms + * @param root + * @return + */ + public List largestValues(TreeNode root) { + List result = new ArrayList<>(); + if(root==null){ + return result; + } + + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()){ + int size = queue.size(); + int max=Integer.MIN_VALUE; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if(max result = new ArrayList<>(); + public List largestValues1(TreeNode root) { + + dfs(root,0); + return result; + + } + + public void dfs(TreeNode node,int depth){ + if(node==null){ + return; + } + if(result.size()==depth){ + result.add(node.val); + }else { + Integer now = result.get(depth); + if(now queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()){ + int size = queue.size(); + Node pre=null; + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + if(pre!=null)pre.next=node; + if(node.left!=null)queue.offer(node.left); + if(node.right!=null)queue.offer(node.right); + pre=node; + } + } + return root; + } + + + /** + * 思路:本质上还是每层之间彼此连接DFS,使用一个list记录每一层的上一个节点 + * 速度击败100%,内存击败57.66% 0ms + * @param root + * @return + */ + List preList=new ArrayList<>(); + public Node connect1(Node root) { + dfs(root,0); + return root; + } + + public void dfs(Node node,int depth) { + if(node==null){ + return; + } + if(preList.size()==depth){ + preList.add(node); + }else { + Node pre = preList.get(depth); + pre.next=node; + preList.set(depth,node); + } + dfs(node.left,depth+1); + dfs(node.right,depth+1); + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T047_Connect.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T047_Connect.java new file mode 100644 index 0000000..e5ea8ce --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T047_Connect.java @@ -0,0 +1,132 @@ +package com.markilue.leecode.tree.second; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 11:31 + *@Description: + * TODO 力扣116题 填充每个节点的下一个右侧节点指针: + * 给定一个二叉树 二叉树定义如下: + * struct Node { + * int val; + * Node *left; + * Node *right; + * Node *next; + * } + * 填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。 + * 初始状态下,所有 next 指针都被设置为 NULL。 + * 与T46似乎完全一致,所以代码完全没有改动 + *@Version: 1.0 + */ +public class T047_Connect { + + + /** + * 思路:本质上还是每层之间彼此连接BFS + * 速度击败66.57%,内存击败58.33% 1ms + * @param root + * @return + */ + public Node connect(Node root) { + if(root==null){ + return root; + } + + Deque queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()){ + int size = queue.size(); + Node pre=null; + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + if(pre!=null)pre.next=node; + if(node.left!=null)queue.offer(node.left); + if(node.right!=null)queue.offer(node.right); + pre=node; + } + } + return root; + } + + + /** + * 思路:本质上还是每层之间彼此连接DFS,使用一个list记录每一层的上一个节点 + * 速度击败100%,内存击败67.82% 0ms + * @param root + * @return + */ + List preList=new ArrayList<>(); + public Node connect1(Node root) { + dfs(root,0); + return root; + } + + public void dfs(Node node,int depth) { + if(node==null){ + return; + } + if(preList.size()==depth){ + preList.add(node); + }else { + Node pre = preList.get(depth); + pre.next=node; + preList.set(depth,node); + } + dfs(node.left,depth+1); + dfs(node.right,depth+1); + } + + /** + * 评论区针对第一种queue思路的优化: + * TODO + * 上面运行效率并不是很高,这是因为我们把节点不同的入队然后再不停的出队, + * 其实可以不需要队列,每一行都可以看成一个链表比如第一行就是只有一个节点的链表, + * 第二行是只有两个节点的链表(假如根节点的左右两个子节点都不为空)…… + * 速度击败100%,内存击败65.71% + * @param root + * @return + */ + public Node connect2(Node root) { + if (root == null) + return root; + //cur我们可以把它看做是每一层的链表的头结点 + Node cur = root; + while (cur != null) { + //遍历当前层的时候,为了方便操作在下一 + //层前面添加一个哑结点(注意这里是访问 + //当前层的节点,然后把下一层的节点串起来) + Node dummy = new Node(0); + //pre表示访下一层节点的前一个节点 + Node pre = dummy; + //然后开始遍历当前层的链表 + while (cur != null) { + if (cur.left != null) { + //如果当前节点的左子节点不为空,就让pre节点 + //的next指向他,也就是把它串起来 + pre.next = cur.left; + //然后再更新pre + pre = pre.next; + } + //同理参照左子树 + if (cur.right != null) { + pre.next = cur.right; + pre = pre.next; + } + //继续访问这一行的下一个节点 + cur = cur.next; + } + //把下一层串联成一个链表之后,让他赋值给cur, + //后续继续循环,直到cur为空为止 + cur = dummy.next; + } + return root; + } + +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T048_MaxDepth.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T048_MaxDepth.java new file mode 100644 index 0000000..173d3a5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T048_MaxDepth.java @@ -0,0 +1,72 @@ +package com.markilue.leecode.tree.second; + +import com.markilue.leecode.tree.TreeNode; + +import java.util.LinkedList; +import java.util.Queue; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-06 12:22 + *@Description: + * TODO 力扣104题 二叉树的最大深度: + * 给定一个二叉树,找出其最大深度。 + * 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 + * 说明: 叶子节点是指没有子节点的节点。 + *@Version: 1.0 + */ +public class T048_MaxDepth { + + /** + * 思路:深度本质上就是要遍历到最底层 + * 速度击败19.79%,内存击败56.29% + * @param root + * @return + */ + public int maxDepth(TreeNode root) { + int depth=0; + if(root==null){ + return depth; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()){ + int size = queue.size(); + depth++; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if(node.left!=null)queue.offer(node.left); + if(node.right!=null)queue.offer(node.right); + } + } + return depth; + } + + /** + * 思路:深度本质上就是要遍历到最底层,DFS法 + * 速度击败100%,内存击败10.99% + * @param root + * @return + */ + int maxDepth=0; + public int maxDepth1(TreeNode root) { + dfs(root,1); + return maxDepth; + } + public void dfs(TreeNode node,int depth){ + if(node==null){ + return; + } + if(maxDepth list = new ArrayList<>(Arrays.asList(1,2,3,4,5)); + TreeNode root = TreeUtils.structureTree(list, 0); + System.out.println(minDepth1(root)); + + } + + /** + * 思路: + * 1)如果有左右结点:分别计算左右节点深度取最小; + * 2)如果只有左或右节点,返回另一边深度 + * 3)如果两边有没有了,那么就是叶子结点,返回1 + * 速度击败34.68%,内存击败5.87% 9ms + * 慢在哪里?找到第一个叶子节点了,还是不能返回,得判断另一端 + * @param root + * @return + */ + public int minDepth(TreeNode root) { + if(root==null){ + return 0; + } + if(root.left==null&&root.right==null){ + return 1; + }else if(root.left==null){ + return minDepth(root.right)+1; + }else if(root.right==null){ + return minDepth(root.left)+1; + } + + return Math.min(minDepth(root.left),minDepth(root.right))+1; + + } + + /** + * 思路:尝试使用层序遍历优化,核心在于找到第一个叶子节点就返回 + * 速度击败100%,内存击败73.53% 0ms + * @param root + * @return + */ + public int minDepth1(TreeNode root) { + int depth=0; + if(root==null){ + return depth; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()){ + int size = queue.size(); + depth++; + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if(node.left==null&&node.right==null){ + return depth; + } + if(node.left!=null){ + queue.offer(node.left); + } + if(node.right!=null){ + queue.offer(node.right); + } + } + } + + return depth; + + } +}