diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/MemoryBarrier.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/MemoryBarrier.java new file mode 100644 index 0000000..249fa4e --- /dev/null +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/MemoryBarrier.java @@ -0,0 +1,73 @@ +package com.markilue.java_learning.thread; + +import lombok.Getter; +import org.junit.Test; +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +/** + *@BelongsProject: java_learning + *@BelongsPackage: com.markilue.java_learning.thread + *@Author: dingjiawen + *@CreateTime: 2023-01-08 17:12 + *@Description: + * TODO 测试内存屏障: + * 使用unsafe类中的loadFence增加读屏障,保证内存中的数据一致性 + *@Version: 1.0 + */ +public class MemoryBarrier { + + @Test + public void test() throws NoSuchFieldException, IllegalAccessException { + String s="dingjiawen"; + System.out.println("first s:"+s); + Field value = s.getClass().getDeclaredField("value"); + value.setAccessible(true); + char[] o = (char[])value.get(s); + o[1]='b'; +// value.set(s,"dingjin"); + System.out.println("last s:"+s); + } + + public static void main(String[] args){ + Unsafe unsafe=null; + try { + Field field = Unsafe.class.getDeclaredField("theUnsafe"); + field.setAccessible(true); + unsafe = (Unsafe) field.get(null); + } catch (Exception e) { + } + ChangeThread changeThread = new ChangeThread(); + new Thread(changeThread).start(); + while (true) { + + boolean flag = changeThread.isFlag(); +// System.out.println(flag);//奇怪的是,只要加上这句,好像就可以检测到flag的变化 +// unsafe.loadFence(); //加入读内存屏障 + if (flag){ + System.out.println("detected flag changed"); + break; + } + } + System.out.println("main thread end"); + } + +} + +@Getter +class ChangeThread implements Runnable{ + /**volatile**/ boolean flag=false; + @Override + public void run() { + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("subThread change flag to:" + flag); + flag = true; + System.out.println("subThread change flag to:" + flag); + } +} + diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/T07_CountNodes.java b/Leecode/src/main/java/com/markilue/leecode/tree/T07_CountNodes.java new file mode 100644 index 0000000..3a91bc2 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/T07_CountNodes.java @@ -0,0 +1,103 @@ +package com.markilue.leecode.tree; + +import java.util.LinkedList; +import java.util.Queue; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree + *@Author: dingjiawen + *@CreateTime: 2023-01-09 10:56 + *@Description: + * TODO 力扣222题 完全二叉树的节点个数: + * 给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。 + * 完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外, + * 其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。 + *@Version: 1.0 + */ +public class T07_CountNodes { + + /** + * 思路:递归法,尝试DFS + * 速度击败100%,内存击败85.46% + * @param root + * @return + */ + public int countNodes(TreeNode root) { + count(root); + return count; + } + int count=0; + public void count(TreeNode node) { + if(node==null){ + return; + } + count+=1; + count(node.left); + count(node.right); + } + + /** + * 迭代法:尝试BFS + * 速度击败19.63%,内存击败6.91% + * @param root + * @return + */ + public int countNodes1(TreeNode root) { + int count=0; + if(root==null){ + return count; + } + 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(); + count++; + if(node.left!=null)queue.offer(node.left); + if(node.right!=null)queue.offer(node.right); + } + } + return count; + + } + + /** + * 代码随想录针对普通二叉树的解法 + * + * + * 速度击败100%,内存击败31.23% + */ + public int countNodes3(TreeNode root) { + if (root == null) return 0; + return countNodes3(root.left) + countNodes3(root.right) + 1; + } + + + /** + * 代码随想录针对完全二叉树的解法 + * + * 满二叉树的结点数为:2^depth - 1 + * 速度击败100%,内存击败89.89% + */ + public int countNodes2(TreeNode root) { + if (root == null) return 0; + TreeNode left = root.left; + TreeNode right = root.right; + int leftDepth = 0, rightDepth = 0; // 这里初始为0是有目的的,为了下面求指数方便 + while (left != null) { // 求左子树深度 + left = left.left; + leftDepth++; + } + while (right != null) { // 求右子树深度 + right = right.right; + rightDepth++; + } + if (leftDepth == rightDepth) {//左右深度相等,那么就是满二叉树 + return (2 << leftDepth) - 1; // 注意(2<<1) 相当于2^2,所以leftDepth初始为0 + } + return countNodes2(root.left) + countNodes2(root.right) + 1; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/IsBalance.java b/Leecode/src/main/java/com/markilue/leecode/tree/T08_IsBalance.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/tree/IsBalance.java rename to Leecode/src/main/java/com/markilue/leecode/tree/T08_IsBalance.java index b037f60..1f6edd7 100644 --- a/Leecode/src/main/java/com/markilue/leecode/tree/IsBalance.java +++ b/Leecode/src/main/java/com/markilue/leecode/tree/T08_IsBalance.java @@ -17,7 +17,7 @@ import java.util.List; * 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 * @Version: 1.0 */ -public class IsBalance { +public class T08_IsBalance { @Test diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/BinaryTreePaths.java b/Leecode/src/main/java/com/markilue/leecode/tree/T09_BinaryTreePaths.java similarity index 99% rename from Leecode/src/main/java/com/markilue/leecode/tree/BinaryTreePaths.java rename to Leecode/src/main/java/com/markilue/leecode/tree/T09_BinaryTreePaths.java index 303a8de..53ed1c9 100644 --- a/Leecode/src/main/java/com/markilue/leecode/tree/BinaryTreePaths.java +++ b/Leecode/src/main/java/com/markilue/leecode/tree/T09_BinaryTreePaths.java @@ -17,7 +17,7 @@ import java.util.Queue; * 叶子节点 是指没有子节点的节点。 * @Version: 1.0 */ -public class BinaryTreePaths { +public class T09_BinaryTreePaths { @Test public void test() { diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T08_IsBalance.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T08_IsBalance.java new file mode 100644 index 0000000..1ec77d5 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T08_IsBalance.java @@ -0,0 +1,74 @@ +package com.markilue.leecode.tree.second; + +import com.markilue.leecode.tree.TreeNode; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-09 11:15 + *@Description: + * TODO 二刷力扣110题 平衡二叉树: + * 给定一个二叉树,判断它是否是高度平衡的二叉树。 + * 本题中,一棵高度平衡二叉树定义为: + * 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 + *@Version: 1.0 + */ +public class T08_IsBalance { + + /** + * 思路:递归法:如果两边差大于1了直接返回-1;当-1了就快速返回 + * 速度击败100%,内存击败57.39% + * @param root + * @return + */ + public boolean isBalanced(TreeNode root) { + if(root==null){ + return true; + } + int leftHeight=nodeHeight(root.left); + if(leftHeight==-1){ + return false; + } + int rightHeight=nodeHeight(root.right); + if(rightHeight==-1||Math.abs(leftHeight-rightHeight)>1){ + return false; + } + return true; + + } + + /** + * 代码随想录递归 + * @param root + * @return + */ + public boolean isBalanced1(TreeNode root) { + return nodeHeight(root) == -1 ? false : true; + } + + public int nodeHeight(TreeNode node) { + if(node==null){ + return 0; + } + int leftHeight=nodeHeight(node.left); + if(leftHeight==-1){ + return -1; + } + int rightHeight=nodeHeight(node.right); + if(rightHeight==-1||Math.abs(leftHeight-rightHeight)>1){ + return -1; + } + return Math.max(leftHeight,rightHeight)+1; + } + + /** + * 思路:迭代法,后序遍历是一个比较好的方式 + * @param root + * @return + */ + public boolean isBalanced2(TreeNode root) { + + return false; + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_0_BinaryTreePaths.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_0_BinaryTreePaths.java new file mode 100644 index 0000000..1f21f31 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_0_BinaryTreePaths.java @@ -0,0 +1,116 @@ +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.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-09 11:45 + *@Description: + * TODO 二刷力扣257题 二叉树的所有路径: + * 给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。 + * 叶子节点 是指没有子节点的节点。 + *@Version: 1.0 + */ +public class T09_0_BinaryTreePaths { + @Test + public void test(){ + ArrayList list = new ArrayList<>(Arrays.asList(1,-2,3,null,5)); + TreeNode root = TreeUtils.structureTree(list, 0); + System.out.println(binaryTreePaths(root)); + } + + /** + * 思路:经典前序遍历递归回溯问题 + * 速度击败100%,内存击败71.19%。回溯的删除路径是这题比较麻烦的点,因为他添加的数字位数无法确定 + * @param root + * @return + */ + public List binaryTreePaths(TreeNode root) { + String curPath=""; + backtracking1(root,curPath); + return totalPath; + } + //curPath记录当前路径 + List totalPath=new ArrayList<>();//记录全部路径 + public void backtracking(TreeNode node,StringBuilder curPath){ + curPath.append(node.val); + + if(node.left==null&&node.right==null){ + totalPath.add(new String(curPath)); + return; + } + + if(node.left!=null){ + backtracking(node.left,curPath.append("->"));//否则有下一个节点,所以先拼上-> + curPath.delete(curPath.length()-2,curPath.length()); + } + if(node.right!=null){ + backtracking(node.right,curPath.append("->")); + curPath.delete(curPath.length()-2,curPath.length()); + } + curPath.delete(curPath.lastIndexOf(node.val+""),curPath.length()); + + + } + + public void backtracking1(TreeNode node,String curPath){ + StringBuilder builder = new StringBuilder(curPath); + builder.append(node.val); + if(node.left==null&&node.right==null){ + totalPath.add(new String(builder)); + return; + } + + int length2 = builder.length(); + if(node.left!=null){ + backtracking1(node.left,new String(builder.append("->")));//否则有下一个节点,所以先拼上-> + builder.delete(length2,builder.length()); + } + int length1 = builder.length(); + if(node.right!=null){ + backtracking1(node.right,new String(builder.append("->"))); + builder.delete(length1,builder.length()); + } + +// builder.delete(length,length1); + + + } + + /** + * 官方比较漂亮的写法 + * @param root + * @return + */ + public List binaryTreePaths1(TreeNode root) { + List result = new ArrayList<>(); + constructPaths(root,"",result); + return result; + } + //这里使用StringBuilder,因为他快是因为他适用于同一个string不可变字符串的时候,这里每一个只添加一次 + //这里处理node的左右节点,而不是处理他本身,因为涉及到要不要新new上一个List + public void constructPaths(TreeNode node,String path, List result) { + + if(node!=null){ + StringBuilder pathDS = new StringBuilder(path); + pathDS.append(node.val); + //遍历到了叶子结点,可以把结果加入 + if(node.left==null&&node.right==null){ + result.add(pathDS.toString()); + }else{ + //不是叶子节点,需要继续遍历 + pathDS.append("->"); + constructPaths(node.left,pathDS.toString(),result); + constructPaths(node.right,pathDS.toString(),result); + } + } + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_1_IsSameTree.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_1_IsSameTree.java new file mode 100644 index 0000000..0657850 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_1_IsSameTree.java @@ -0,0 +1,76 @@ +package com.markilue.leecode.tree.second; + +import com.markilue.leecode.tree.TreeNode; + +import java.util.Stack; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-09 12:46 + *@Description: + * TODO 力扣100题 相同的树: + * 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 + * 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 + *@Version: 1.0 + */ +public class T09_1_IsSameTree { + + /** + * 思路:即完全相同,值相同子树不相同也可以,实际上可以依次遍历个节点。递归法DFS前序遍历 + * 速度击败100%,内存击败72.54% + * @param p + * @param q + * @return + */ + public boolean isSameTree(TreeNode p, TreeNode q) { + if(p==null&&q==null){ + return true; + } + if(p==null||q==null)return false;//其中一个为null + return p.val==q.val&&isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);//都不为null + + } + + /** + * 思路:即完全相同,值相同子树不相同也可以,实际上可以依次遍历个节点。迭代法BFS + * 速度击败100%,内存击败20.92% + * @param p + * @param q + * @return + */ + public boolean isSameTree1(TreeNode p, TreeNode q) { + if(p==null&&q==null){ + return true; + } + if(p==null||q==null)return false;//其中一个为null + Stack stack = new Stack<>(); + stack.push(p); + stack.push(q);//一直同进同出即可 + while (!stack.isEmpty()){ + int size = stack.size(); + for (int i = 0; i < size; i+=2) { + TreeNode node1 = stack.pop(); + TreeNode node2 = stack.pop(); + if(node1==null&&node2==null){ + continue; + } + if(node1==null||node2==null){ + return false;//其中一个为null + }else { + //全不为null + if(node1.val!=node2.val){ + return false; + } + stack.push(node1.left); + stack.push(node2.left); + stack.push(node1.right); + stack.push(node2.right); + } + } + } + return true;//都不为null + + } +} diff --git a/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_2_IsSubtree.java b/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_2_IsSubtree.java new file mode 100644 index 0000000..6930f54 --- /dev/null +++ b/Leecode/src/main/java/com/markilue/leecode/tree/second/T09_2_IsSubtree.java @@ -0,0 +1,108 @@ +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.ArrayList; +import java.util.Arrays; + +/** + *@BelongsProject: Leecode + *@BelongsPackage: com.markilue.leecode.tree.second + *@Author: dingjiawen + *@CreateTime: 2023-01-09 13:00 + *@Description: + * TODO 力扣572题 另一颗子树: + * 给你两棵二叉树 root 和 subRoot 。 + * 检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。 + * 二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。 + * + *@Version: 1.0 + */ +public class T09_2_IsSubtree { + + @Test + public void test() { + ArrayList list = new ArrayList<>(Arrays.asList(3,4,5,1,2)); + ArrayList list1 = new ArrayList<>(Arrays.asList(4,1,2)); + TreeNode root = TreeUtils.structureTree(list, 0); + TreeNode subRoot = TreeUtils.structureTree(list1, 0); + System.out.println(isSubtree(root,subRoot)); + + } + + /** + * 思路:T09_1的浅进阶,找到相同的root后,开始比较子树 + * 速度击败92.32%,内存击败70.43% + * @param root + * @param subRoot + * @return + */ + public boolean isSubtree(TreeNode root, TreeNode subRoot) { + if(root==null&&subRoot==null){ + return true; + } + if(root==null||subRoot==null){ + return false;//其中一个为null + } + boolean flag=false; + boolean flag1=false; + if(root.val==subRoot.val){ + //现在相同子树对象 + flag=isSameTree(root.left,subRoot.left)&&isSameTree(root.right,subRoot.right); + } + if(flag==false){//在开始比较两个子树的过程中就不能进这个了 + flag=isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);//只要有子树一样即可 + } + return flag; + + + } + /** + * 思路:即完全相同,值相同子树不相同也可以,实际上可以依次遍历个节点。递归法DFS前序遍历 + * 速度击败100%,内存击败72.54% + * @param p + * @param q + * @return + */ + public boolean isSameTree(TreeNode p, TreeNode q) { + if(p==null&&q==null){ + return true; + } + if(p==null||q==null)return false;//其中一个为null + return p.val==q.val&&isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);//都不为null + + } + + + /** + * 官方最快 + * 速度击败100%,内存击败5.4% + * @param root + * @param subRoot + * @return + */ + public boolean isSubtree1(TreeNode root, TreeNode subRoot) { + if(root==null&&subRoot!=null) return false; + if(root!=null&&subRoot==null) return false; + if(root==null&&subRoot==null) return true; + if(root.val==subRoot.val){ + boolean res=isSametree1(root,subRoot); + if(res==true) return res; + } + boolean leftIsSubtree=isSubtree(root.left,subRoot); + boolean rightIsSubtree=isSubtree(root.right,subRoot); + return leftIsSubtree||rightIsSubtree; + + } + public boolean isSametree1(TreeNode root, TreeNode subRoot) { + if(root==null&&subRoot!=null) return false; + if(root!=null&&subRoot==null) return false; + if(root==null&&subRoot==null) return true; + if(root.val!=subRoot.val) return false; + boolean leftIsSametree=isSametree1(root.left,subRoot.left); + boolean rightIsSametree=isSubtree1(root.right,subRoot.right); + return leftIsSametree&&rightIsSametree; + } +}