diff --git a/src/main/java/name/zicat/leetcode/alibaba/TestUtil.java b/src/main/java/name/zicat/leetcode/alibaba/TestUtil.java new file mode 100644 index 0000000..fac1f5b --- /dev/null +++ b/src/main/java/name/zicat/leetcode/alibaba/TestUtil.java @@ -0,0 +1,238 @@ +package name.zicat.leetcode.alibaba; + +import name.zicat.leetcode.tree.TreeNode; + +public class TestUtil { + public static void main(String[] args) throws Exception{ + + int[] A = {5,-3,5}; + System.out.println("maxSubArraySumCircular(A) = " + maxSubArraySumCircular(A)); + + } + + + /** + * 1035 不相交的线 + * + * 基本思路就是:dp[i][j]表示A截止到i,B截止到j点,此时的最大连线数 + * + * 转移方程为: + * + * 当A[i] == B[j]时: dp[i][j] = dp[i - 1][j - 1] + 1 + * 当A[i] != B[j]时: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + * + * 是个九宫图 + * + */ + + public static int maxUncrossedLines(int[] A,int[] B) { + int[][] dp = new int[A.length+1][B.length+1]; + for(int i =0;i这个结点待覆盖 + * 1=>这个结点已经覆盖 + * 2=>这个结点上安装了相机 + * + * 建立数学模型来描述问题 + * 把求解的问题分成若干个子问题 + * 对每个子问题求解,得到子问题的局部最优解 + * 把子问题的解局部最优解合成原来问题的一个解 + */ + static int res; + public static int minCameraCover(TreeNode root){ + if(lrd(root)==0){ + res ++; + } + return res; + + + } + + public static int lrd(TreeNode node){ + if(node ==null){ + return 1; + } + + int left = lrd(node.left); + int right = lrd(node.right); + /** + * 左孩子和右孩子的覆盖情况,都待覆盖的话 + */ + if(left == 0|| right ==0){ + res ++; + return 2; + } + /** + * 左右孩子 已经覆盖的情况,那当前节点 待覆盖 + */ + if(left == 1 && right ==1){ + return 0; + } + /** + * 左右孩子中间 有一个 是 装了 相机,那该节点已经覆盖 + */ + if(left + right >=3){ + return 1; + } + + return -1; + } + + /** + * 918 + * 给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。 + * + * 在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i < A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] = C[i]) + * + * 示例 1: + * + * 输入:[1,-2,3,-2] + * 输出:3 + * 解释:从子数组 [3] 得到最大和 3 + * 示例 2: + * + * 输入:[5,-3,5] + * 输出:10 + * 解释:从子数组 [5,5] 得到最大和 5 + 5 = 10 + * 示例 3: + * + * 输入:[3,-1,2,-1] + * 输出:4 + * 解释:从子数组 [2,-1,3] 得到最大和 2 + (-1) + 3 = 4 + * 示例 4: + * + * 输入:[3,-2,2,-3] + * 输出:3 + * 解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3 + * 示例 5: + * + * 输入:[-2,-3,-1] + * 输出:-1 + * 解释:从子数组 [-1] 得到最大和 -1 + */ + + public static int maxSubArraySumCircular(int[] A) { + int[] A2= new int[A.length *2]; + int n = A.length; + + + /** + * + */ + for(int i=0;i2->3->3->4->4->5] + * output [1->2->5] + */ + + public static ListNode deleteDuplicates(ListNode head) { + + ListNode dummpy = new ListNode(-1); + dummpy.next = head; + ListNode first = dummpy; + ListNode second = head; + + + + while(second!=null &&second.next !=null){ + /** + * 如果不相等 两个指针 都 往后 走 + */ + if(first.next.val!=second.next.val){ + first = first.next; + second = second.next; + } + /** + * 如果相等,second往后走 直到 不相等为止,不相等后 first next指正 指向 second + */ + else { + while(second!=null && second.next!=null&& first.next.val==second.next.val){ + second = second.next; + } + first.next = second.next; + second = second.next; + } + + } + return dummpy.next; + + } + + /** + * 42 + */ + + + /** + * 80 + */ + + /** + * 19 + * + * + */ + + /** + * 82 + */ + + /** + * + * 1171 + */ + + + + } diff --git a/src/main/java/name/zicat/leetcode/array/LRUCache.java b/src/main/java/name/zicat/leetcode/array/LRUCache.java new file mode 100644 index 0000000..0b2aec0 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/array/LRUCache.java @@ -0,0 +1,147 @@ +package name.zicat.leetcode.array; + +import java.util.HashMap; + +/** + * 146. + * LRU,最近最少使用缓存机制 + * 1.固定大小,限制内存占用 + * 2. 插入和查找的效率要高, 最好是O(1) + * 3. 缓存超过界限后删除不常用的 + * + * HashMap + 双链表来实现 + * HashMap保存 key和数据的地址,双链表保存数据的值 + * 最近更新的数据 存在双链表的 链表头 + * + * put操作 没超过容量的情况下 put存在双链表的头 + * + * 容量变小的话, remove end 并且 setHeader + * + * Get操作 + * get K3,并把该值移到 链表的头 + * + *获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。 + * 写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。 + * + * 首先插入删除两个操作都需要在O(1)O(1)的时间复杂度中完成,根据这点就已经排除了选择数组来作为存储结构的可能,基本的方向就是使用链表。 + * + * 然后使用单链表双链表其实都可以,只是若使用单链表的话,需要通过前驱来访问目标结点才能够进行删除/插入操作,不太直观,所以我更推荐使用双链表。 + * + * 但是链表仍有存在一个问题,访问某个元素不能做到O(1)O(1),即不支持随机访问。为此,可以结合哈希表进行辅助。因为出现key冲突的情况,按照题目要求只需要进行值的覆盖,所以哈希表处理冲突的方式只需要覆盖原值,故随机访问的速度不会退化,一直是O(1)O(1)。 + * + * 作者:beney-2 + * 链接:https://leetcode-cn.com/problems/lru-cache/solution/lruhuan-cun-ji-zhi-by-beney-2/ + * 来源:力扣(LeetCode) + * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 + */ +public class LRUCache { + /** + * key 和 节点 这个 节点 是在一个 双向链链表里面,有 pre和 next + * value ,Node的地址 + */ + HashMap map = new HashMap<>(); + + int capacity =0; + /** + * 头节点插入 + * + * 尾节点删除 + */ + Node head =null; + Node end = null; + + public LRUCache(int capacity){ + this.capacity = capacity; + } + + /** + * get 操作 + * @param key + * @return + */ + public int get(int key){ + if(map.containsKey(key)){ + Node node = map.get(key); + //链表中 去掉 当前节点,并且 移到链表头 + remove(node); + setHeader(node); + return node.value; + } else { + return -1; + } + + } + + + /** + * put 操作 + * @param key + * @param value + */ + public void put(int key,int value){ + //存在该节点 + if(map.containsKey(key)){ + Node node = map.get(key); + //更新 改node节点 value的值 + node.value = value; + remove(node); + setHeader(node); + //不存在 该节点 + } else { + Node newNode = new Node(key,value); + //判断 map的大小是否超过 capacity + if(map.size() >= capacity){ + //删掉最后的节点 key和 node + map.remove(end.key); + remove(end); + } + // + setHeader(newNode); + map.put(key,newNode); + } + } + + /** + * reset 双链表的头节点,容易丢指针, + * @param node + */ + private void setHeader(Node node) { + /** + * 看不懂 + */ + node.pre = null; + node.next = head; + if( head!=null){ + head.pre = node; + } + + head = node; + if(end == null) end = head; + + } + + /** + * 删除 双链表的 节点 + * @param node + */ + private void remove(Node node) { + + /** + * 头节点和尾节点的特殊处理 + */ + if(node.pre ==null){ + head = node.next; + } else { + node.pre.next = node.next; + } + + if(node.next == null){ + end = node.pre; + } else { + node.next.pre = node.pre; + } + } + + + +} diff --git a/src/main/java/name/zicat/leetcode/array/ListNode.java b/src/main/java/name/zicat/leetcode/array/ListNode.java new file mode 100644 index 0000000..df1286f --- /dev/null +++ b/src/main/java/name/zicat/leetcode/array/ListNode.java @@ -0,0 +1,7 @@ +package name.zicat.leetcode.array; + +public class ListNode { + public int val; + public ListNode next; + public ListNode(int x) { val = x;} +} diff --git a/src/main/java/name/zicat/leetcode/array/Node.java b/src/main/java/name/zicat/leetcode/array/Node.java new file mode 100644 index 0000000..e7a5d1a --- /dev/null +++ b/src/main/java/name/zicat/leetcode/array/Node.java @@ -0,0 +1,13 @@ +package name.zicat.leetcode.array; + +public class Node { + int key; + int value; + Node pre; + Node next; + + public Node(int key, int value){ + this.key =key; + this.value = value; + } +} diff --git a/src/main/java/name/zicat/leetcode/bytedance/TestUtil.java b/src/main/java/name/zicat/leetcode/bytedance/TestUtil.java new file mode 100644 index 0000000..4f03665 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/bytedance/TestUtil.java @@ -0,0 +1,61 @@ +package name.zicat.leetcode.bytedance; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +public class TestUtil { + + public static void main(String[] args) { + } + /** + * + */ + + public static String[] findWords(String[] words) { + char[] first = {'Q','W','E','R','T','Y','U','I','O','P'}; + char[] second = {'A','S','D','F','G','H','J','K','L'}; + char[] third = {'Z','X','C','V','B','N','M'}; + HashSet firstSet = new HashSet(); + HashSet secondSet = new HashSet(); + HashSet thirdSet = new HashSet(); + + for(char firstStr: first){ + firstSet.add(firstStr); + } + for(char secondStr: second){ + secondSet.add(secondStr); + } + for(char thirdStr: third){ + thirdSet.add(thirdStr); + } + List arrayList = new ArrayList<>(); + for(String word:words){ + boolean firstBool = true; + boolean secondBool = true; + boolean thirdBool = true; + for(int i=0;i end){ + return -1; + } + + + int mid = start + (end-start)/2; + if(nums[mid] > target){ + return searchBinaryDivide(nums,start,mid-1,target); + } else if(nums[mid]< target){ + return searchBinaryDivide(nums,mid+1,end,target); + } else { + return mid; + } + } +} + diff --git a/src/main/java/name/zicat/leetcode/dynmaic/program/DynamicProgramming.java b/src/main/java/name/zicat/leetcode/dynmaic/program/DynamicProgramming.java new file mode 100644 index 0000000..ee71a6d --- /dev/null +++ b/src/main/java/name/zicat/leetcode/dynmaic/program/DynamicProgramming.java @@ -0,0 +1,374 @@ +package name.zicat.leetcode.dynmaic.program; + +/** + * 动态规划 分类 + * + * + * 动态规划的 三要素 + * + * 1.状态变量 + * + * 2.递推方程 + * + * 3.初始条件 + * + * 动态规划分类 + * + * 序列型 -关键词是 前 ,前i个的最大值最小值等 + * + * 划分型 + * + * 博弈题目 + * + * 背包问题 + * + */ +public class DynamicProgramming { +/** + * 序列型 动态规划 + * 198 打家劫舍 + * + * 盗窃4个房屋 得到的金子 ,不可偷相邻的 + * 输入[1,2,3,1] + * 输出 1+3 =4 + * + * [2,7,9,3,1] + * + * 2+9+1 =12 + * 对于每个房屋是2种选择 + * 偷还是不偷 + * + * 最后一个房子是偷还是不偷 + * 来定义转移方程 + * + * 1.定义状态变量 dp[i] 表示盗窃前i家房子最多收益 + * 序列型 是 前i个最大值最小值 最多收益 等等 + * 从第一个开始 连续的序列 + * + * 2.转移方程: + * dp[i] =max(dp[i-2]+house[i],dp[i-1]) + * + * dp[i-2] + house[i] 表示 盗窃房间i的钱,加上盗窃前i-2房子最多收益 + * + * dp[i-1] 表示 不盗窃房间i,盗窃i-1家房子最多收益 + * + * 3.初始状态: + * dp[0] =0 + * dp[1] =house[1] 前1个房子的最多收益 就是 偷取第一个房子的钱 + * + */ + +public int rob(int[] nums){ + /** + * 边界情况 + */ + if(nums==null || nums.length ==0){ + return 0; + } + + if(nums.length ==1){ + return nums[0]; + } + + int[] dp = new int[nums.length+1]; + dp[0] =0; + dp[1] =nums[0]; + + //转移方程 + for(int i=2;i<= nums.length;i++){ + dp[i] = Math.max(dp[i-2]+nums[i-1],dp[i-1]); + } + return dp[nums.length]; +} + + +/** + * 序列型 动态规划 + * 264,粉刷房子2 + * [[1,5,3],[2,9,4]] + * + * 从最后一步入手 + * 粉刷到最后一个房子 要保证 颜色和前面的不同,并且花费最小 + * 罗列最后的房子 粉刷的颜色,同事求出 前一个房子 不同颜色的情况下 加上最后的房子的最小值 + * + * 1.状态变量:dp[i][j] 表示粉刷前i个房子,第i个房子的颜色 j有k种可能性, + * 代表粉刷了i个房子,并且颜色为j的最小花费 + * + * 2.转移方程: dp[i][j]=min( x=0...k)(dp[i-1][x] + costs[i-1][j]|x!=j) 粉刷前i-1的花费和最后一个房子的最小值 + * x!=j 因为 相邻的房子 颜色不同 + * + * 3. 初始状态 + * dp[0][j] =0 前0个房子 粉刷各种颜色的最小收益 是0 + * dp[1][j] =cost[0][j] 前1个房子各种颜色的最小花费 + */ +//没有滚动数组的情况下 +public int minCost(int[][] costs){ + if(costs.length == 0) return 0; + int n = costs.length; + int k = costs[0].length; + int result = Integer.MAX_VALUE; + //另外开一个维度的数组 来保存颜色 + int[][] dp = new int[n+1][k]; + //对他进行初始化 + for(int i=0;i=0 && j=0 && j= sum; + } + + /** + * 背包问题 + * + * 416 分割等和子集 + * + * [1,5,11,5] + * true + * + * 数组可以分割成[1,5,5]和[11] + * + * 放入背包和 不放入背包 + * + * 数组下标 拼出来的 容积,数组长度 是 背包容积+1 + * + * 分割等和自己 + * + * 状态变量 + * dp[i] 是否可以拼出 和为 i + * + * 转移方程 dp[i]=dp[i]||dp[i-nums[j-1]]|i>=nums[j-1] + * + * 初始状态 + * dp[0]=true + * + * 完全没听懂 + */ + + public boolean canPartition(int[] nums){ + int n=nums.length; + int sum=0; + for(int i=0;i=num;j--){ + if(dp[j-num]) dp[j]=true; + } + } + return dp[sum]; + } + /** + * 有一串长度为N的小球排成一排,要求将他们全部染色, + * 共有k种颜色,但是不能出现连续三个及以上相同颜色的珠子。 + * 输入长度N和颜色种类K,求所有染色的方法总数 + * + * 排列问题暴力破解的时间复杂度为O(k^n)。 + * + * 可以考虑DP,因为第i个小球的颜色只受i-1和i-2小球颜色的影响。 + * + * 用dp[i-2]表示当染色到i-2个球时,所有的染色方法总数,则: + * + * 若将i-1和i染成同样的颜色,那么i和i-1有k-1种染色方法,因为只需和i-2不同即可。此种情况下,染色总方法数为(i和i-1的染色方法数)*(前i-2的染色方法总数) 即:(k-1)*dp[i-2]; + * + * 若将i-1和i染成不同的颜色, 那么i的颜色选择和i-2无关,此时i仍然有k-1种颜色选择。此种情况下,染色总方法数为(i的染色方法数)*(前i-1的染色方法总数)即:(k-1)*dp[i-1]; + * + * 综合两种情况,dp[i]=(k-1)*dp[i-2] + (k-1)*dp[i-1]; + * + * 当k=2时,退化为dp[i] = dp[i-2] + dp[i-1]; + */ + + public int numWays(int n, int k) { + if (n <= 2) { + return k*k; + } else { + int[] m = new int[n]; + m[0] = k; + m[1] = k*k; + for (int i = 2; i < n; i++) { + m[i] = m[i-1] * (k-1) + m[i-2] * (k-1); + } + return m[n-1]; + } + } + +} + + diff --git "a/src/main/java/name/zicat/leetcode/dynmaic/program/\345\212\250\346\200\201\350\247\204\345\210\222.png" "b/src/main/java/name/zicat/leetcode/dynmaic/program/\345\212\250\346\200\201\350\247\204\345\210\222.png" new file mode 100644 index 0000000..4222fdc Binary files /dev/null and "b/src/main/java/name/zicat/leetcode/dynmaic/program/\345\212\250\346\200\201\350\247\204\345\210\222.png" differ diff --git a/src/main/java/name/zicat/leetcode/graph/Graph.java b/src/main/java/name/zicat/leetcode/graph/Graph.java new file mode 100644 index 0000000..347a0f2 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/graph/Graph.java @@ -0,0 +1,172 @@ +package name.zicat.leetcode.graph; + +import java.util.Arrays; + +/** + * 1.图的定义 + * 图是由顶点的有穷非空集合和顶点之间的边的集合组成, + * 通常表示为G(V,E),其中G表示一个图, + * V表示图中顶点的集合,E是图中边的集合 + * + * 无向图:顶点之间边没有方向 + * 有向图:顶点之间边有方向 + * 权:图中边相关的数据,用来表示从一个顶点到另一个顶点的距离或者耗时 + * + * 2.树是一种特殊的图 树是没有环的图 + * + * 3.图的深度优先和广度优先遍历 + * + * 4.并查集在图中的应用 + * + * 5.拓扑排序 + */ +public class Graph { + /** + * 785.判断二分图 + * 邻接表 + * graph[i]表示图中与节点i相连的所有节点。每个节点都是一个在0到graph.length-1之间的整数。这图中没有自环和平行边: graph[i] 中不存在i,并且graph[i]中没有重复的值 + * 输入:[[1,3],[0,2],[1,3],[0,2]] + * 0 [1,3] 与节点0 相邻的节点是 1和3 + * 1 [0,2] 与节点1 相邻的节点是 0和2 + * 2 [1,3] 与节点2 相邻的节点是 1和3 + * 3 [0,2] 与节点3 相邻的节点是 0和2 + * 0 -- 1 + * | | + * | | + * 3 -- 2 + * 输出: true + * + * 可以将节点分成两组 {0,2}和{1,3} + * 同理 + * 输入:[[1,2,3],[0,2],[0,1,3],[0,2]] + * 0 —— 1 + * | \ | + * 3 —— 2 + * 输出: false + * + * 方法:相邻的节点 的颜色不一样 + * 时间复杂度是 O(N+E) + * + * + */ + + public boolean isBipartite(int[][] graph){ + /** + * 表示 相邻节点的颜色,初始值为0,表示没有着色 + */ + int[] colors = new int[graph.length]; + //1和 -1表示颜色分组 + //O(N) + for(int i=0;i1 边 1 选择parent 为0 + * 2->4 4 选择 parent 为2 + * 如果 1——2 因为在两个集合里面,需要合并 1-2这两个集合 + * 1-2 需要把父节点都 连接起来, 0,2中选择 一个 作为 parent + * 那0作为 parent + * 1——>4 这个边 查找 是不是同一个 parent,他们的parent的都是0 + * 如果 再加这个边,那么一定会 形成环 + */ + + /** + * 261.以图判树 + * + * 树: + * 不包含环 + * 树所有顶点是联通的,所以边数是 顶点数-1 + * + * 输入 n=5,边列表 edges =[[0,1],[0,2],[0,3],[1,4]] + * 输出 true + * + * 输入 n=5 edges =[[0,1],[1,2],[2,3],[1,3],[1,4]] + * 输出 false + */ + + public boolean validTree(int n,int[][] edges){ + /** + * 用来保存 每个点集合的代表点 + */ + int[] parent = new int[n]; + Arrays.fill(parent,-1); + for(int[] edge:edges){ + int x = edge[0]; + int y = edge[1]; + //查找 这个集合代表是不是相等的 + x = find(parent,x); + y = find(parent,y); + if(x == y){ + return false; + } + /** + * 不相等的话就是个 union操作 + */ + parent[x]=y; + } + + /** + * 树 的第二个条件 边的 数量 等于节点数-1 + */ + return edges.length == n-1; + } + + public int find(int[] parent, int i){ + /** + * 如果自己是 代表节点 返回 i + */ + if(parent[i] == -1) + return i; + /** + * 查找他的 parent + 路径压缩的操作 + */ + return parent[i] = find(parent,parent[i]); + } + + + + +} diff --git a/src/main/java/name/zicat/leetcode/index/Index.java b/src/main/java/name/zicat/leetcode/index/Index.java new file mode 100644 index 0000000..eee51f2 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/index/Index.java @@ -0,0 +1,451 @@ +package name.zicat.leetcode.index; + +import name.zicat.leetcode.array.ListNode; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.TreeMap; + +/** + * 双指针和滑动窗口 + * + * 快慢指针 链表的问题,链表是否有环 + * 初始情况,快慢指针 都 在head + * + * 左右指针 数组或者字符串中的问题,比如二分查找 + * + * 左右指针是 维护一个区间, 主要再二分查找,翻转数组,滑动窗口 + * + * + * 滑动窗口 是 子字符串匹配的问题 + * + */ +public class Index { + + /** + * 环型链表 是否有 环,并且环的入口是啥 + * 设两指针 fast,slow 指向链表头部 head,fast 每轮走 22 步,slow 每轮走 11 步; + * 第一种结果: fast 指针走过链表末端,说明链表无环,直接返回 null; + * 输入 head =[3,2,0,-4],pos =1 + * 输出 + * + * 第二种结果: 当fast == slow时, 两指针在环中第一次相遇 。 + * + * 1.设链表共有 a+b个节点,其中链表头部到链表入口有a个节点(不计链表入口节点,链表环有b个节点 + * fast 走的步数是slow步数的2倍,即 f = 2s + * fast 比slow多走了 n 个环的长度,即 f = s + nb( 解析: 双指针都走过 a 步,然后在环内绕圈直到重合,重合时 fast 比 slow 多走 环的长度整数倍 ); + * 以上两式相减得:f = 2nb,s = nb,即fast和slow 指针分别走了 2n,n 个 环的周长 。 + * + * 如果让指针从链表头部一直向前走并统计步数k,那么所有 走到链表入口节点时的步数 是:k=a+nb(先走 aa 步到入口节点,之后每绕 11 圈环( bb 步)都会再次到入口节点)。 + * 而目前,slow 指针走过的步数为 nbnb 步。因此,我们只要想办法让 slow 再走 aa 步停下来,就可以到环的入口。 + * 但是我们不知道 a 的值,该怎么办?依然是使用双指针法。我们构建一个指针,此指针需要有以下性质:此指针和slow 一起向前走 a 步后,两者在入口节点重合。那么从哪里走到入口节点需要 aa 步?答案是链表头部head + * + * @param head + * @return + */ + public ListNode detectCycle(ListNode head){ + ListNode fast = head, slow = head; + //判断 是否有环 + while (true) { + if (fast == null || fast.next == null) return null; + fast = fast.next.next; + slow = slow.next; + if (fast == slow) break; + } + + //有环的话,把fast 重置到 head, 来走 a步寻找到 环的入口 + fast = head; + while (slow != fast) { + slow = slow.next; + fast = fast.next; + } + return fast; + + } + /** + * 3.无重复字符的最长子串 + * 滑动窗口 来做 + * + * 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 + * + * 示例 1: + * + * 输入: "abcabcbb" + * 输出: 3 + * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 + * 示例 2: + * + * 输入: "bbbbb" + * 输出: 1 + * 解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。 + * 示例 3: + * + * 输入: "pwwkew" + * 输出: 3 + * 解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。 + *   请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。 + * + * 定义 两个指针和一个 set + * + *算法思路: + * windows是滑窗内字符的集合,初始化为空。从前向后移动滑窗,同时更新curr_len和max_len。 + * 如果ch已存在windows中,那么从滑窗的左端起删除字符,直到删除ch。每删除一个字符cur_len减1。 + * 将ch添加到windows中,cur_len加1。 + * 更新max_len + * + */ + public int lengthOfLongestSubstring(String s) { + /** + * 临界情况 + */ + if (s == null){ + return 0; + } + HashSet window = new HashSet(); + int left = 0, cur_len = 0, max_len = 0; + char[] sc = s.toCharArray(); + for (char ch: sc){ + // 从前向后删除,直到删除了ch + while (window.contains(ch)){ + window.remove(sc[left]); + left ++; + cur_len --; + } + // 添加ch + window.add(ch); + cur_len ++; + // 更新长度 + max_len = Math.max(max_len, cur_len); + } + return max_len; + } + + /** + * 1438 绝对差 不超过限制的最长连续子数组 + * + * nums=[8,2,4,7] limit =4 + * + * 输出2 + * + * 示例 1: + * + * 输入:nums = [8,2,4,7], limit = 4 + * 输出:2 + * 解释:所有子数组如下: + * [8] 最大绝对差 |8-8| = 0 <= 4. + * [8,2] 最大绝对差 |8-2| = 6 > 4. + * [8,2,4] 最大绝对差 |8-2| = 6 > 4. + * [8,2,4,7] 最大绝对差 |8-2| = 6 > 4. + * [2] 最大绝对差 |2-2| = 0 <= 4. + * [2,4] 最大绝对差 |2-4| = 2 <= 4. + * [2,4,7] 最大绝对差 |2-7| = 5 > 4. + * [4] 最大绝对差 |4-4| = 0 <= 4. + * [4,7] 最大绝对差 |4-7| = 3 <= 4. + * [7] 最大绝对差 |7-7| = 0 <= 4. + * 因此,满足题意的最长子数组的长度为 2 。 + */ + + public int longestSubArray(int[] A,int limit){ + /** + * 两个指正 维护 窗口 + */ + int left =0; + int right; + int ans =0; + /** + * treemap 保存最大值和最小值 + * 整数值 保存为 key key中 整数 在滑动窗口中的个数保存为 value + */ + // treemap add,remove,containsKey o(nlogn) + TreeMap m = new TreeMap<>(); + /** + * 移动右指针 扩大窗口的范围 + */ + for(right =0;right limit){ + //缩小 滑动窗口 + m.put(A[left],m.get(A[left])-1); + if(m.get(A[left])==0){ + m.remove(A[left]); + } + left ++; + } + ans = Math.max(ans,right - left +1); + + } + return ans; + + } + + /** + * 看不懂 + * @param A + * @param limit + * @return + */ + public int longestSubArray2(int[] A,int limit){ + // complexity O(n) + Deque maxd = new ArrayDeque<>(); + Deque mind = new ArrayDeque<>(); + int i=0,j; + int ans =0; + for(j=0;j< A.length;++j){ + while(!maxd.isEmpty() && A[j]>maxd.peekLast()) maxd.pollLast(); + while(!mind.isEmpty() && A[j] < mind.peekLast()) mind.pollLast(); + maxd.addLast(A[j]); + mind.addLast(A[j]); + while(maxd.peek() - mind.peek() > limit) { + if(maxd.peek() == A[i]) maxd.poll(); + if(mind.peek() == A[i]) mind.poll(); + ++i; + } + ans = Math.max(ans,j-i+1); + } + return ans; + } + + /** + * 1004 最大连续 1的 个数,最多可以将K个0变成1 + * 输入 A [1,1,1,0,0,0,1,1,1,1,0] K=2 + * [1,1,1,0,0,1,1,1,1,1] + * 数字从 0 翻转到1,最长的子数组长度为 6 + * + */ + + public int longestOnes(int[] A,int K) { + int left =0,right; + int ans =0; + for(right =0;right 1 + */ + for(;right=0){ + sum = sum +A[left]; + if(sum ==S){ + count = count +1; + } else if(sum >S){ + break; + } + left --; + } + sum =0; + } + return count; + } + + /** + * 三指针法 + * 和相同的二元子数组 + * + * 我们遍历区间的右端点 j,同时维护四个变量: + * + * sum_lo:A[i_lo..j] 的值; + * + * sum_hi:A[i_hi..j] 的值; + * + * i_lo:最小的满足 sum_lo <= S 的 i; + * + * i_hi:最大的满足 sum_hi <= S 的 i。 + * + * 例如,当数组 A 为 [1,0,0,1,0,1],S 的值为 2 。当 j = 5 时,i_lo 的值为 1,i_hi 的值为 3。对于每一个 j,和为 S 的非空子数组的数目为 i_hi - i_lo + 1。 + */ + + public static int numSubArrayWithSum(int[] A,int S){ + int iLo = 0,iHi=0; + int sumLo=0,sumHi =0; + int ans =0; + for(int j=0;jS){ + sumLo-=A[iLo++]; + //相当于 sumLo-=A[iLo],iLo++ + } + sumHi +=A[j]; + /** + * Hi 两种情况 + * A[iHi] =1的话只要判断 sumHi>s即可 + * A[iHi] =0的话,要判断 sumHi ==S + */ + while(iHi S ||sumHi ==S &&A[iHi]==0)) + sumHi -=A[iHi++]; + + if(sumLo ==S){ + ans += iHi-iLo +1; + } + } + return ans; + } + + /** + * K个不同整数的子数组 + * 给定一个正整数数组 A,如果 A 的某个子数组中不同整数的个数恰好为 K,则称 A 的这个连续、不一定独立的子数组为好子数组。 + * + * (例如,[1,2,3,1,2] 中有 3 个不同的整数:1,2,以及 3。) + * + * 返回 A 中好子数组的数目。 + * 输入:A = [1,2,1,2,3], K = 2 + * 输出:7 + * 解释:恰好由 2 个不同整数组成的子数组:[1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2]. + * + * 输入:A = [1,2,1,3,4], K = 3 + * 输出:3 + * 解释:恰好由 3 个不同整数组成的子数组:[1,2,1,3], [2,1,3], [1,3,4]. + * + */ + + /** + * 暴力解法 会超时 + * @param A + * @param K + * @return + */ + public static int subArrayWithKDistinct(int[] A,int K){ + HashSet set = new HashSet(); + int left; + int right=0; + int count =0; + for(;right=0){ + set.add(A[left]); + if(set.size() ==K){ + count = count +1; + } else if(set.size()>K){ + break; + } + left --; + } + set.clear(); + } + return count; + + } + + /** + * 滑动窗口解法 + * 我们要维护两个滑动窗口以维护 + * 每一个滑动窗口能够计算窗口内有多少个不同的数字,并且支持像队列一样动态的增加 / 移除元素 + * + */ + + public int subarraysWithKDistinct(int[] A, int K) { + Window window1 = new Window(); + Window window2 = new Window(); + int ans = 0, left1 = 0, left2 = 0; + + for (int right = 0; right < A.length; ++right) { + int x = A[right]; + window1.add(x); + window2.add(x); + + while (window1.different() > K) + window1.remove(A[left1++]); + while (window2.different() >= K) + window2.remove(A[left2++]); + + ans += left2 - left1; + } + + return ans; + } + + + public static void main(String[] args) { + int[] A = {1,0,1,0,1}; + System.out.println("A = " + numSubArrayWithSum(A, 2)); + } + + + + +} + +class Window { + Map count; + int nonzero; + + Window() { + count = new HashMap(); + nonzero = 0; + } + + void add(int x) { + count.put(x, count.getOrDefault(x, 0) + 1); + if (count.get(x) == 1) + nonzero++; + } + + void remove(int x) { + count.put(x, count.get(x) - 1); + if (count.get(x) == 0) + nonzero--; + } + + int different() { + return nonzero; + } +} diff --git a/src/main/java/name/zicat/leetcode/microsoft/ListNode.java b/src/main/java/name/zicat/leetcode/microsoft/ListNode.java new file mode 100644 index 0000000..0ff8117 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/microsoft/ListNode.java @@ -0,0 +1,8 @@ +package name.zicat.leetcode.microsoft; + +public class ListNode { + public int val; + public ListNode pre; + public ListNode next; + public ListNode(int x) { val = x;} +} diff --git a/src/main/java/name/zicat/leetcode/microsoft/TestUtil.java b/src/main/java/name/zicat/leetcode/microsoft/TestUtil.java new file mode 100644 index 0000000..f150290 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/microsoft/TestUtil.java @@ -0,0 +1,77 @@ +package name.zicat.leetcode.microsoft; + +public class TestUtil { + + public static void main(String[] args) throws Exception{ + + ListNode head = new ListNode(1); + ListNode listNode2 = new ListNode(2); + ListNode listNode3 = new ListNode(3); + ListNode listNode4 = new ListNode(4); + ListNode listNode5 = new ListNode(5); + + head.next = listNode2; + + listNode2.pre = head; + listNode2.next = listNode3; + + listNode3.pre = listNode2; + listNode3.next = listNode4; + + listNode4.pre = listNode3; + listNode4.next = listNode5; + listNode5.pre = listNode4; + ListNode result = reverseMethod(head); + System.out.println("head = " + result); + + } + + public static ListNode reverseMethod(ListNode head){ + // + if(head == null){ + return head; + } + + ListNode first = head; + ListNode end = head; + + //end 指向最后一个 node + while (end.next!=null){ + + end = end.next; + } + + //从头进行遍历 + + //双数的情况 直接遍历到最后? + int count =0; + while(first.next!=null){ + count ++; + if(count %2 ==0) { + continue; + } + ListNode endBefore = end.pre; + ListNode temp = first.next; + first.next = end; + first.next.pre = first; + first.next.next = temp; + temp.pre = first.next; + + //并且end 往前 移动一位 + + end = endBefore; + // 链表是个单数的情况 ,退出 + if(first.next == end){ + break; + } + + //往下遍历 + first= first.next; + + } + + + return first; + + } +} diff --git a/src/main/java/name/zicat/leetcode/queue/Deques.java b/src/main/java/name/zicat/leetcode/queue/Deques.java new file mode 100644 index 0000000..2d4a171 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/Deques.java @@ -0,0 +1,12 @@ +package name.zicat.leetcode.queue; + +/** + * 1.两边都开口的队列 + * + * 2. 支持从队列前端删加 + * + * 3. 支持从队列后端删加 + */ +public class Deques { +} + diff --git a/src/main/java/name/zicat/leetcode/queue/FreqStack.java b/src/main/java/name/zicat/leetcode/queue/FreqStack.java new file mode 100644 index 0000000..749c508 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/FreqStack.java @@ -0,0 +1,62 @@ +package name.zicat.leetcode.queue; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + *实现 FreqStack,模拟类似栈的数据结构的操作的一个类。 + * + * FreqStack 有两个函数: + * + * push(int x),将整数 x 推入栈中。 + * pop(),它移除并返回栈中出现最频繁的元素。 + * 如果最频繁的元素不只一个,则移除并返回最接近栈顶的元素。 + * + */ +public class FreqStack { + /** + * 时间复杂度:对于 push 和 pop 操作,O(1)O(1)。 + * 空间复杂度:O(N)O(N),其中 N 为 FreqStack 中元素的数目。 + */ + + + + + /** + * key: 3,value :频率 + */ + Map freq; + /** + * key: 频率 ,value是 Stack 里面存 相同频率的 key的信息,靠近 栈顶的元素 相对更新一点 + */ + Map> group; + int maxfreq; + + public FreqStack(){ + freq = new HashMap<>(); + group = new HashMap<>(); + maxfreq =0; + } + + + public void push(int x ){ + int f = freq.getOrDefault(x,0) +1; + freq.put(x, f); + if(f > maxfreq){ + maxfreq = f; + } + + group.computeIfAbsent(f, z->new Stack<>()).push(x); + } + + public int pop(){ + int x = group.get(maxfreq).pop(); + freq.put(x, freq.get(x) -1); + if(group.get(maxfreq).size()==0){ + maxfreq--; + } + return x; + } + + } diff --git a/src/main/java/name/zicat/leetcode/queue/LockBoundedBlockingQueue.java b/src/main/java/name/zicat/leetcode/queue/LockBoundedBlockingQueue.java new file mode 100644 index 0000000..ec8f674 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/LockBoundedBlockingQueue.java @@ -0,0 +1,56 @@ +package name.zicat.leetcode.queue; + +import java.util.LinkedList; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +public class LockBoundedBlockingQueue { + + private final ReentrantLock lock = new ReentrantLock(); + //控制阻塞等待,代替传统的wait和notify实现线程间的协作 + private final Condition notFull = lock.newCondition(); + private final Condition notEmpty = lock.newCondition(); + private final LinkedList que = new LinkedList<>(); + private final int capacity; + + /** + * 每个Condition会有自己单独的等待队列,调用await方法,会放到对应的等待队列中。当调用某个Condition的signalAll/signal方法,则只会唤醒对应的等待队列中的线程。 + * 唤醒的粒度变小了,且更具针对性。如果只使用一个Condition的话,有些线程即使被唤醒并取得锁,其依然有可能并不满足条件而浪费了机会,产生时间损耗,相当于notEmpty的Condition唤醒了 + * notFull的队列线程。 + * @param capacity + */ + + public LockBoundedBlockingQueue(int capacity){ + this.capacity = capacity; + } + + public void enqueue(int element) throws InterruptedException { + lock.lock(); + try { + while (que.size() == capacity) { + //当前arraylist是满的,会放到对应的等待队列中, + notFull.await(); + } + que.add(element); + //已经放入数据 不是空的了,唤醒队列中的线程 + notEmpty.signal(); + } finally { + lock.unlock(); + } + } + + public int dequeue() throws InterruptedException { + lock.lock(); + + while(que.size() ==0){ + //arraylist是空的,线程放到对应的等待队列中 + notEmpty.await(); + } + int val = que.poll(); + //已经取出数据,不是满的了,唤醒 队列中的线程 + notFull.signal(); + return val; + } + + +} diff --git a/src/main/java/name/zicat/leetcode/queue/MyCircularQueue.java b/src/main/java/name/zicat/leetcode/queue/MyCircularQueue.java new file mode 100644 index 0000000..1cba096 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/MyCircularQueue.java @@ -0,0 +1,63 @@ +package name.zicat.leetcode.queue; + +/** + * 需要看一下 + */ +public class MyCircularQueue { + private int[] queue; + private int headIndex; + private int count; + private int capacity; + + /** Initialize your data structure here. Set the size of the queue to be k. */ + public MyCircularQueue(int k) { + this.capacity = k; + this.queue = new int[k]; + this.headIndex = 0; + this.count = 0; + } + + /** Insert an element into the circular queue. Return true if the operation is successful. */ + public boolean enQueue(int value) { + if (this.count == this.capacity) + return false; + this.queue[(this.headIndex + this.count) % this.capacity] = value; + this.count += 1; + return true; + } + + /** Delete an element from the circular queue. Return true if the operation is successful. */ + public boolean deQueue() { + if (this.count == 0) + return false; + this.headIndex = (this.headIndex + 1) % this.capacity; + this.count -= 1; + return true; + } + + /** Get the front item from the queue. */ + public int Front() { + if (this.count == 0) + return -1; + return this.queue[this.headIndex]; + } + + /** Get the last item from the queue. */ + public int Rear() { + if (this.count == 0) + return -1; + int tailIndex = (this.headIndex + this.count - 1) % this.capacity; + return this.queue[tailIndex]; + } + + /** Checks whether the circular queue is empty or not. */ + public boolean isEmpty() { + return (this.count == 0); + } + + /** Checks whether the circular queue is full or not. */ + public boolean isFull() { + return (this.count == this.capacity); + } + +} diff --git a/src/main/java/name/zicat/leetcode/queue/PriorityQueueTest.java b/src/main/java/name/zicat/leetcode/queue/PriorityQueueTest.java new file mode 100644 index 0000000..e8fbd85 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/PriorityQueueTest.java @@ -0,0 +1,259 @@ +package name.zicat.leetcode.queue; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * 优先队列 + * + * 1.元素被赋予优先级,优先级高的元素先被访问 + * + * 2.int 类型弄人 最大值优先级高 + * + * 3.可以自定义优先级 + * + * 4.基于堆实现的优先队列的插入复杂度是O(logN) + * + * 堆是个 二叉堆 是一个完全二叉树 + * + * 根节点总是大于左右子节点(大顶堆), 或者是小于左右子节点(小顶堆) + * + * 保证每次取出的元素都是队列中权值最小的,这里涉及到了大小关系,元素大小的评判可以通过元素自身的自然顺序(使用默认的比较器),也可以通过构造时传入的比较器 + * + * leftNo = parentNo*2+1 + * + * rightNo = parentNo*2+2 + * + * parentNo = (nodeNo-1)/2 + */ +public class PriorityQueueTest { + + /** + * 默认初始容量 + */ + private static final int DEFAULT_INITIAL_CAPACITY = 11; + + + /** + * 维护一个队列,因为基于二叉堆来实现优先队列,queue[i] 的子节点为 queue[2*i +1]/queue[2*i +2] + */ + transient Object[] queue; + + /** + * 优先级队列中的 数据条数 + */ + private int size =0; + + /** + * + * @param data + */ + + private final Comparator< ? super T> comparator; + + + /** + * 被修改的次数 + */ + transient int modCount =0; + + /** + * The maximum size of array to allocate. + * Some VMs reserve some header words in an array. + * Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + public PriorityQueueTest(Comparator comparator){ + this.queue = new Object[DEFAULT_INITIAL_CAPACITY]; + this.comparator = comparator; + } + + public PriorityQueueTest(){ + this.comparator = null; + } + + + public boolean add(T data) { + if(data ==null) + throw new NullPointerException(); + modCount ++; + int i = size; + if(i>=queue.length) + grow(i+1); + size = i+1; + if( i ==0) + queue[0] = data; + else + siftUp(i,data); + return true; + } + + /** + * Inserts item x at position k, maintaining heap invariant by + * promoting x up the tree until it is greater than or equal to + * its parent, or is the root. + * + * To simplify and speed up coercions and comparisons. the + * Comparable and Comparator versions are separated into different + * methods that are otherwise identical. (Similarly for siftDown.) + * + * @param k the position to fill + * @param x the item to insert + */ + private void siftUp(int k, T x) { + siftUpUsingComparator(k, x); + } + + private void siftUpUsingComparator(int k, T x) { + + /** + * >>>表示不带符号向右移动二进制数,移动后前面统统补0;两个箭头表示带符号移动, + * + * 没有<<<这种运算符,因为左移都是补零,没有正负数的区别。 + * + * 如 -12 的二进制为:1111 1111 1111 1111 1111 1111 1111 0100; + * + * -12 >> 3 即带符号右移3位,结果是:1111 1111 1111 1111 1111 1111 1111 1110,十进制为: -2; + * + * -12 >>> 3 就是右移三位,前面补零,为:0001 1111 1111 1111 1111 1111 1111 1110,十进制为:536870910 + * + * 插入的数据 可能影响 大顶堆或者小顶堆,需要和 parent进行一个交换 保证顶点是最小的 + */ + while (k > 0) { + //先找到其parent的编号 + int parent = (k - 1) >>> 1; + Object e = queue[parent]; + if (comparator.compare(x, (T) e) >= 0) + break; + queue[k] = e; + k = parent; + } + queue[k] = x; + + } + + private void grow(int minCapacity) { + int oldCapacity = queue.length; + // Double size if small; else grow by 50% + int newCapacity = oldCapacity + ((oldCapacity < 64) ? + (oldCapacity + 2) : + (oldCapacity >> 1)); + // overflow-conscious code + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + queue = Arrays.copyOf(queue, newCapacity); + } + + private static int hugeCapacity(int minCapacity) { + if (minCapacity < 0) // overflow + throw new OutOfMemoryError(); + return (minCapacity > MAX_ARRAY_SIZE) ? + Integer.MAX_VALUE : + MAX_ARRAY_SIZE; + } + + public T peek(){ + + return (size == 0) ? null : (T) queue[0]; + } + + public T poll(){ + if (size == 0) + return null; + int s = --size; + modCount++; + T result = (T) queue[0]; + T x = (T) queue[s]; + queue[s] = null; + if (s != 0) + siftDown(0, x); + return result; + } + + public int size(){ + return size; + } + + /** + * Removes a single instance of the specified element from this queue, + * if it is present. More formally, removes an element {@code e} such + * that {@code o.equals(e)}, if this queue contains one or more such + * elements. Returns {@code true} if and only if this queue contained + * the specified element (or equivalently, if this queue changed as a + * result of the call). + * + * @param o element to be removed from this queue, if present + * @return {@code true} if this queue changed as a result of the call + */ + public boolean remove(Object o) { + int i = indexOf(o); + if (i == -1) + return false; + else { + removeAt(i); + return true; + } + } + + private int indexOf(Object o) { + if (o != null) { + for (int i = 0; i < size; i++) + if (o.equals(queue[i])) + return i; + } + return -1; + } + + /** + * Removes the ith element from queue. + * + * Normally this method leaves the elements at up to i-1, + * inclusive, untouched. Under these circumstances, it returns + * null. Occasionally, in order to maintain the heap invariant, + * it must swap a later element of the list with one earlier than + * i. Under these circumstances, this method returns the element + * that was previously at the end of the list and is now at some + * position before i. This fact is used by iterator.remove so as to + * avoid missing traversing elements. + */ + @SuppressWarnings("unchecked") + private T removeAt(int i) { + // assert i >= 0 && i < size; + modCount++; + int s = --size; + if (s == i) // removed last element + queue[i] = null; + else { + T moved = (T) queue[s]; + queue[s] = null; + siftDown(i, moved); + if (queue[i] == moved) { + siftUp(i, moved); + if (queue[i] != moved) + return moved; + } + } + return null; + } + + private void siftDown(int k, T x) { + int half = size >>> 1; + while (k < half) { + int child = (k << 1) + 1; + Object c = queue[child]; + int right = child + 1; + if (right < size && + comparator.compare((T) c, (T) queue[right]) > 0) + c = queue[child = right]; + if (comparator.compare(x, (T) c) <= 0) + break; + queue[k] = c; + k = child; + } + queue[k] = x; + } + + +} diff --git a/src/main/java/name/zicat/leetcode/queue/Queue.java b/src/main/java/name/zicat/leetcode/queue/Queue.java new file mode 100644 index 0000000..5b3cc94 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/Queue.java @@ -0,0 +1,61 @@ +package name.zicat.leetcode.queue; + +/** + * 先进先出的数据结构 + * + * 从 尾部 追加,头部 取出 + */ +public class Queue { + + private static class QueueNode { + private T data; + private QueueNode next; + + public QueueNode(T data){this.data = data;} + } + + private QueueNode first; + private QueueNode last; + + /** + * 从队首取出元素 + * @return + */ + public T remove(){ + if(first == null) throw new RuntimeException(); + + T value = first.data; + first = first.next; + + /** + * 如果只有一个元素,取出来就空了,需要把 last也设置成 null + */ + if(first == null) last =null; + return value; + } + + /** + * 加元素 + */ + + public void add(T item) { + QueueNode node = new QueueNode(item); + if(last!=null) last.next=node; + last= node; + if(first==null) first =last; + + } + + /** + * 从队首取元素,不会 remove 元素 + * @return + */ + public T peek(){ + if(first == null) throw new RuntimeException(); + return first.data; + } + + public boolean isEmpty(){ + return first == null; + } +} diff --git a/src/main/java/name/zicat/leetcode/queue/QueueUtil.java b/src/main/java/name/zicat/leetcode/queue/QueueUtil.java new file mode 100644 index 0000000..5bca0ef --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/QueueUtil.java @@ -0,0 +1,176 @@ +package name.zicat.leetcode.queue; + +import name.zicat.leetcode.array.ListNode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.PriorityQueue; + +public class QueueUtil { + /** + * 239.滑动窗口最大值 (hard) + * + * 滑动窗口 + * + * 双端队列 + * Deque 中 队首 永远都是 滑动窗口的最大值 + * [1,3,-1,-3,5,3] + * [1] Deque 1 Result + * [1,3] Deque 3 Result + * [1,3,-1] Deque 3 -1 Result 3(Deque) + * [1,3,-1,-3] Deque 3 -1 -3 Result 3 3 + * [1,3,-1,-3 5] Deque 5 (3,-1,-3出队并且滑动窗口的size大于k) Result 3 3 5 + * [1,3,-1,-3 5 3] Deque 5 3 Result 3 3 5 5 + * + */ + + public int[] maxSlidingWindow(int[] nums,int k){ + if(nums==null || nums.length ==0){ + return new int[0]; + } + int[] result = new int[nums.length-k+1]; + /** + * 存放 最大值的索引 + */ + LinkedList deque = new LinkedList(); + for(int i=0;ik的情况 + if(!deque.isEmpty() && deque.peekFirst()==i-k){ + //需要队首移除 元素 + deque.poll(); + } + /** + * 新入栈的元素大于 队尾的元素的话,把元素移除,保证队首是最大值 + */ + while(!deque.isEmpty()&&nums[deque.peekLast()]=k){ + result[i+1-k] = nums[deque.peek()]; + } + } + return result; + } + /** + * 23.合并k个排序链表 + * 用优先队列 优先队列需要重新实现一遍 + * + */ + + public ListNode mergeKList(ListNode[] lists){ + // 边界情况 + + if(lists==null|| lists.length==0){ + return null; + } + + PriorityQueueTest queue = new PriorityQueueTest<>(); + //定义带头人 + ListNode fakeHead = new ListNode(0); + ListNode p = fakeHead; + //遍历排好序的lists 的首节点,放在队列中 + for(ListNode list:lists){ + if(list!=null){ + queue.add(list); + } + } + // + while(queue.size() >0){ + ListNode n = queue.poll(); + p.next = n; + //最小节点的下一个节点入队 + if(n.next!=null){ + queue.add(n.next); + } + } + + return fakeHead.next; + + } + + /** + * 时间复杂度:考虑优先队列中的元素不超过 kk 个,那么插入和删除的时间代价为 O(\log k)O(logk),这里最多有 knkn 个点,对于每个点都被插入删除各一次,故总的时间代价即渐进时间复杂度为 O(kn \times \log k)O(kn×logk)。 + * 空间复杂度:这里用了优先队列,优先队列中的元素不超过 kk 个,故渐进空间复杂度为 O(k)O(k) + * + * @param lists + * @return + */ + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) return null; + PriorityQueue queue = new PriorityQueue(lists.length, new Comparator() { + @Override + public int compare(ListNode o1, ListNode o2) { + if (o1.val < o2.val) return -1; + else if (o1.val == o2.val) return 0; + else return 1; + } + }); + ListNode dummy = new ListNode(0); + ListNode p = dummy; + for (ListNode node : lists) { + if (node != null) queue.add(node); + } + while (!queue.isEmpty()) { + p.next = queue.poll(); + p = p.next; + if (p.next != null) queue.add(p.next); + } + return dummy.next; + + } + + /** + * 621.任务调度器 + * + * 在选择每一轮中的任务时,我们也可以用优先队列(堆)来代替排序。在一开始,我们把所有的任务加入到优先队列中。在每一轮,我们从优先队列中选择最多 n + 1 个任务,把它们的数量减去 1,再放回堆中(如果数量不为 0),直到堆为空。 + * 输入:tasks = ["A","A","A","B","B","B"], n = 2 + * 输出:8 + * 解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B. + * 在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。 + * + */ + + public static int leastInterval(char[] tasks,int n) { + int[] map = new int[26]; + for (char c: tasks) + map[c - 'A']++; + PriorityQueue < Integer > queue = new PriorityQueue <> (26, Collections.reverseOrder()); + for (int f: map) { + if (f > 0) + queue.add(f); + } + int time = 0; + while (!queue.isEmpty()) { + int i = 0; + List< Integer > temp = new ArrayList< >(); + while (i <= n) { + if (!queue.isEmpty()) { + if (queue.peek() > 1) + temp.add(queue.poll() - 1); + else + queue.poll(); + } + time++; + if (queue.isEmpty() && temp.size() == 0) + break; + i++; + } + for (int l: temp) + queue.add(l); + } + return time; + + } + + + + + +} diff --git a/src/main/java/name/zicat/leetcode/queue/SyncBoundedBlockingQueue.java b/src/main/java/name/zicat/leetcode/queue/SyncBoundedBlockingQueue.java new file mode 100644 index 0000000..916ee54 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/SyncBoundedBlockingQueue.java @@ -0,0 +1,62 @@ +package name.zicat.leetcode.queue; + +import java.util.LinkedList; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +public class SyncBoundedBlockingQueue { + + private final ReentrantLock lock = new ReentrantLock(); + //控制阻塞等待 + private final Condition notFull = lock.newCondition(); + private final Condition notEmpty = lock.newCondition(); + private final LinkedList que = new LinkedList<>(); + private final int capacity; + + public SyncBoundedBlockingQueue(int capacity){ + this.capacity = capacity; + } + + public void enqueue(int element) throws InterruptedException { + lock.lock(); + try { + while (que.size() == capacity) { + + /** + * 已经满的情况下,调用wait释放锁,当前线程进入该对象的等待队列, + * 等待被其他线程环形,被唤醒并且获得锁后,线程恢复运行 + */ + wait(); + } + que.add(element); + /** + * 唤醒其他等待该对象锁的线程 + */ + notifyAll(); + } finally { + lock.unlock(); + } + } + + public int dequeue() throws InterruptedException { + lock.lock(); + int val; + try { + while (que.size() == 0) { + /** + * 调用wait 释放锁,当前线程进入当前对象锁的 + * 等待队列,等待被其他线程唤醒,被唤醒并且获得锁之后,线程恢复运行 + */ + wait(); + } + val = que.poll(); + } finally{ + lock.unlock(); + } + //唤醒其他线程 + notifyAll(); + return val; + } + + +} diff --git a/src/main/java/name/zicat/leetcode/queue/TestUtil.java b/src/main/java/name/zicat/leetcode/queue/TestUtil.java new file mode 100644 index 0000000..ad751b8 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/queue/TestUtil.java @@ -0,0 +1,23 @@ +package name.zicat.leetcode.queue; + +public class TestUtil { + + public static int numWays(int n, int k) { + if (n <= 2) { + return k*k; + } else { + int[] m = new int[n]; + m[0] = k; + m[1] = k*k; + for (int i = 2; i < n; i++) { + m[i] = m[i-1] * (k-1) + m[i-2] * (k-1); + } + return m[n-1]; + } + } + + + public static void main(String[] args) { + System.out.println("args = " + numWays(6,2)); + } +} diff --git a/src/main/java/name/zicat/leetcode/recursion/Recursion.java b/src/main/java/name/zicat/leetcode/recursion/Recursion.java new file mode 100644 index 0000000..ef6c31e --- /dev/null +++ b/src/main/java/name/zicat/leetcode/recursion/Recursion.java @@ -0,0 +1,60 @@ +package name.zicat.leetcode.recursion; + +import name.zicat.leetcode.tree.TreeNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * + */ +public class Recursion { + + /** + * 894.所有可能的满二叉树 + * + * 满二叉树是一类二叉树,其中每个结点恰好有0或2个子结点 + * n 如果是 偶数 是不可能为 二叉树的 + * + * 依次递归 左子树和右子树,判断 左右子树 是不是 单数 + * + */ + + public List allPossibleFBT(int N){ + List result = new ArrayList<>(); + if(N%2 == 0){ + return result; + } + //base case + if(N ==1){ + TreeNode node = new TreeNode(0); + result.add(node); + return result; + } + //去掉 root + N-=1; + for(int i=1;i left = allPossibleFBT(i); + List right = allPossibleFBT(N-i); + /** + * 把左右所有可能的组合和 root相结合 + */ + for(TreeNode lnode:left){ + for(TreeNode rnode:right) { + TreeNode root = new TreeNode(0); + root.left = lnode; + root.right = rnode; + //整体解加在 root中 再返回 + result.add(root); + } + } + } + return result; + + } +} diff --git a/src/main/java/name/zicat/leetcode/recursion/RecursionUtil.java b/src/main/java/name/zicat/leetcode/recursion/RecursionUtil.java new file mode 100644 index 0000000..507dfba --- /dev/null +++ b/src/main/java/name/zicat/leetcode/recursion/RecursionUtil.java @@ -0,0 +1,290 @@ +package name.zicat.leetcode.recursion; + +import name.zicat.leetcode.tree.TreeNode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * @author jessica + */ +public class RecursionUtil { + /** + * 递归模板 + * + * 写递归的 要点 leetcode 国际站 most votes看一遍 + * + * + * public void recur(int level,int param){ + * //terminator + * if(level > MAX_LEVEL){ + * //process result + * return; + * } + * //process current logic + * process(level,param); + * + * //drill down + * recur(level,level+1,newParam) + * + * + * //restore current status + * + * } + * + */ + + /** + * 递归 找重复性 + * + */ + + /** + * 爬楼梯 leetcode 70 + */ + public static void climbStairs(int n){ + + } + + public static void main(String[] args) { + generateParenthesis(3); + } + + + /** + * 括号生成 leetcode 22 + * 输入:n = 3 + * 输出:[ + * "((()))", + * "(()())", + * "(())()", + * "()(())", + * "()()()" + * ] + */ + public static List generateParenthesis(int n){ + //_generate(0,2 * n,""); + + _generate(0,0,2* n,""); + + return null; + + } + + private static void _generate(int level, int max, String s) { + //terminator + if(level >=max){ + //filter s is invalid 只能 有 n个 左括号 ,right 必须出现 左括号的后面 + + System.out.println("s = " + s); + return; + } + + //process + + String s1= s + "("; + String s2= s + ")"; + + //drill down + + _generate(level+1,max,s1); + _generate(level+1,max,s2); + + //reverse states + + + } + + private static void _generate(int left, int right,int n, String s) { + //terminator + if(left == n && right == n){ + //filter s is invalid 只能 有 n个 左括号 ,right 必须出现 左括号的后面 + + System.out.println("s = " + s); + return; + } + + //process + + String s1= s + "("; + String s2= s + ")"; + + //drill down + if(left < n) { + _generate(left + 1, right,n, s1); + } + if(left > right) { + _generate(left, right + 1, n, s2); + } + + + //reverse states + + + } + + /** + * 斐波那契数列 非递归法 + * @param n + * @return + */ + public static int fib(int n){ + int first =0; + int second =1; + while (n -->0){ + int temp = first + second; + first = second; + + second = temp; + } + + return first; + + } + + /** + * 140. 单词拆分 + * 记忆化搜索 + * 给定一个非空字符串 s和一个包含非空单词列表的字典 + * + * s ="catsanddog" + * + * wordDict = ["cat","cats","and","sand","dog"] + * + * wordDict = ["cats and dog","cat sand dog"] + * + * wordBreak("catsanddog") = + * "cat" + "" + wordBreak("sanddog") -> "cat sand" + "" +wordBreak("dog"); + * 或者 "cats" + "" + wordBreak("anddog") -> "cats and" + "" + wordBreak("dog"); + * + * 注意 "dog" 已经被检测过,不用重复计算,我们可以把它的结果存起来 + * 记忆化搜索, 中间结果保存在 数据结构中,遇到重复的不需要再计算 + + */ + + public List wordBreak(String s,List wordDict){ + return helper(s, wordDict, new HashMap>()); + } + + //返回所有s对应的满足条件的结果 + // s 为子串 比如 sanddog + private List helper(String s, List wordDict, HashMap> prevRes) { + //记忆化搜索的精髓,不进行重复计算 + if(prevRes.containsKey(s)){ + return prevRes.get(s); + } + + ArrayList result = new ArrayList<>(); + //容易错,不要忘记base case 定义基础解 + if(s.length() ==0){ + result.add(""); + return result; + } + + for(String word:wordDict){ + if(s.startsWith(word)){ + //递归调用剩下的部分 + List subRes = helper(s.substring(word.length()),wordDict,prevRes); + //append 到后面 + for(String sub:subRes){ + result.add(word + (sub.isEmpty()?"": " ") + sub); + } + } + } + prevRes.put(s,result); + return result; + } + /** + * 687.最长同值路径 + * 给定一个二叉树 找到最长的路径,这个路径每个节点具有相同的值 + * 递推方程 : helper(root) 表示以root为顶点的最大同值边长 + * 基础值: root ==null,返回 0 + * 每次function call 记录全局最大路径 + * + * 时间 O(n) n= number of tree nodes + */ + int ans =0; + public int longestUnivaluePath(TreeNode root){ + if(root ==null){ + return 0; + } + helperTree(root); + return ans; + } + + private int helperTree(TreeNode root) { + //terminal + if(root ==null){ + return 0; + } + int left = helperTree(root.left); + int right = helperTree(root.right); + int pleft =0; + int pright =0; + //左子树的最大边长+1 + if(root.left!=null&& root.val==root.left.val) pleft =left+1; + //右子树的最大边长+1 + if(root.right!=null && root.val==root.right.val) pright =right +1; + // + this.ans = Math.max(this.ans,pleft+pright); + //左右中的最大值 + return Math.max(pleft,pright); + + } + + /** + * 783 二叉搜索树 结点最小距离 + * + * + */ + Integer prev,ans1; + public int minDiffInBST(TreeNode root){ + ans = Integer.MAX_VALUE; + prev = null; + inOrder(root); + return ans1; + } + //一定是升序的 + public void inOrder(TreeNode node){ + if(node==null) return; + //先处理左子树 + inOrder(node.left); + //处理当前的节点 + if(prev!=null){ + ans1 = Math.min(ans,node.val-prev); + } + prev =node.val; + //再处理 右子树 + inOrder(node.right); + } + + /** + * 1137 第N个 泰波那契数 + * n=4 + * 输出4 + * T_3 =0 +1 +1 =2 + * T_4 = 1+ 1+ 2 =4 + * + */ + HashMap cache = new HashMap<>(); + public int tribonacci(int n){ + if(cache.containsKey(n)){ + return cache.get(n); + } + if(n ==0){ + cache.put(n,0); + return 0; + } + if(n==1 || n==2){ + cache.put(n,1); + return 1; + } + int result = tribonacci(n-1) + tribonacci(n-2) + tribonacci(n-3); + cache.put(n,result); + return result; + + } + + +} diff --git a/src/main/java/name/zicat/leetcode/search/BinarySearch.java b/src/main/java/name/zicat/leetcode/search/BinarySearch.java new file mode 100644 index 0000000..17b5928 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/search/BinarySearch.java @@ -0,0 +1,97 @@ +package name.zicat.leetcode.search; + +/** + * 二分搜索 + * + * 1.针对排好序的数据集合 + * 跟中间元素比较 + * + * 通过比较,把查找区缩小为之前的一半 + * + * 直到找到元素 + * + * 区间为0d的时候查找失败 + * + * 时间复杂度为 O(logn) 查找的复杂度是 二叉树的高度 + */ +public class BinarySearch { + + /** + * 二分法的查找 + * + * 输入是个排好序的数组 + * + * 返回 下标 + */ + + public int binarySearch(int[] input,int low,int high,int value){ + if(high >=low){ + /** + * 拿出中间值 + */ + int mid = high+(high-low)/2; + + if(input[mid] ==value) return mid; + + /** + * 在 左半部分查找 + */ + if(input[mid] > value){ + return binarySearch(input,low,mid -1,value); + } + + return binarySearch(input,mid+1,high,value); + } + return -1; + } + + /** + * 162.寻找峰值 + * + * 输入 nums = [1,2,3,1] + * 3 是 峰值 返回 索引 2 + * + * 输出 [1,2,1,3,5,6,4] + * + * 2, 6 是峰值 + * + * 峰值的左边 是 递增的 + * + * 峰值的右边 是 递减的 + * + */ + + public int findPeakElement(int[] nums){ + //查找边界条件 + if(nums == null || nums.length ==0|| nums.length ==1){ + return 0; + } + + //起始点 + int start =0; + int end = nums.length -1; + + while( start+1 nums[end]){ + return start; + } else { + return end; + } + } + + + + +} diff --git a/src/main/java/name/zicat/leetcode/search/SearchUtil.java b/src/main/java/name/zicat/leetcode/search/SearchUtil.java new file mode 100644 index 0000000..bf69224 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/search/SearchUtil.java @@ -0,0 +1,29 @@ +package name.zicat.leetcode.search; + +public class SearchUtil { + + public static void main(String[] args) { + int[] nums ={0,1,2,3,4,5}; + int target = 7; + System.out.println("target = " + binarySearchInsert(nums,target));; + } + + /** + * 二分法,寻找 元素 插入的位置,时间复杂度 O(logN) + */ + + public static int binarySearchInsert(int[] nums,int target) { + if(nums == null) return 0; + int start =0; + int end = nums.length-1; + while(start<=end){ + int mid = (start+end)/2; + if(nums[mid] == target) return mid; + else if(nums[mid] < target) start = mid +1; + else end = mid-1; + + } + + return start; + } +} diff --git a/src/main/java/name/zicat/leetcode/sort/HeapSort.java b/src/main/java/name/zicat/leetcode/sort/HeapSort.java new file mode 100644 index 0000000..b23afc2 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/sort/HeapSort.java @@ -0,0 +1,64 @@ +package name.zicat.leetcode.sort; + + +import java.util.Arrays; + +/** + * 构建初始堆+交换堆顶元素和末尾元素并重建堆 + */ +public class HeapSort { + + + + public static void main(String[] args) { + + int[] arr = new int[]{10,3,4,8,9,1,2,6,7}; + int length = arr.length; + + //build Initial max heap + for (int i = length/2-1; i >=0; i--) { + buildMaxHeap(i, arr,length); + } + + System.out.println(Arrays.toString(arr)); + + // swap last node with root node , and rebuild MaxHeap + for (int i = arr.length-1; i >=0; i--) { + swap(arr,0,i); + length--; + buildMaxHeap(0,arr,length); + } + System.out.println(Arrays.toString(arr)); + } + + + + //buildMaxHeap + public static void buildMaxHeap(int i,int[] arr,int length){ + int temp = arr[i]; + for (int j = i*2+1; j < length; j=j*2+1) { + //if right child > left child, + if(j+1arr[j]){ + j++; + } + //if child > current node + if(arr[j]>temp){ + arr[i] = arr[j]; + i = j; + }else{ + break; + } + } + //put temp + arr[i] = temp; + } + + //swap + public static void swap(int[] arr,int i,int j){ + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + + +} diff --git a/src/main/java/name/zicat/leetcode/sort/Sort.java b/src/main/java/name/zicat/leetcode/sort/Sort.java new file mode 100644 index 0000000..d264353 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/sort/Sort.java @@ -0,0 +1,224 @@ +package name.zicat.leetcode.sort; + +import java.util.Arrays; + +/** + * 冒泡排序 + * 比较相邻两个元素,比较大小,不满足大小关系的情况下互换 + * + * 一个冒泡可以让一个元素找到相应的位置 + * + * n个元素的排序,需要冒泡n次 + * + * 平均最坏 时间复杂度O(n^2): 4,3,2,1 + * + * 选择排序 + * 遍历所有的元素,选择最小的放在第一个位置 + * 遍历未排序所有元素,选择最小的放第2个位置 + * 重复 n次 + * 平均,最坏时间复杂度O(n^2):4,3,2,1 + * + * 归并排序 + * 把数组分为两半,分别排序 + * 然后归并在一起 + * 按照同样的方法将分成两个部分 分别排序再合并 + * + * 平均,最坏时间复杂度O(nlogn):4 3 2 1 + * 从递归的角度来分析的话:第一步:遍历所有的数,两两比较进行排序,这就完成了一趟操作。它的时间复杂度为n.. + 第二步:递归完成上述操作,它的时间复杂度为logn,二分递归,所以总的就是nlogn它是稳定的算法 + * + * 12 8 5 9 + * 先平分成两组 + * [12,8] [5,9] + * + * [8,12] [5,9] + * + * 再合并 + * [5,8,9,12] + * + * 快速排序 + * 随意挑选一个元素 + * 小于此元素的排在左边,大于此元素的排在右边 + * 对于左右两部分重复此操作 + * + * 因为不是报纸挑选的元素是中值,最坏的时间复杂度是O(N^2) + * + * 平均时间复杂度 O(nlogn),正好取到的元素是 中值 就是归并,最大的数的话就是 冒泡排序 + * + * 11 12 4 3 5 9 8 取8 + * + * 4 3 5 [8] 11 12 9 取 4 和11 + * + * 3 [4] 5 [8] 9 [11] 12 + * + * + * 堆排序 + * + * 树形的选择排序 + * + * 整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成 + * + * 堆排序的基本思想是:将待排序序列构造成一个大顶堆, + * 此时,整个序列的最大值就是堆顶的根节点。 + * 将其与末尾元素进行交换,此时末尾就为最大值。 + * 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了 + * + * nlogn。所以堆排序时间复杂度最好和最坏情况下都是O(nlogn)级。 + * + * + * + */ +public class Sort { + + public static void main(String[] args) { + int[] arr = new int[]{4,6,8,5,9}; + int length = arr.length; + //从最后一个非叶节点开始构建大顶堆 + for (int i = arr.length/2-1; i >=0; i--) { + maximumHeap(i,arr,length); + } + //从最小的叶子节点开始与根节点进行交换并重新构建大顶堆 + for (int i = arr.length-1; i >=0; i--) { +// System.out.println(Arrays.toString(arr)); + swap(arr,0,i); + length--; + maximumHeap(0,arr,length); + } + System.out.println(Arrays.toString(arr)); + } + + /** + * 归并排序 + */ + public void mergeSort(int[] input){ + devide(input,0,input.length-1); + + } + + private void devide(int[] input, int low, int high) { + if(low < high){ + int mid =(low+high)/2; + devide(input,low,mid);//left half + devide(input,mid+1,high); + merge(input,low,mid,high); + } + + } + + private void merge(int[] input, int low, int mid, int high) { + int[] helper = new int[input.length]; + for(int i= low;i<=high;i++){ + //对input进行修改之前 进行copy + helper[i] = input[i]; + } + /** + * 分治完以后 + */ + int left =low; + int right = mid +1; + int i =low; + /** + * 对比两个头元素,最小的 放到 input[i]的位置 + */ + while(left<=mid && right<=high){ + if(helper[left] <=helper[right]){ + input[i++] = helper[left++]; + //然后 i再++,在移动 left和 right的位置 + } else { + input[i++] = helper[right++]; + } + } + + /** + * left和 right 可能没有走到最后的元素 + * right部分不需要copy,因为 helper就是copy的input 对应位置上的元素就是相等的 + */ + if(leftpivot){ + /** + * 已经在正确的位置 --即可 + */ + right --; + } + if(left <=right){ + /** + * 如果不是 进行交换 + */ + int temp = input[left]; + input[left] =input[right]; + input[right] = temp; + left ++; + right --; + } + } + return left; + } + + + //构建大顶堆 + public static void maximumHeap(int i,int[] arr,int length){ + int temp = arr[i]; + for (int j = i*2+1; j < length; j=j*2+1) { + //如果右孩子大于做孩子,则指向右孩子 + if(j+1arr[j]){ + j++; + } + //如果最大的孩子大于当前节点,则将大孩子赋给当前节点,修改当前节点为其大孩子节点,再向下走。 + if(arr[j]>temp){ + arr[i] = arr[j]; + i = j; + }else{ + break; + } + } + //将temp放到最终位置 + arr[i] = temp; + } + //交换 + public static void swap(int[] arr,int i,int j){ + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + + + + +} diff --git a/src/main/java/name/zicat/leetcode/sort/SortUtil.java b/src/main/java/name/zicat/leetcode/sort/SortUtil.java new file mode 100644 index 0000000..d41b7b9 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/sort/SortUtil.java @@ -0,0 +1,440 @@ +package name.zicat.leetcode.sort; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +public class SortUtil { + /** + * 215 数组中的第K个最大的元素 + * 在未排序的数组中找到 第k个最大的元素 + * + * 输入 [3,2,1,5,6,4] k=2 + * + * 输出 5 + * + * 输入 [3,2,1,2,4,5,5,6] k=4 + * + * 输出 4 + * + * 算法 + * 长度为 7 + * + * k =4,转化为找 第4个最小元素 (7-4+1) + * + * left->11 12 4 3 5 9 8<-right + * + * left->11 12 4 3 5<-right 9 8 [11,5]交换 + * + * 5 left->12 4 3<-right 11 9 8 [12,3]交换 + * + * 5 3 4 left->12<-right 11 9 8 left和 right重叠了 停止分割 12和8交换位置 + * + * 5 3 4 8 11 9 12 k= left+1=4 ,所以8就是答案 + * + * 如果不是 往左边 或者 右边继续找 + * + * + * + * 用 快排 最优 nlogn 选择的数正好是中间的值 最坏的是 n^2 选择的数不是 最小就是最大 + * + * pivot =8 + * nums[left] <8 往中间移动left + * nums[right] >=8 往中间移动 right + */ + + public int findKthLarges(int[] nums,int k){ + //[3,2,1,5,6,4] + //k =2 + //nums.length -k +1 =5 + if(k<1 || nums == null) { + return 0; + } + return getKth(nums.length -k +1,nums,0,nums.length -1); + + } + + /** + * 第K个最小的元素 + * @param k + * @param nums + * @param start + * @param end + * @return + */ + public int getKth(int k,int[] nums, int start, int end){ + int pivot = nums[end]; + int left = start; + int right = end; + + while(true){ + while( nums[left]=pivot && left < right){ + right --; + } + if(left == right){ + break; + } + swap1(nums,left,right); + } + + swap1(nums,left,end); + if(k == left +1){ + return pivot; + } else if( k=nums[2] < + * + * 奇数 下标小于等于前一个数, + * odd 要>=前一个数 + * even 要 <=前一个数 + * 不符合条件的 和 前一个数交换即可,不会影响前面的排序顺序 + * 3,5, 2, 1, 6, 4 + * odd even odd even odd + * + */ + + public void wiggleSort(int[] nums) { + for (int i = 1; i < nums.length; i++) { + // 需要交换的情况:奇数时nums[i] < nums[i - 1]或偶数时nums[i] > nums[i - 1] + if ((i % 2 == 1 && nums[i] < nums[i-1]) || + (i % 2 == 0 && nums[i] > nums[i-1])) { + int tmp = nums[i-1]; + nums[i-1] = nums[i]; + nums[i] = tmp; + } + } + } + /** + * 324 摆动排序 2 + * 给定一个无序数组,将他重新排列 + * nums[0] nums[2] mid){ + ans[r] = nums[i]; + r +=2; + } + } + } else { + l =0; + r =nums.length -2; + for(int i=0;imid){ + ans[r] =nums[i]; + r-=2; + } + } + } + for(int i=0;i0 ) r--; + points[l] = points[r]; + while(l < r && dist(points[l],pivot)<=0) l++; + points[r] =points[l]; + } + points[l] = pivot; + return l; + } + + /** + * 原点到原点的距离 + * 是根号 x^2 + y^2 + * 那判断 哪个点 离原点近 相减 >0即可 + * @param point + * @param pivot + * @return + */ + private int dist(int[] point, int[] pivot) { + return point[0] * point [0] + point[1] * point[1] + - pivot[0]* pivot[0] - pivot[1] * pivot[1]; + } + + /** + * 按奇偶排序数组 + * 给定一个非负整数数组 A, A 中一半整数是奇数,一半整数是偶数。 + * + * 对数组进行排序,以便当 A[i] 为奇数时,i 也是奇数;当 A[i] 为偶数时, i 也是偶数。 + * + * 你可以返回任何满足上述条件的数组作为答案 + * 输入:[4,2,5,7] + * 输出:[4,5,2,7] + * 解释:[4,7,2,5],[2,5,4,7],[2,7,4,5] 也会被接受。 + */ + public static int[] sortArrayByParity2(int[] A){ + int odd =1; + Integer notoddIndex =null; + Integer noteventIndex =null; + int even =0; + while(odd < A.length){ + if(notoddIndex==null){ + if(A[odd] %2 !=1){ + notoddIndex = odd; + } else { + odd = odd + 2; + } + } + + if(noteventIndex ==null && even < A.length){ + if(A[even] % 2 !=0){ + noteventIndex = even; + } else { + even = even + 2; + } + } + + if(noteventIndex!=null && notoddIndex!=null){ + int temp = A[notoddIndex]; + A[notoddIndex] = A[noteventIndex]; + A[noteventIndex] = temp; + noteventIndex = null; + notoddIndex = null; + } + + } + return A; + + } + + /** + * 按奇偶排序数组 + * 双指针 正解 + * 时间复杂度: O(N),其中 N 是 A 的长度。 + */ + public static int[] sortArrayByParity3(int[] A) { + int j = 1; + for (int i = 0; i < A.length; i += 2) + if (A[i] % 2 == 1) { + while (A[j] % 2 == 1) + j += 2; + + // Swap A[i] and A[j] + int tmp = A[i]; + A[i] = A[j]; + A[j] = tmp; + } + + return A; + } + + /** + * 将矩阵按对角线排序 + * 对角线 是 横坐标和 纵纵坐标+1 + * @param + */ + public int[][] diagonalSort(int[][] mat) { + // 行数 + int m = mat.length; + // 列数 + int n = mat[0].length; + // 主对角线的条数 + int dLen = m + n - 1; + + // 每一条对角线都创建一个动态数组 + ArrayList[] diagonal = new ArrayList[dLen]; + for (int i = 0; i < dLen; i++) { + diagonal[i] = new ArrayList<>(m); + } + + // 遍历原始矩阵,把原始矩阵中的元素放进对应的动态数组中 + // 主对角线上元素的特点是:纵坐标 - 横坐标 = 定值 + // 加上偏移 m - 1 是为了能够放进数组中 + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + diagonal[j - i + (m - 1)].add(mat[i][j]); + } + } + + // 对每一个对角线上的动态数组分别进行升序排序 + for (int i = 0; i < dLen; i++) { + Collections.sort(diagonal[i]); + } + + int[][] res = new int[m][n]; + + // 对角线数组上还未取出的元素的下标,初始化的时候均为 0 + int[] next = new int[dLen]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // 对角线的坐标 + int index = j - i + (m - 1); + // 记录结果 + res[i][j] = diagonal[index].get(next[index]); + // 维护 next 数组的值 + next[index]++; + } + } + return res; + } + + + /** + * 字典序的第K小数字 + * + * 给定整数 n 和 k,找到 1 到 n 中字典序第 k 小的数字 + * + * 字典序的排列是 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9],所以第二小的数字是 10。 + * + * + * 1 + * 10 11 12 13 + * 100...109 110..119 + * + * 2 + * + * 3 + * + * @param + */ + + public static int findKthNumber(int n, int k) { + int cur = 1; + k = k - 1;//扣除掉第一个0节点 + while(k>0){ + + int num = getNodeNum(n,cur,cur+1); + + if(num<=k){//第k个数不在以cur为根节点的树上 + cur+=1;//cur在字典序数组中从左往右移动 + //并且k 去掉 以cur为根节点的 数值 + k-=num; + }else{//在子树中 + cur*=10;//cur在字典序数组中从上往下移动 + //从 以10为root开始搜索 + k-=1;//刨除根节点 + } + } + return cur; + } + + /** + * rootNode 为1 + * 1 + * 10 11 12.。。19 [10..19] 9个值 19<20 就是 20- + * + * + * @param n + * @param first + * @param last + * @return + */ + public static int getNodeNum(int n, long first, long last){ + int num = 0; + while(first <= n){ + /** + * rootNode 为1 + * 第二层是 last(20,200,2000) - first(10,100,1000) + * + * rootNode 为 10 + * + * 第二层是 last(11,110) - first(10,100) + * + */ + num += Math.min(n+1,last) - first;//比如n是195的情况195到100有96个数 + first *= 10; + last *= 10; + } + return num; + } + + + public static void main(String[] args) { + //int[] A = {3,1,3,2,2,1,1,1,2,0,0,4,0,1,0,1,1,1,2,2}; + //System.out.println("A = " + Arrays.toString(sortArrayByParity2(A)));; + findKthNumber(2000,1000); + } +} diff --git a/src/main/java/name/zicat/leetcode/stack/MedianFinder.java b/src/main/java/name/zicat/leetcode/stack/MedianFinder.java new file mode 100644 index 0000000..237afbe --- /dev/null +++ b/src/main/java/name/zicat/leetcode/stack/MedianFinder.java @@ -0,0 +1,58 @@ +package name.zicat.leetcode.stack; + +import name.zicat.leetcode.queue.PriorityQueueTest; + +import java.util.PriorityQueue; + +/** + * 295 数据流的中位数 + * + * 有序列表 + * + * 优先队列 维护最大值的优先队列,维护最小值的优先队列 + * + * 奇数 拿 左队列的 第一个数, 偶数 拿 左右的数 做个平方 + * + * addNum + * + * findMedian + */ +public class MedianFinder { + + /** + * 当前大顶堆和小顶堆的元素个数之和 + */ + private int count; + private PriorityQueue maxheap; + private PriorityQueue minheap; + + /** + * initialize your data structure here. + */ + public MedianFinder() { + count = 0; + maxheap = new PriorityQueue<>((x, y) -> y - x); + minheap = new PriorityQueue<>(); + } + + public void addNum(int num) { + count += 1; + maxheap.add(num); + minheap.add(maxheap.poll()); + // 如果两个堆合起来的元素个数是奇数,小顶堆要拿出堆顶元素给大顶堆 + if ((count & 1) != 0) { + maxheap.add(minheap.poll()); + } + } + + public double findMedian() { + if ((count & 1) == 0) { + // 如果两个堆合起来的元素个数是偶数,数据流的中位数就是各自堆顶元素的平均值 + return (double) (maxheap.peek() + minheap.peek()) / 2; + } else { + // 如果两个堆合起来的元素个数是奇数,数据流的中位数大顶堆的堆顶元素 + return (double) maxheap.peek(); + } + } + +} diff --git a/src/main/java/name/zicat/leetcode/stack/Stack.java b/src/main/java/name/zicat/leetcode/stack/Stack.java new file mode 100644 index 0000000..270e27c --- /dev/null +++ b/src/main/java/name/zicat/leetcode/stack/Stack.java @@ -0,0 +1,70 @@ +package name.zicat.leetcode.stack; + +import java.util.EmptyStackException; + +/** + * 先进 后出 + * + * 总结 + * 括号的题目想到用栈 + * + * 有配对关系的题目可以用栈 + * + * + */ +public class Stack { + + private static class StackNode { + private T data; + private StackNode next; + + public StackNode(T data){ + this.data =data; + } + } + + /** + * 对栈顶元素的操作 + */ + private StackNode top; + + /** + * 从栈顶取元素 + * @return + */ + public T pop(){ + if(top == null) throw new RuntimeException(); + T value = top.data; + /** + * 栈顶的指针指向 下一个 + */ + top = top.next; + return value; + } + + /** + * 新的 node的 + * @param item + */ + public void push(T item){ + + StackNode node = new StackNode(item); + node.next = top; + top = node; + + } + + /** + * peek是 查看 栈顶元素的值, pop是取出元素 + * @return + */ + public T peek(){ + if(top == null) throw new EmptyStackException(); + return top.data; + + } + + public boolean isEmpty(){ + return top == null; + } +} diff --git a/src/main/java/name/zicat/leetcode/stack/StackUtil.java b/src/main/java/name/zicat/leetcode/stack/StackUtil.java new file mode 100644 index 0000000..4e37dc0 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/stack/StackUtil.java @@ -0,0 +1,287 @@ +package name.zicat.leetcode.stack; + +/** + * 总结 + * * 括号的题目想到用栈 + * * + * * 有配对关系的题目可以用栈 + */ +public class StackUtil { + /** + * 配对问题 用 栈 + */ + + /** + * 84 柱状图中最大的矩形面积。 + * + * 输入 [2,1,5,6,2,3] + * + * 输出 10 ,5和6 最大矩阵组合是 10 + * + * 某个柱子 向左右扩展,遇到比自己矮的柱子,停止 + * + * 给定一个数 找到 左边和右边 第一个 比自己小的数的位置 + * + * 单调栈里面的元素 是 单调递增或者 单调递减 + * + * 如果 入栈的数 比自己小,就可以知道出栈的元素左右的比自己小的数,即计算出栈的元素的面积 + * + */ + public static int largestRectangleArea(int[] heights) { + /** + * 处理边界情况 + */ + if( heights == null || heights.length ==0) { + return 0; + } + + int l =heights.length; + /** + * 维护成单调递增的栈 + */ + Stack stack = new Stack<>(); + int result =0 ; + for (int i=0;i<=l;i++){ + /** + * 出栈的时候 才会计算 柱子为高度的矩阵,所以最后一个元素的时候要 push进去个-1 + */ + int cur = i == l ? -1:heights[i]; + + /** + * 如果小于 栈订端的元素,即找到 顶端元素 左边和右边最小的数 的index停止即可 + */ + while(!stack.isEmpty() && cur<=heights[stack.peek()]) { + //高就是要出栈的元素 + int height = heights[stack.pop()]; + /** + * 单调递增的 left 基本上就是自己的index位置 + */ + int left = stack.isEmpty()? 0: (stack.peek()+1); + + /** + * 右边就是 + */ + int right = i-1; + int erea = height * (right - left +1); + result = Math.max(result,erea); + + } + /** + * push 索引位置 + */ + stack.push(i); + } + return result; + } + + + /** + * 1249 移除无效的括号 (medium) + * + * 左括号对应 入栈操作, 右括号出栈操作 + * + * input s= "lee(t(c)o)de)" + * + * output leet() + * + */ + + public static String minRemoveToMakeValid(String s) { + if(s == null){ + return null; + } + // 保存 左括号的下标 + Stack stack = new Stack<>(); + + // 默认值是 false,数组中值为 true的数组下标对应的值需要移除 + boolean[] checkExist = new boolean[s.length()]; + + for(int i=0;i< s.length();i++){ + + if(s.charAt(i) == '('){ + //左括号的话入栈 + stack.push(i); + //并且此时 暂无匹配的 右括号 + checkExist[i] =true; + } + + /** + * 栈不是空的 ,说明有匹配 + */ + if(s.charAt(i) == ')' && !stack.isEmpty()){ + //有匹配 + checkExist[stack.pop()] =false; + + } + else if (s.charAt(i) ==')' && stack.isEmpty()){ + //无匹配 + checkExist[i] = true; + } + + } + + StringBuilder result = new StringBuilder(); + for(int i=0;i< s.length();i++){ + if(checkExist[i] == true){ + continue; + } + result.append(s.charAt(i)); + } + return result.toString(); + } + + /** + * 42.接雨水 + * + * 输入 [0,1,0,2,1,0,1,3,2,1,2,1] + * + * 输出 6 + * + * 递减时不断入栈 + * + * 出现高出前面的柱子,可以计算 接水槽 + * + * [4 3 2 5] + * 出栈 第三个元素 2 + * 计算 5 与 3之间的积水 + * + * 积水槽的面积 (3(5的下标)-1(3的下标)-1 )* [min(3,5)-2] + * + * 计算 5与 4之间的积水 + * (3(5的下标)-0(4的下标)-1)*(4-3) + * + * O(N) 每个柱子都最多进栈一次,出栈一次 + * + */ + + public static int trap(int[] height) { + Stack stack = new Stack<>(); + int area =0; + int index =0; + while(index < height.length){ + while(!stack.isEmpty()&& height[index] > height[stack.peek()]) { + int top = stack.pop(); + if(stack.isEmpty()){ + break; + } + int distance = index - stack.peek() -1; + int height2= Math.min(height[index],height[stack.peek()]) -height[top]; + area += distance * height2; + + } + stack.push(index); + index++; + } + return area; + + } + + /** + * 85 最大矩形 + * + * [1,0,1,0,0] + * [1,0,1,1,1] + * [1,1,1,1,1] + * [1,0,0,1,0] + * + * 替换成柱状图 求 柱状图的最大面积 + * + * + * 没看懂 + * + * + */ + + + + public static int maxArea(int[] heights) { + if(heights == null || heights.length ==0){ + return 0; + } + Stack stack = new Stack(); + int max =0; + int i=0; + while(i stack = new Stack < > (); + stack.push(-1); + int maxarea = 0; + for (int i = 0; i < heights.length; ++i) { + while (stack.peek() != -1 && heights[stack.peek()] >= heights[i]) + maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1)); + stack.push(i); + } + while (stack.peek() != -1) + maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() -1)); + return maxarea; + } + + public static int maximalRectangle(char[][] matrix) { + + if (matrix.length == 0) return 0; + int maxarea = 0; + int[] dp = new int[matrix[0].length]; + + for(int i = 0; i < matrix.length; i++) { + for(int j = 0; j < matrix[0].length; j++) { + + // update the state of this row's histogram using the last row's histogram + // by keeping track of the number of consecutive ones + + dp[j] = matrix[i][j] == '1' ? dp[j] + 1 : 0; + } + // update maxarea with the maximum area from this row's histogram + maxarea = Math.max(maxarea, leetcode84(dp)); + } return maxarea; + } + + /** + * 503 + */ + + /** + * 895 + */ + + /** + * 353 + */ + + /** + * 621 + */ + + /** + * 622 + */ + + /** + * 862 + */ + + + + + + + + +} diff --git a/src/main/java/name/zicat/leetcode/tree/TreeNode.java b/src/main/java/name/zicat/leetcode/tree/TreeNode.java new file mode 100644 index 0000000..f14c9ec --- /dev/null +++ b/src/main/java/name/zicat/leetcode/tree/TreeNode.java @@ -0,0 +1,8 @@ +package name.zicat.leetcode.tree; + +public class TreeNode { + public int val; + public TreeNode left; + public TreeNode right; + public TreeNode(int x) { val =x;} +} diff --git a/src/main/java/name/zicat/leetcode/tree/TreeUtils.java b/src/main/java/name/zicat/leetcode/tree/TreeUtils.java new file mode 100644 index 0000000..e4e5b5e --- /dev/null +++ b/src/main/java/name/zicat/leetcode/tree/TreeUtils.java @@ -0,0 +1,549 @@ +package name.zicat.leetcode.tree; + + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * @author jessica + * Date 2020/7/29 + */ +public class TreeUtils { + /** + * BST 中序遍历是有序的 + */ + + /** + * https://visualgo.net/zh/bst + * 前序遍历 中左右 + * def preorder(self,root) + * if root: + * self.traverse_path.append(root.val) + * self.preorder(root.left) + * self.preorder(root.right) + * + * 中序遍历 左中右 + * + * def inorder(self,root): + * if root + * self.inorder(root.left) + * self.traverse_path append(root.val) + * self inorder(root.right) + * + * 后序遍历 + * + * def postorder(self,root) + * if root: + * self.postorder(root.left) + * self.postorder(root.right) + * self.traverse_path append(root.val) + */ + + /** + * 二叉树 vs 二叉搜索树 + * + * 二叉搜索树 是 二叉树的一种 + * + * 二叉搜索树节点大小规律:所有左边的节点<=node.value <=所有右边的节点 + * 如果左子树不为空,那么其上的所有节点值都小于根节点的值 + * 如果右子树不为空,那么其上所有节点值都大于跟节点的值 + * 左右子树也分别为二叉查找树 + * 8 二叉搜索树 8 二叉树 + * / \ /\ + * 4 11 4 11 + * /\ \ /\ \ + *2 6 18 2 20 18 + *平衡二叉树的查找效率是O(logN),类似于二分法 + * 平衡二叉树节点的平衡因子,它指的是该节点的两个子树,即左子树和右子树的高度差,即用左子树的高度减去右子树的高度,如果该节点的某个子树不存在,则该子树的高度为0,如果高度差的绝对值超过1就要根据情况进行调整。 + * https://blog.csdn.net/u014634338/article/details/42465089 + * 完全不平衡二叉树的查找效率是O(N) + */ + + + /** + * https://leetcode-cn.com/problems/binary-tree-inorder-traversal + * input [1,null,2,3] + * + */ + public static int inOrderTraversal(TreeNode root){ + + return 0; + } + + /** + * 297 二叉树的序列化与反序列化 + * 示例 + * 1 + * /\ + *2 3 + * /\ + * 4 5 + * + * 序列化为[1,2,3,null,null,4,5] null为了 在反序列的时候可以重新生成树 + * + * 深度优先和广度优先遍历 + * + * 深度优先遍历 + * 先遍历根结点,再遍历左子树,最后再遍历右子树 + * + * 广度优先遍历 + * 需要靠队列 + * 根节点入队 出队 跟节点 左右子节点分别入队 + * + * 出队 左节点, 入队 左节点的子左右节点 + * + * + */ + + /** + * 深度遍历 递归,时间复杂度 O(N) + */ + + public String serialize(TreeNode root){ + return serializeHelper(root,""); + } + + /** + * 序列化 时间复杂度 遍历了 所有的节点 是 O(N) + * 空间复杂度 O(N) + * @param root + * @param str + * @return + */ + private String serializeHelper(TreeNode root,String str) { + //递归处理 + if(root ==null){ + str +="null,"; + } else { + //前序遍历 + str += root.val + ","; + str = serializeHelper(root.left,str); + str = serializeHelper(root.right,str); + } + return str; + } + + //反序列化 + public TreeNode deserialize(String data){ + String[] data_array = data.split(","); + List data_list = new LinkedList(Arrays.asList(data_array)); + return deserializeHelper(data_list); + } + + private TreeNode deserializeHelper(List data_list) { + //递归处理,从字符串首元素开始处理 + if(data_list.get(0).equals("null")) { + data_list.remove(0); + return null; + } + //不是null建立新的 treenode + TreeNode root = new TreeNode(Integer.valueOf(data_list.get(0))); + data_list.remove(0); + //递归处理先反序列化左子树,再反序列化右子树 + root.left = deserializeHelper(data_list); + root.right = deserializeHelper(data_list); + return root; + } + + /** + * 广度优先 遍历 + */ + + /** + * 广度优先遍历 序列化 时间复杂度 O(N) + * @param root + * @return + */ + public static String serializeG(TreeNode root){ + if(root ==null){ + return ""; + } + StringBuilder sb = new StringBuilder(); + LinkedList queue = new LinkedList<>(); + //队列里面加 跟节点 + queue.add(root); + + while(!queue.isEmpty()){ + //出queue的操作 + TreeNode t =queue.poll(); + if(t!=null){ + sb.append(t.val+","); + //左右节点 加入 queue中 + queue.add(t.left); + queue.add(t.right); + } else { + sb.append("#,"); + } + } + + sb.deleteCharAt(sb.length() -1); + return sb.toString(); + + } + /** + * 广度优先遍历 反序列化 时间复杂度 O(N) + */ + + public static TreeNode deserializeG(String data){ + if(data==null || data.length()==0){ + return null; + } + String[] arr = data.split(","); + TreeNode root = new TreeNode(Integer.parseInt(arr[0])); + LinkedList queue = new LinkedList<>(); + + queue.add(root); + //建立zhizhne + int i=1; + while(!queue.isEmpty()){ + TreeNode t =queue.poll(); + if(t ==null){ + continue; + } + /** + * 先还原 左子树 + */ + if(!arr[i].equals("#")){ + t.left = new TreeNode(Integer.parseInt(arr[i])); + queue.offer(t.left); + } else { + // # 号是 null value + t.left = null; + queue.offer(null); + } + i++; + + /** + * 再还原 右子树 + */ + if(!arr[i].equals("#")){ + t.right = new TreeNode(Integer.parseInt(arr[i])); + queue.offer(t.right); + } else { + t.right =null; + queue.offer(null); + } + i++; + } + return root; + + } + + /** + * 450.删除二叉搜索树中的节点 + * 给定一个二叉搜索树的根节点 root和一个值key, + * 删除二叉搜索树中的key对应的节点,并保证二叉搜索树的性质不变 + * 返回二叉搜索树(会被更新)的根节点的引用 + * + * 一般来说,删除节点可分为两个步骤: + * + * 首先找到需要删除的节点: + * 如果找到了,删除它 + * + * 要求算法时间复杂度为O(h),h为树的高度 + * + * root [5,3,6,2,4,null,7] + * key =3 + * 5 + * /\ + * 3 6 + * /\ \ + * 2 4 7 + * + * + * 结果是 + * 5 + * /\ + * 4 6 + * / \ + * 2 7 + * + * [5,4,6,2,null,7] + * + * 思路: + * 如果 key > root.val 递归右子树,删除节点 root.right = deleteNode(root.right, key) + * 如果 key < root.val 递归左子树,删除节点 root.left = deleteNode(root.left,key) + * + * 如果 key == root.val + * 1.node 是叶子节点,直接删除: root =null + * 2.node 不是叶子节点,并且有 右子树 ,用前驱节点(successor) 代替该节点 + * 后继节点是 右子树的最小值 + * + * 3.node不是叶子节点 并且有 左子树 ,用(predecessor) 后继节点代替 该节点 + * 前驱节点 是左子树的最小值 + * + * + **/ + public TreeNode deleteNode(TreeNode root,int key){ + //递归终止条件 + if(root==null){ + return null; + } + if(key > root.val){ + //在右子树 查找 该节点 + root.right =deleteNode(root.right,key); + } else if(key < root.val){ + //在左子树 查找该节点 + root.left = deleteNode(root.left,key); + } else { + //直接删除该节点 + if(root.left == null && root.right ==null){ + root =null; + } else if(root.right!=null){ + //替换 右子树的最小值,再删除 该节点 + root.val = rightMin(root); + root.right = deleteNode(root.right,root.val); + } else if(root.left!=null){ + //替换 左子树的最大值,再删除该节点 + root.val = leftMax(root); + root.left = deleteNode(root.left,root.val); + } + + } + return root; + } + + /** + * 找到以某个结点为根节点的右子树的最小值 + * @param root + * @return + */ + public int rightMin(TreeNode root){ + root = root.right; + while(root.left!=null) root = root.left; + return root.val; + } + + /** + * 找到某个节点为根节点的 左子树的最大值 + */ + public int leftMax(TreeNode root){ + root = root.left; + while(root.right!=null) root= root.right; + return root.val; + } + + /** + * 951.翻转等价二叉树 + * + * 树的子结构是 数,所以适合递归 + * + * + */ + + public boolean flipEquiv(TreeNode root1,TreeNode root2){ + + if(root1==null && root2==null){ + return true; + } + if(root1==null||root2==null ||root1.val!=root2.val){ + return false; + } + /** + * 两种情况 + */ + return (flipEquiv(root1.left,root2.left)&&flipEquiv(root1.right,root2.right)|| + flipEquiv(root1.left,root2.right)&& flipEquiv(root1.right,root2.left)); + } + + /** + * 1008 先序遍历 + * Input: 8 5 1 7 10 12 + * + * lowerBound: Integer.Min + * UpperBound: Integer.Max + * 值是不是在 [lowerBound,uppserBound]的范围内 是的话 upperBound为当前值 + * 时间复杂度 O(N) 遍历数组 + * 空间复杂度 O(N) 存储二叉树 + **/ + + int index =0; + int[] preorder; + int n; + public TreeNode bstFromPreOrder (int[] preorder) { + this.preorder = preorder; + n = preorder.length; + + return helper(Integer.MIN_VALUE,Integer.MAX_VALUE); + } + + /** + * 通过下限和上限来控制指针移动的范围 + * @param lowerBound + * @return + */ + private TreeNode helper(int lowerBound,int upperBound){ + //所有元素都加到 二叉树中 + if(index == n) + return null; + + int cur = preorder[index]; + + if(cur < lowerBound || cur >upperBound) return null; + index ++; + TreeNode root = new TreeNode(cur); + /** + * 递归组成 其做子树和右子树 + */ + root.left = helper(lowerBound,cur); + root.right = helper(cur,upperBound); + return root; + } + + /** + * 425.单词方块 + * + * 单词序列 + */ + + /** + * N 叉数的最大深度 + */ + + public int maxDepth(Node root){ + if(root ==null){ + return 0; + } else if(root.children.isEmpty()) { + return 1; + } else { + List height = new LinkedList(); + for(Node node:root.children){ + height.add(maxDepth(node)); + } + return Collections.max(height)+1; + } + + } + /** + * 二叉树最长连续序列 + */ + + private int maxLength = 0; + public int longestConsecutive(TreeNode root) { + dfs(root, null, 0); + return maxLength; + } + + private void dfs(TreeNode p, TreeNode parent, int length) { + if (p == null) return; + length = (parent != null && p.val == parent.val + 1) ? length + 1 : 1; + maxLength = Math.max(maxLength, length); + dfs(p.left, p, length); + dfs(p.right, p, length); + } + /** + * 425.单词方块 + * 给定一个单词集合(没有重复),找出其中所有的单词方块 + * + * 单词序列[ball,area,lead,lady] 形成的单词方块是 + * + * 第一行和第一列相等 + * 根据 对角线 b r a y 作为对称轴 相互对称 + * b a l l + * a r e a + * l e a d + * l a d y + * + * 不会 + */ + + /** + * 212 单词搜索 2 + * + */ + + /** + * 词典中最长的单词 + * 给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。 + * + *输入: + * words = ["w","wo","wor","worl", "world"] + * 输出:"world" + * 解释: + * 单词"world"可由"w", "wo", "wor", 和 "worl"添加一个字母组成。 + * + * 输入: + * words = ["a", "banana", "app", "appl", "ap", "apply", "apple"] + * 输出:"apple" + * 解释: + * "apply"和"apple"都能由词典中的单词组成。但是"apple"的字典序小于"apply"。 + * + */ + + + public String longestWord(String[] words) { + Trie trie = new Trie(); + int index = 0; + for (String word: words) { + trie.insert(word, ++index); //indexed by 1 + } + trie.words = words; + return trie.dfs(); + } + + /** + * 226.翻转二叉树 + * 我们从根节点开始,递归地对树进行遍历,并从叶子结点先开始翻转。如果当前遍历到的节点 \textit{root}root 的左右两棵子树都已经翻转,那么我们只需要交换两棵子树的位置,即可完成以 \textit{root}root 为根节点的整棵子树的翻转。 + * + */ + public static TreeNode invertTree(TreeNode root) { + if(root ==null){ + return null; + } + TreeNode left = invertTree(root.left); + TreeNode right = invertTree(root.right); + root.left = right; + root.right = left; + return root; + + } + /** + * 红黑树 + * https://www.jianshu.com/p/e136ec79235c + */ + + + /*public static boolean isSubStructure (TreeNode rootA,TreeNode rootB) { + + } + + public static boolean isSub(TreeNode A,TreeNode B){ + if() + + } + + boolean isSub(TreeNode A, TreeNode B) { + //这里如果B为空,说明B已经访问完了,确定是A的子结构 + if (B == null) + return true; + //如果B不为空A为空,或者这两个节点值不同,说明B树不是 + //A的子结构,直接返回false + if (A == null || A.val != B.val) + return false; + //当前节点比较完之后还要继续判断左右子节点 + return isSub(A.left, B.left) && isSub(A.right, B.right); + }*/ + + + + + +} + +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } +}; diff --git a/src/main/java/name/zicat/leetcode/tree/Trie.java b/src/main/java/name/zicat/leetcode/tree/Trie.java new file mode 100644 index 0000000..c2e1fc1 --- /dev/null +++ b/src/main/java/name/zicat/leetcode/tree/Trie.java @@ -0,0 +1,140 @@ +package name.zicat.leetcode.tree; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/** + * 前缀树 + * + * 又成字典树,Trie + * Search + * 用来存储 查找字符串,特别适合字符串前缀查找 + * + * 字段串的公共前缀只保存一次,节省空间 + * + * 存储字符串 组成的字典,对字符串进行前缀查找,实现剪枝的时间优化 + */ +public class Trie { + private TrieNode root; + String[] words; + + public Trie(){ + root = new TrieNode(); + } + /** + * root为 / + * + * / + * H F A + * E R I + * L E R + * P L E + * O + */ + + + public void insert(String word,int index){ + HashMap child = root.children; + //对word的字母进行循环 + for(int i=0; i< word.length();i++){ + + char c = word.charAt(i); + TrieNode next; + //是否在子节点中 + if(child.containsKey(c)){ + //拿出来用来插入 下一个字母 + next = child.get(c); + } else { + //新建一个 trieNode + next = new TrieNode(c); + next.end = index; + //更新子节点 + child.put(c,next); + } + + child = next.children; + + if(i == (word.length() -1)){ + //查找的时候就知道是 前缀还是 完整的字符串 + next.isWord =true; + } + } + + } + + /** + * 查找和前缀查找,看 isWord是 true还是 false + * @return + */ + public boolean search(String word){ + TrieNode t = searchNode(word); + if(t!=null && t.isWord){ + return true; + } + return false; + } + + public boolean startsWith(String prefix){ + if(searchNode(prefix) == null){ + return false; + } + return true; + } + + public TrieNode searchNode(String str){ + Map children = root.children; + TrieNode cur =null; + for(int i=0;i < str.length();i++){ + char c = str.charAt(i); + if(children.containsKey(c)){ + cur = children.get(c); + children = cur.children; + } else { + return null; + } + } + return cur; + + } + public String dfs() { + String ans = ""; + Stack stack = new Stack(); + stack.push(root); + while (!stack.empty()) { + TrieNode node = stack.pop(); + if (node.end > 0 || node == root) { + if (node != root) { + String word = words[node.end - 1]; + if (word.length() > ans.length() || + word.length() == ans.length() && word.compareTo(ans) < 0) { + ans = word; + } + } + for (TrieNode nei: node.children.values()) { + stack.push(nei); + } + } + } + return ans; + } + +} + +class TrieNode { + char c; + HashMap children = new HashMap<>(); + int end; + /** + * 标注节点是不是 字符串的结尾 + */ + public boolean isWord = false; + public TrieNode(char c) { + this.c =c; + } + + public TrieNode(){ + + } + +}