├── Leetcode ├── dp │ ├── 10. 正则表达式匹配.md │ ├── 121. 买卖股票的最佳时机.md │ ├── 139. 单词拆分.md │ ├── 152. 乘积最大子数组.md │ ├── 198. 打家劫舍.md │ ├── 221. 最大正方形.md │ ├── 238. 除自身以外数组的乘积.md │ ├── 279. 完全平方数.md │ ├── 300. 最长递增子序列.md │ ├── 309. 买卖股票的最佳时机含冷冻期.md │ ├── 312. 戳气球.md │ ├── 32. 最长有效括号.md │ ├── 322. 零钱兑换.md │ ├── 337. 打家劫舍 III.md │ ├── 416. 分割等和子集.md │ ├── 42. 接雨水.md │ ├── 494. 目标和.md │ ├── 5. 最长回文子串.md │ ├── 53. 最大子数组和.md │ ├── 62. 不同路径.md │ ├── 647. 回文子串.md │ ├── 70. 爬楼梯.md │ ├── 72. 编辑距离.md │ ├── 85. 最大矩形.md │ └── 96. 不同的二叉搜索树.md ├── 二叉树 │ ├── 101. 对称二叉树.md │ ├── 102. 二叉树的层序遍历.md │ ├── 104. 二叉树的最大深度.md │ ├── 105. 从前序与中序遍历序列构造二叉树.md │ ├── 114. 二叉树展开为链表.md │ ├── 124. 二叉树中的最大路径和.md │ ├── 226. 翻转二叉树.md │ ├── 236. 二叉树的最近公共祖先.md │ ├── 297. 二叉树的序列化与反序列化.md │ ├── 437. 路径总和 III.md │ ├── 538. 把二叉搜索树转换为累加树.md │ ├── 543. 二叉树的直径.md │ ├── 617. 合并二叉树.md │ ├── 94. 二叉树的中序遍历.md │ ├── 96. 不同的二叉搜索树.md │ └── 98. 验证二叉搜索树.md ├── 二进制 │ ├── 338. 比特位计数.md │ └── 461. 汉明距离.md ├── 优先队列 │ ├── 23. 合并 K 个升序链表.md │ └── 239. 滑动窗口最大值.md ├── 单调栈 │ ├── 739. 每日温度.md │ └── 84. 柱状图中最大的矩形.md ├── 回溯 │ ├── 17. 电话号码的字母组合.md │ ├── 22. 括号生成.md │ ├── 39. 组合总和.md │ ├── 46. 全排列.md │ └── 78. 子集.md ├── 数组 │ ├── 1. 两数之和.md │ ├── 11. 盛最多水的容器.md │ ├── 15. 三数之和.md │ ├── 215. 数组中的第K个最大元素.md │ ├── 238. 除自身以外数组的乘积.md │ ├── 240. 搜索二维矩阵 II.md │ ├── 283. 移动零.md │ ├── 287. 寻找重复数.md │ ├── 309. 买卖股票的最佳时机含冷冻期.md │ ├── 31. 下一个排列.md │ ├── 322. 零钱兑换.md │ ├── 33. 搜索旋转排序数组.md │ ├── 34. 在排序数组中查找元素的第一个和最后一个位置.md │ ├── 4. 寻找两个正序数组的中位数.md │ ├── 48. 旋转图像.md │ ├── 55. 跳跃游戏.md │ ├── 56. 合并区间.md │ ├── 62. 不同路径.md │ └── 75. 颜色分类.md ├── 构建数据结构 │ ├── 207. 课程表.md │ ├── 208. 实现 Trie (前缀树).md │ └── 399. 除法求值.md ├── 栈、队列、HashMap()、HashSet() │ ├── 128. 最长连续序列.md │ ├── 169. 多数元素 - 副本 (3).md │ ├── 169. 多数元素.md │ ├── 20. 有效的括号.md │ ├── 3. 无重复字符的最长子串.md │ ├── 394. 字符串解码.md │ ├── 438. 找到字符串中所有字母异位词.md │ └── 560. 和为 K 的子数组.md ├── 深度优先搜索和广度优先搜索 │ ├── 200. 岛屿数量.md │ └── 79. 单词搜索.md ├── 贪心 │ ├── 253. 会议室II.md │ ├── 406. 根据身高重建队列.md │ └── 621. 任务调度器.md └── 链表 │ ├── 114. 二叉树展开为链表.md │ ├── 141. 环形链表.md │ ├── 142. 环形链表 II.md │ ├── 146. LRU 缓存.md │ ├── 148. 排序链表.md │ ├── 160. 相交链表.md │ ├── 19. 删除链表的倒数第 N 个结点.md │ ├── 2. 两数相加.md │ ├── 206. 反转链表.md │ ├── 21. 合并两个有序链表.md │ ├── 23. 合并 K 个升序链表.md │ └── 234. 回文链表.md └── README.md /Leetcode/dp/10. 正则表达式匹配.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //动态规划,点评dp[i][j]表示字符串s的前i个字符能否与字符串p的前j的字符串实现正则表达式匹配 4 | public boolean isMatch(String s, String p) { 5 | int m = s.length(); 6 | int n = p.length(); 7 | boolean[][] dp = new boolean[m+1][n+1]; 8 | dp[0][0] = true; 9 | for (int i = 1; i <= n; i++) { 10 | if (p.charAt(i-1) == '*') { 11 | dp[0][i] = dp[0][i-2]; 12 | } 13 | } 14 | for (int i = 1; i <= m; i++) { 15 | for (int j = 1; j <= n; j++) { 16 | if (p.charAt(j - 1) == '.' || p.charAt(j-1) == s.charAt(i-1)) { 17 | dp[i][j] = dp[i-1][j-1]; 18 | } else if (p.charAt(j-1) == '*') { 19 | dp[i][j] = dp[i][j-2] || ((s.charAt(i-1) == p.charAt(j-2) || p.charAt(j-2) == '.')&&dp[i-1][j] ); 20 | // 匹配0次,匹配多次 21 | } 22 | } 23 | } 24 | return dp[m][n]; 25 | } 26 | } 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /Leetcode/dp/121. 买卖股票的最佳时机.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 找某个元素的右边的元素的最大值,作差,比较差的大小,暴力法会超时 4 | // 对于某一个具体的日子,在当天卖出股票所能获得的最大值,是当前的股票价格减去之前出现过的最小值 5 | // 求整个过程的最大值 6 | public int maxProfit(int[] prices) { 7 | int max = 0; 8 | int min = Integer.MAX_VALUE; 9 | for (int i = 0; i < prices.length; i++) { 10 | if (prices[i] < min) { 11 | min = prices[i]; 12 | } 13 | if (prices[i] - min > max) { 14 | max = prices[i] - min; 15 | 16 | } 17 | } 18 | return max; 19 | } 20 | } 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /Leetcode/dp/139. 单词拆分.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //动态规划 dp[]代表前i个字符能否由List里的字符串组成 4 | //初始化dp[0] = 0; 5 | //遍历顺序:从左到右 6 | //递推公式 7 | //s.substring(start,endIndex) start是起始索引,endIndex是结束索引,不包括 8 | // 排列而不是组合 9 | public boolean wordBreak(String s, List wordDict) { 10 | int n = s.length(); 11 | boolean[] dp = new boolean[s.length() + 1]; //代表s的前i个元素能否由字典中的元素组成 12 | dp[0] = true; 13 | for (int i = 1; i <= n; i++) { 14 | for (String str : wordDict) { 15 | if (i >= str.length() && dp [i - str.length()] && s.substring(i - str.length(), i).equals(str)) { 16 | dp[i] = true; 17 | } 18 | } 19 | } 20 | return dp[n]; 21 | } 22 | } 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /Leetcode/dp/152. 乘积最大子数组.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 两种方法: 方法一:用两个变量,记录以某个索引为末尾的子数组的最大乘积和最小乘积 4 | // 遍历 5 | // 方法二:dpMax[i], dpMin[i]维护两个动态规划数组,一个用来记录最大值,一个用来记录最小值 6 | 7 | public int maxProduct(int[] nums) { 8 | // int min = nums[0]; 9 | // int max = nums[0]; // 当前索引位置的乘积最大值 10 | // int res = nums[0]; // 当前所有位置的乘积最小值 11 | // for (int i = 1; i < nums.length; i++) { 12 | // if (nums[i] < 0) { // 如果nums[i] < 0 交换最大最小值 13 | // int temp = max; 14 | // max = min; 15 | // min = temp; 16 | // } 17 | // max = Math.max(nums[i], nums[i]*max); 18 | // min = Math.min(nums[i], nums[i]* min); 19 | // res = Math.max(res,max); 20 | // } 21 | // return res; 22 | int n = nums.length; 23 | int[] dpMax = new int[n]; 24 | int[] dpMin = new int[n]; 25 | int res = nums[0]; 26 | dpMax[0] = nums[0];dpMin[0] = nums[0]; 27 | for (int i = 1; i < n; i++) { 28 | dpMax[i] = Math.max(nums[i],Math.max(nums[i]*dpMax[i-1],nums[i]*dpMin[i-1])); 29 | dpMin[i] = Math.min(nums[i], Math.min(nums[i] * dpMax[i-1],nums[i]*dpMin[i-1])); 30 | res = Math.max(res, dpMax[i]); 31 | } 32 | return res; 33 | 34 | 35 | } 36 | } 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /Leetcode/dp/198. 打家劫舍.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //动态规划4步:1.确定dp数组及下标的含义 4 | // 2.确定递推公式 5 | // 3.dp数组如何初始化 6 | // 4.确定遍历顺序 7 | // 偷窃当前房屋,不偷窃当前房屋 8 | // dp[i] 代表从0-i间房偷窃获得的最大值是多少 9 | // dp[i] = Math.max(dp[i-1], dp[i-2] +nums[i]); 10 | // dp[0] = nums[0], dp[1] = Math.max(nums[1], nums[0]); 11 | // 12 | public int rob(int[] nums) { 13 | int n = nums.length; 14 | if (n == 1) { 15 | return nums[0]; 16 | } else if (n == 2) { 17 | return Math.max(nums[0], nums[1]); 18 | } 19 | int[] dp = new int[n]; 20 | dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]); 21 | for (int i = 2; i < n; i++) { 22 | dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i]); //对于每到一下个房间有两种可能,1.偷当前房间, 2.不偷当前房间 23 | } 24 | return dp[n-1]; 25 | } 26 | } 27 | 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /Leetcode/dp/221. 最大正方形.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 动态规划: dp[i][j]代表以nums[i][j] 为右下角能得到的正方形的最大值 4 | // 递推公式: dp[i][j] = Math.min(dp[i-1][j], dp[i][j - 1], dp[i - 1][j - 1]) 5 | // 初始化 对i==0 || j==0的情况进行初始化 6 | // 遍历顺序 从左到右,从上到下 7 | public int maximalSquare(char[][] matrix) { 8 | int m = matrix.length; 9 | int n = matrix[0].length; 10 | int[][] dp = new int[m][n]; 11 | int maxSide = 0; 12 | for (int i = 0; i < m; i++) { 13 | if (matrix[i][0] == '1') { 14 | dp[i][0] = 1; 15 | maxSide = 1; 16 | } 17 | } 18 | for (int j = 0; j < n; j++) { 19 | if (matrix[0][j] == '1') { 20 | dp[0][j] = 1; 21 | maxSide = 1; 22 | 23 | } 24 | } 25 | for (int i = 1; i < m; i++) { 26 | for (int j = 1; j < n; j++) { 27 | if (matrix[i][j] == '1') { 28 | dp[i][j] = Math.min(dp[i - 1][j],Math.min(dp[i][j - 1], dp[i - 1][j - 1])) + 1; 29 | maxSide = Math.max(maxSide, dp[i][j]); 30 | } 31 | } 32 | } 33 | return maxSide*maxSide; 34 | } 35 | } 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /Leetcode/dp/238. 除自身以外数组的乘积.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 题目要求o(n)的时间复杂度 4 | // 题目类型dp 5 | public int[] productExceptSelf(int[] nums) { 6 | //除nums[i]之外的所有数的乘积也就是将nums[i]当做1 7 | // 可以计算这个数左乘积和右乘积 8 | // 用三个数组分别存储前缀、后缀和乘积 9 | // 对于元素nums[0],我们设定初始值left[0] = 1; 10 | // 对于元素nums[nums.length - 1] 设定初始值right[nums.length - 1] = 1; 11 | int n = nums.length; 12 | int[] left = new int[n]; 13 | int[] right = new int[n]; 14 | int[] res = new int[n]; 15 | left[0] = 1;right[n - 1] = 1; 16 | for (int i = 1; i < n; i++) { 17 | left[i] = left[i-1] * nums[i-1]; 18 | } 19 | for (int j = n - 2; j >= 0; j--) { 20 | right[j] = right[j + 1] * nums[j + 1]; 21 | } 22 | for (int i = 0; i < n; i++) { 23 | res[i] = left[i] * right[i]; 24 | } 25 | return res; 26 | 27 | } 28 | } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /Leetcode/dp/279. 完全平方数.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //dp[]数组代表和为n的完全平方数的最少数量 4 | //递推公式 5 | // dp[i] = Math.min(dp[i],dp[i-j*j] + 1) for all j*j <= i; 6 | // 完全背包 7 | public int numSquares(int n) { 8 | // int[] dp = new int[n+1]; 9 | // dp[0] = 0; 10 | // for(int i = 1; i <= n; i++) { 11 | // int min = n; 12 | // for(int j = 1;j*j <= i; j++) { 13 | // min = Math.min(min,dp[i-j*j]); 14 | // } 15 | // dp[i] = min + 1; 16 | // } 17 | // return dp[n]; 18 | int[] dp = new int[n + 1]; 19 | Arrays.fill(dp,10001); 20 | dp[0] = 0; 21 | //dp[j] = Math.min(dp[j],dp[j-i*i] + 1); 22 | 23 | for (int i = 1; i*i<= n; i++) { 24 | for (int j = i*i; j <= n; j++) { 25 | dp[j] = Math.min(dp[j],dp[j- i*i] + 1); 26 | } 27 | } 28 | return dp[n]; 29 | } 30 | } 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /Leetcode/dp/300. 最长递增子序列.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // dp[i] 表示到以元素i结尾的的最长递增子序列的长度 4 | public int lengthOfLIS(int[] nums) { 5 | int[] dp = new int[nums.length]; 6 | Arrays.fill(dp,1); 7 | for (int i = 1; i < nums.length; i++) { 8 | for (int j = 0; j < i; j++) { 9 | if (nums[i] > nums[j]) { 10 | dp[i] = Math.max(dp[i],dp[j] + 1); 11 | } 12 | } 13 | } 14 | int max = 0; 15 | for (int i = 0; i < nums.length; i++) { 16 | max = Math.max(dp[i],max); 17 | } 18 | return max; 19 | } 20 | } 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /Leetcode/dp/309. 买卖股票的最佳时机含冷冻期.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //动态规划 4 | //动态规划四部曲:确定dp[]数组及下标对应的含义,递推公式、初始化、遍历顺序 5 | // 三种状态: 持有股票,不持有股票并且处于冷冻期,不持有股票并且不处于冷冻期对应dp[i][0],dp[i][1],dp[i][2]; 6 | // 递推公式:dp[i][0] = dp[i-1][2] - prices[0]; 7 | // dp[i][1] = dp[i][0] + prices[i] dp[i][2] = dp[i-1][1]; 8 | public int maxProfit(int[] prices) { 9 | int n = prices.length; 10 | int[][] dp = new int[n][3]; 11 | dp[0][0] = -prices[0]; 12 | dp[0][1] = 0; dp[0][2] = 0; 13 | for (int i = 1; i < prices.length; i++) { 14 | dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2] - prices[i]); 15 | dp[i][1] = dp[i-1][0] + prices[i]; 16 | dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]); 17 | } 18 | return Math.max(dp[n-1][1],dp[n-1][2]); 19 | 20 | } 21 | } 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /Leetcode/dp/312. 戳气球.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // dp[i][j] 代表戳i到j之间内的气球能获得的硬币最大值 4 | // 递推公式:dp[i][j] = Math.max (dp[i][k] + dp[k][j] + val[i]*val[j]*val[k], dp[i][j]) 5 | // k代表最后一次戳的气球 6 | public int maxCoins(int[] nums) { 7 | int n = nums.length; 8 | int[][] dp = new int[n + 2][n + 2]; 9 | int[] val = new int[n + 2]; 10 | val[0] = val[n + 1] = 1; 11 | for (int i = 1; i <= n; i++) { 12 | val[i] = nums[i - 1]; 13 | } 14 | for (int len = 1; len <= n; len++) { // 可以戳的气球数量 15 | for (int i = 1; i <= n - len + 1; i++) { // 可以戳的起点 16 | int j = i + len - 1; // 终点 17 | for (int k = i; k <= j; k++) { // 最后一个戳的气球 18 | dp[i][j] = Math.max (dp[i][k-1] + dp[k + 1][j] + val[i-1]*val[j+1]*val[k], dp[i][j]); 19 | } 20 | } 21 | } 22 | return dp[1][n]; 23 | } 24 | } 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /Leetcode/dp/32. 最长有效括号.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 动态规划 4 | // dp[i] 代表以i结尾的最长有效括号子串的长度 5 | // s.charAt(i) == '( dp[i] = 0; 6 | // s.charAt(i) == ')' s.charAt(i - 1) = '(' dp[i] = dp[i - 2] + 2 7 | // if (i - dp[i - 1] - 2 >= 0)s.charAt(i - dp[i - 1] - 1) == '(' dp[i] = dp[i-1] + 2 + dp[i - dp[i - 1] - 2] 8 | // 初始化dp[0] = 0; 9 | // 遍历顺序 从左向右 10 | public int longestValidParentheses(String s) { 11 | int len = s.length(); 12 | int[] dp = new int[len]; 13 | int maxLen = 0; 14 | for (int i = 1; i <= len - 1; i++) { 15 | if (s.charAt(i) == ')') { 16 | if (s.charAt(i-1) == '(') { 17 | dp[i] = (i >= 2 ? dp[i-2] : 0) + 2; 18 | maxLen = Math.max(maxLen,dp[i]); 19 | } else if (i-1 >= dp[i-1] && dp[i-1] > 0 && s.charAt(i-dp[i-1]-1) == '(') { // 需要判断dp[i-1] > 0 20 | dp[i] = (i - dp[i-1] >= 2)? dp[i-1] + dp[i-dp[i-1]-2] + 2 : dp[i-1] + 2; 21 | } 22 | maxLen = Math.max(dp[i],maxLen); 23 | } 24 | } 25 | return maxLen; 26 | } 27 | } 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /Leetcode/dp/322. 零钱兑换.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 背包问题,完全背包 4 | public int coinChange(int[] coins, int amount) { 5 | int[] dp = new int[amount + 1]; // dp[i] 代表组成金额为i的最少硬币数 6 | Arrays.fill(dp, amount + 1); 7 | dp[0] = 0; 8 | for (int i = 0; i < coins.length; i++) { 9 | for (int j = coins[i]; j <= amount; j++) { 10 | dp[j] = Math.min(dp[j], dp[j-coins[i]] + 1); 11 | } 12 | } 13 | return dp[amount]== amount+1 ? - 1 : dp[amount]; 14 | 15 | } 16 | } 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /Leetcode/dp/337. 打家劫舍 III.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | //对于每一个节点,都有选或者不选两种决定 19 | //选了该节点,则它的左子节点和右子节点不能选 20 | //设定一个数组,下标0代表选了该节点的最大偷盗金额,下标1代表未选择该节点的最小偷盗金额 21 | // dfs + dp 22 | // 时间复杂度O(n),空间复杂度O(n); 每个节点遍历一次 23 | public int rob(TreeNode root) { 24 | int[] res = dfs(root); // res[]记录选root和不选root可以偷窃的最大值 25 | return Math.max(res[0], res[1]); 26 | } 27 | public int[] dfs(TreeNode root) { 28 | if (root == null) { 29 | return new int[]{0,0}; 30 | } 31 | int[] left = dfs(root.left); 32 | int[] right = dfs(root.right); 33 | int selected = root.val + left[1] + right[1]; 34 | int noSelected = Math.max(left[0], left[1]) + Math.max (right[0], right[1]); 35 | return new int[]{selected, noSelected}; 36 | } 37 | 38 | } 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /Leetcode/dp/416. 分割等和子集.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //两重for循环计算 4 | public boolean canPartition(int[] nums) { 5 | // int sum = 0; 6 | // for(int i = 0; i < nums.length; i++) { 7 | // sum += nums[i]; 8 | // } 9 | // if(sum % 2 == 1) return false; 10 | // int target = sum / 2; 11 | // System.out.print(target); 12 | // // //dp[i]代表是否有和为i的子集 13 | // // int[] dp = new int[target + 1]; 14 | // // dp[0] = 0; // 初始化条件 15 | // // for(int i = 0; i < nums.length; i++) { 16 | // // for(int j = target; j >= nums[i]; j--) { 17 | // // dp[j] = Math.max(dp[j],dp[j - nums[i]] + nums[i]); 18 | // // } 19 | 20 | // // } 21 | // int[][] dp = new int[nums.length][target+1]; 22 | // for(int i = 0; i < nums.length; i++){ 23 | // dp[i][0] = 0; 24 | // } 25 | // for (int j = 0; j <= target; j++) { 26 | // if (j >= nums[0]) { 27 | // dp[0][j] = nums[0]; 28 | // } 29 | // } 30 | // for (int i = 1; i < nums.length; i++) { 31 | // for (int j = 1; j <= target; j++) { 32 | // if (j < nums[i]) { 33 | // dp[i][j] = dp[i-1][j]; 34 | // } else { 35 | // dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-nums[i]] + nums[i]); 36 | // } 37 | 38 | // } 39 | // } 40 | 41 | // return dp[nums.length - 1][target] == target; 42 | 43 | int sum = 0; 44 | for(int i = 0; i < nums.length; i++) { 45 | sum += nums[i]; 46 | } 47 | if(sum % 2 == 1) return false; 48 | int target = sum / 2; 49 | boolean[] dp = new boolean[target + 1]; //dp数组代表容量为j的背包能否恰好装满 50 | dp[0] = true; 51 | for (int i = 0; i < nums.length; i++) { 52 | for (int j = target; j >= nums[i]; j--) { 53 | 54 | dp[j] = dp[j] || dp[j-nums[i]]; 55 | 56 | 57 | } 58 | } 59 | return dp[target]; 60 | 61 | 62 | } 63 | } 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /Leetcode/dp/42. 接雨水.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //下标i处能接的雨水为左右两边的最大值里的最小值- height[i] 4 | // 这里可以用双指针的思想 时间复杂度O(n),空间复杂度O(1) 5 | // 动态规划更直接, 用两个数组记录左右两边的最大值 时间复杂度O(n),空间复杂度O(1) 6 | public int trap(int[] height) { 7 | int n = height.length; 8 | // int[] leftMax = new int[n]; 9 | // int[] rightMax = new int[n]; 10 | // leftMax[0] = height[0]; 11 | // rightMax[n - 1] = height[n - 1]; 12 | // for (int i = 1; i < n; i++) { 13 | // leftMax[i] = Math.max(leftMax[i-1], height[i]); 14 | // } 15 | // for (int i = n - 2; i >= 0; i--) { 16 | // rightMax[i] = Math.max(rightMax[i + 1], height[i]); 17 | // } 18 | // int sum = 0; 19 | // for (int i = 0; i < n; i++) { 20 | // sum += Math.min(leftMax[i], rightMax[i]) - height[i]; 21 | // } 22 | // return sum; 23 | int leftMax = 0, rightMax = 0; 24 | int left = 0,right = n - 1, sum = 0; 25 | while (left <= right) { 26 | leftMax = Math.max(height[left], leftMax); 27 | rightMax = Math.max(height[right], rightMax); 28 | if (leftMax < rightMax) { 29 | sum += leftMax - height[left]; 30 | left++; 31 | } else { 32 | sum += rightMax - height[right]; 33 | right--; 34 | } 35 | } 36 | return sum; 37 | } 38 | } 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /Leetcode/dp/494. 目标和.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 首先想到的是回溯,但是回溯的时间复杂度过大(2^n); 4 | // 想到背包问题: 5 | // 两个背包,一个放正数,一个放负数, 6 | // pos[i] - neg[i] = target; 7 | // pos[i] + neg[i] = sum; 8 | // pos[i] = (target + sum) / 2 9 | public int findTargetSumWays(int[] nums, int target) { 10 | int sum = 0; 11 | for (int i = 0; i < nums.length; i++) { 12 | sum += nums[i]; 13 | } 14 | if ((target + sum) % 2 != 0 || (target + sum) < 0) { 15 | return 0; 16 | } 17 | int[] pos = new int[(target + sum) / 2 + 1]; 18 | pos[0]= 1; //pos[i] 代表容量为i的背包有多少种方式填满 19 | for (int i = 0; i < nums.length; i++) { 20 | for (int j = (target + sum) / 2; j >= nums[i]; j--) { 21 | pos[j] += pos[j-nums[i]]; 22 | } 23 | } 24 | return pos[(target + sum) / 2]; 25 | } 26 | } 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /Leetcode/dp/5. 最长回文子串.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //最长回文子串,动态规划, 4 | //dp[i][j]代表从i到j的最长回文子串的长度 5 | //初始化的dp[i][i] = 1 6 | //遍历顺序:按长度遍历,再从左到右按索引遍历 7 | public String longestPalindrome(String s) { 8 | // char[] arr = s.toCharArray(); 9 | // 反序与原始字符串相同,动态规划 10 | // 如果s.substring(i,j)是回文子串,则s.substring(i+1,j-1)一定也是回文子串 11 | // 每一个字符一定是回文子串,判断2个长度的字符串是不是回文子串只需要判断两个字符是否相等 12 | // 用二维哈希表来表示所有子串是不是回文子串,行下标与列下标的最大差值代表长度 13 | // int len = arr.length; 14 | // if(len < 2){ //长度小于2说明是动态数组 15 | // return s; 16 | // } 17 | // boolean[][] hash = new boolean[len][len]; 18 | // int maxLen = 1; 19 | // int begin = 0; 20 | // for(int i = 0 ; i < len; i++){ 21 | // hash[i][i] = true; // 单个字符一定是回文串 22 | // } 23 | // for(int L = 2; L <= len; L++){ //L代表的长度 24 | // for(int i = 0 ; i < len; i++){ //左边界,字符串的起始下标 25 | // int j = L + i - 1; //子字符串的长度为L, j - i + 1 = L; 26 | // if( j >= len){ 27 | // break; 28 | // } 29 | // if(arr[i] != arr[j]){ // 子字符串长度小于等于3的时候,只需要看左右边界 30 | // hash[i][j] = false; // 子字符串长度>3的时候,hash[i][j] = hash[i+1][j-1]; 31 | // }else { 32 | // if(L <= 3){ 33 | // hash[i][j] = true; 34 | // }else{ 35 | // hash[i][j] = hash[i+1][j-1]; 36 | // } 37 | // } 38 | // if(hash[i][j]&& L > maxLen){ 39 | // maxLen = L; 40 | // begin = i; 41 | // } 42 | // } 43 | // } 44 | // return s.substring(begin,begin+maxLen); // 不包括begin+maxLen 45 | int n = s.length(); 46 | if (n == 1) { 47 | return s; 48 | } 49 | boolean[][] dp = new boolean[n][n]; 50 | for (int i = 0; i < n; i++) { 51 | dp[i][i] = true; 52 | } 53 | int maxLen = 1; 54 | int begin = 0; 55 | for (int i = 2; i <= n; i++) { 56 | for (int j = 0; j < n;j++) { 57 | int k = j + i - 1; 58 | if(k >= n) { 59 | break; 60 | } 61 | if (i <= 3) { 62 | if (s.charAt(j) == s.charAt(k)) { 63 | dp[j][k] = true; 64 | }else { 65 | dp[j][k] = false; 66 | } 67 | } else { 68 | if (s.charAt(j) == s.charAt(k)) { 69 | dp[j][k] = dp[j +1][k - 1] ; 70 | }else { 71 | dp[j][k] = false; 72 | } 73 | } 74 | if (dp[j][k] && i > maxLen ) { 75 | maxLen = i; 76 | begin = j; 77 | } 78 | } 79 | } 80 | return s.substring(begin,begin+maxLen); 81 | 82 | } 83 | } 84 | ``` 85 | 86 | -------------------------------------------------------------------------------- /Leetcode/dp/53. 最大子数组和.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int maxSubArray(int[] nums) { 4 | // dp数组代表以nums[i]结尾的的最大子数组和,i代表索引 5 | // if (dp[i-1] < 0) dp[i] = nums[i]; 6 | // else dp[i] = dp[i-1] + nums[i]; 7 | // 找到dp数组的最大值 8 | 9 | int[] dp = new int[nums.length]; 10 | int max = nums[0]; 11 | dp[0] = nums[0]; 12 | for (int i = 1; i < nums.length; i++) { 13 | dp[i] = Math.max(dp[i-1] + nums[i], nums[i]); 14 | max = Math.max(dp[i],max); 15 | } 16 | return max; 17 | } 18 | } 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /Leetcode/dp/62. 不同路径.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | 4 | // 动态规划 5 | // dp[i][j] 代表从(0,0)出发到(i,j)有多少条不同的路径 6 | // dp[i][j] = dp[i-1][j] + dp[i][j-1]; 7 | // 左边界和上边界都只有1种可能 8 | // 从上向下,从左往右遍历 9 | int res = 0; 10 | public int uniquePaths(int m, int n) { 11 | int[][] dp = new int[m][n]; 12 | dp[0][0] = 1; 13 | 14 | for(int i = 1; i < m;i++){ 15 | dp[i][0] = 1; 16 | } 17 | for(int j = 1; j < n; j++){ 18 | dp[0][j] = 1; 19 | } 20 | for(int i = 1 ; i < m; i++){ 21 | for(int j = 1; j < n; j++){ 22 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 23 | } 24 | } 25 | return dp[m-1][n-1]; 26 | } 27 | 28 | } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /Leetcode/dp/647. 回文子串.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //二维动态规划 4 | public int countSubstrings(String s) { 5 | int n = s.length(); 6 | int[][] dp = new int[n][n]; 7 | for (int i = 0; i < n; i++) { 8 | dp[i][i] = 1; 9 | } 10 | for(int len = 2; len <= n; len++) { 11 | for (int i = 0; i <= n-len; i++) { 12 | int j = i + len - 1; 13 | if(s.charAt(i) == s.charAt(j)){ 14 | if (len > 2) { 15 | dp[i][j] = dp[i + 1][j - 1]; 16 | } else { 17 | dp[i][j] = 1; 18 | } 19 | 20 | }else { 21 | dp[i][j] = 0; 22 | } 23 | } 24 | } 25 | int count = 0; 26 | for (int i = 0; i < n; i++) { 27 | for (int j = 0; j < n; j++) { 28 | if (dp[i][j] != 0){ 29 | count++; 30 | } 31 | } 32 | } 33 | return count; 34 | } 35 | } 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /Leetcode/dp/70. 爬楼梯.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // dp[n] 代表 爬到n接楼梯的方法数 4 | // 递推公式:f(n) = f(n-1) + f(n-2) 5 | // 初始值dp[0] = 1;dp[1] = 1; 6 | // 遍历顺序:从小到大 7 | public int climbStairs(int n) { 8 | int[] res = new int[n+1]; 9 | res[0] = 1; 10 | res[1] = 1; 11 | for(int i = 2; i <= n; i++){ 12 | res[i] = res[i-1] + res[i-2]; 13 | } 14 | return res[n]; 15 | } 16 | } 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /Leetcode/dp/72. 编辑距离.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //动态规划 4 | // dp[i][j] 意味着将word1的前i个字符转化为word2的前j个字符的最少操作数 5 | // dp[i][0] = i; dp[0][j] = j; 6 | //递推公式:if (s.charAt(i) == s.charAt(j)) dp[i][j] == dp[i - 1][j - 1] 7 | // else dp[i][j] = Math.max(dp[i - 1][j -1] + 1, dp[i-1][j] + 1, dp[i][j-1] + 1); 8 | // 对应替换的 9 | public int minDistance(String word1, String word2) { 10 | int n = word1.length(); 11 | int m = word2.length(); 12 | int[][] dp = new int[n + 1][m + 1]; 13 | for (int i = 0; i <= n; i++) { 14 | dp[i][0] = i; 15 | } 16 | for (int j = 0; j <= m; j++) { 17 | dp[0][j] = j; 18 | } 19 | for (int i = 1; i <= n; i++) { 20 | for (int j = 1; j <= m; j++) { 21 | if (word1.charAt(i - 1) == word2.charAt(j - 1)) { 22 | dp[i][j] = dp[i-1][j-1]; 23 | } else { 24 | dp[i][j] = Math.min(dp[i-1][j-1] + 1 , Math.min(dp[i-1][j] + 1, dp[i][j-1] + 1)); 25 | } 26 | } 27 | } 28 | return dp[n][m]; 29 | 30 | } 31 | } 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /Leetcode/dp/85. 最大矩形.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 把当前元素作为矩阵的右下角,用一个数组记录当前行到当前位置的连续的1的个数,得到矩阵的最大宽 4 | // 垂直遍历,计算宽和高,得到面积,求出对应位置面积的最大值 5 | public int maximalRectangle(char[][] matrix) { 6 | int row = matrix.length; 7 | int col = matrix[0].length; 8 | int maxArea = 0; 9 | int[][] maxWidth = new int[row][col]; // 记录以当前元素为右下角的最大宽 10 | for (int i = 0; i < row; i++) { 11 | for (int j = 0; j < col; j++) { 12 | if (matrix[i][j] == '1') { 13 | maxWidth[i][j] = (j > 0) ? maxWidth[i][j - 1] + 1 : 1; 14 | } 15 | } 16 | } 17 | for (int i = 0; i < row; i++) { 18 | for (int j = 0; j < col; j++) { 19 | if (matrix[i][j] == '1') { 20 | int width = maxWidth[i][j]; 21 | int area = width; 22 | for (int k = i - 1; k >= 0; k--) { 23 | width = Math.min(maxWidth[k][j], width); //构建矩形的最大宽 24 | area = Math.max(width*(i - k + 1), area); 25 | } 26 | maxArea = Math.max(area, maxArea); 27 | } 28 | } 29 | } 30 | return maxArea; 31 | } 32 | } 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /Leetcode/dp/96. 不同的二叉搜索树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int numTrees(int n) { 4 | int[] dp = new int[n+1]; //dp[i]表示n个节点组成的二叉搜索数的种数 5 | dp[1] = 1; 6 | dp[0] = 1; 7 | //递推公式:选定任意点i作为根节点,则左子树为[1,i-1];右子树为[i+1,n]; 8 | //dp[i] = ∑(dp[i-1] * dp[n-i]) 9 | // dp[1] = 1 10 | // 遍历顺序:从前向后 11 | for (int i = 2; i <=n; i++) { // 总共有多少个节点 12 | for (int j = 1; j <= i; j++) { // 选哪一个做根节点 13 | dp[i] += dp[j-1]*dp[i-j]; 14 | } 15 | } 16 | 17 | return dp[n]; 18 | } 19 | } 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /Leetcode/二叉树/101. 对称二叉树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 1.层序遍历,每次删除两个节点,判断轴对称的两个节点的值是否相等,并将他们的左右子节点的轴对称位置添加进行 19 | // 2.递归 20 | public boolean isSymmetric(TreeNode root) { 21 | // if (root == null) { 22 | // return true; 23 | // } 24 | // return isMirror(root.left, root.right); 25 | if (root == null) { 26 | return true; 27 | } 28 | Queue que = new LinkedList<>(); 29 | que.offer(root.left); 30 | que.offer(root.right); 31 | while (!que.isEmpty()) { 32 | int len = que.size(); 33 | for (int i = len; i > 0; i -= 2) { 34 | TreeNode tmp1 = que.poll(); 35 | TreeNode tmp2 = que.poll(); 36 | if (tmp1 == null && tmp2 == null) { 37 | continue; 38 | } 39 | if (tmp1 == null || tmp2 == null || tmp1.val != tmp2.val) { 40 | return false; 41 | } 42 | que.offer(tmp1.left); //轴对称的添加节点 43 | que.offer(tmp2.right); 44 | que.offer(tmp1.right); 45 | que.offer(tmp2.left); 46 | } 47 | } 48 | return true; 49 | } 50 | 51 | // public boolean isMirror(TreeNode left, TreeNode right) { 52 | // if (left == null && right == null) return true; 53 | // if (left == null || right == null) return false; 54 | // return left.val == right.val && isMirror(left.left, right.right) && isMirror(left.right, right.left); 55 | // } 56 | } 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /Leetcode/二叉树/102. 二叉树的层序遍历.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // public List> resList = new ArrayList>(); 19 | // public List> levelOrder(TreeNode root) { 20 | // checkFun(root); 21 | // return resList; 22 | // } 23 | // public void checkFun(TreeNode node){ 24 | // Queue que = new LinkedList(); 25 | // if(node == null){ 26 | // return; 27 | // } 28 | // que.offer(node); 29 | // while(!que.isEmpty()){ 30 | // List itemList = new ArrayList();//每一层的元素放到一个列表里 31 | // int len = que.size(); //遍历每一层的节点时。将他们的左右节点放入队列,队列的size就是整个 32 | // while(len > 0){ //层的元素个数 33 | // TreeNode tmpNode = que.poll(); 34 | // itemList.add(tmpNode.val); 35 | // if(tmpNode.left != null){ 36 | // que.offer(tmpNode.left); 37 | // } 38 | // if(tmpNode.right != null){ 39 | // que.offer(tmpNode.right); 40 | // } 41 | // len--; 42 | // } 43 | // resList.add(itemList); 44 | // } 45 | 46 | // } 47 | // 层序遍历,经典算法题,首先把头结点添加到队列,然后将队列中的元素按顺序poll(),如果左右节点不为空,则将左右节点添加到队列 48 | public List> levelOrder(TreeNode root) { 49 | List> res = new ArrayList<>(); 50 | if (root == null) { 51 | return res; 52 | } 53 | Queue que = new LinkedList<>(); 54 | que.offer(root); 55 | while (!que.isEmpty()) { 56 | int len = que.size(); 57 | List level = new ArrayList<>(); 58 | while (len > 0) { 59 | TreeNode tmp = que.poll(); 60 | level.add(tmp.val); 61 | if (tmp.left != null) { 62 | que.offer(tmp.left); 63 | } 64 | if (tmp.right != null) { 65 | que.offer(tmp.right); 66 | } 67 | len--; 68 | } 69 | res.add(level); 70 | } 71 | return res; 72 | } 73 | 74 | } 75 | ``` 76 | 77 | -------------------------------------------------------------------------------- /Leetcode/二叉树/104. 二叉树的最大深度.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 1. 层序遍历,用一个变量记录总共有多少层 19 | // 2.递归 20 | public int maxDepth(TreeNode root) { 21 | if (root == null) { 22 | return 0; 23 | } 24 | int leftDepth = maxDepth(root.left); 25 | int rightDepth = maxDepth(root.right); 26 | return Math.max(leftDepth,rightDepth) + 1; 27 | // if (root == null) { 28 | // return 0; 29 | // } 30 | // Queue que = new LinkedList<>(); 31 | // que.offer(root); 32 | // int count = 0; 33 | // while (!que.isEmpty()) { 34 | // int len = que.size(); 35 | // count++; 36 | // while (len > 0) { 37 | // TreeNode tmp = que.poll(); 38 | // if (tmp.left != null) { 39 | // que.offer(tmp.left); 40 | // } 41 | // if (tmp.right != null) { 42 | // que.offer(tmp.right); 43 | // } 44 | // len--; 45 | // } 46 | // } 47 | // return count; 48 | } 49 | } 50 | ``` 51 | 52 | -------------------------------------------------------------------------------- /Leetcode/二叉树/105. 从前序与中序遍历序列构造二叉树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | public class Solution { 18 | // 对于一颗二叉树,前序遍历的第一个值是根节点的值,根据根节点的值和中序遍历可以得到左右子树 19 | // 递归的得到左右子树的根节点 20 | // 从先序遍历中取出第一个元素创建根节点。 21 | // 在中序遍历中找到该根节点的位置,这将中序遍历分为两部分,左边是树的左子树,右边是树的右子树。 22 | // 递归地使用左子树的先序和中序遍历结果重建左子树。 23 | // 递归地使用右子树的先序和中序遍历结果重建右子树。 24 | // 返回根节点 25 | int preIndex = 0; 26 | HashMap hs = new HashMap<>(); 27 | public TreeNode buildTree(int[] preorder, int[] inorder) { 28 | for (int i = 0; i < inorder.length; i++) { 29 | hs.put(inorder[i], i); 30 | } 31 | return buildTreeRecursive(preorder, 0, inorder.length - 1); 32 | } 33 | // recursive 递归 34 | // 利用前序遍历的顺序构建子树,中序遍历得到左子树和右子树的范围 35 | public TreeNode buildTreeRecursive(int[] preorder, int left , int right) { 36 | if (left > right) { 37 | return null; 38 | } 39 | int rootVal = preorder[preIndex++]; // 前序遍历的第一个节点总是当前子树的根 40 | TreeNode root = new TreeNode(rootVal); 41 | int index = hs.get(rootVal); 42 | root.left = buildTreeRecursive(preorder, left, index - 1); 43 | root.right = buildTreeRecursive(preorder, index + 1, right); 44 | return root; 45 | } 46 | 47 | } 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /Leetcode/二叉树/114. 二叉树展开为链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 用一个列表存储先序遍历的结果,再构建单链表,将列表中的节点的左指针赋值为空,右指针指向列表中的下一个位置 19 | List res = new ArrayList<>(); 20 | public void flatten(TreeNode root) { 21 | if (root == null) { 22 | return; 23 | } 24 | preOrder(root); 25 | for (int i = 0; i < res.size() - 1; i++) { // 这里是小于 res.size() - 1,确保不会数组越界 26 | TreeNode node = res.get(i); 27 | node.left = null; 28 | node.right = res.get(i + 1); 29 | } 30 | } 31 | public void preOrder(TreeNode root) { 32 | if (root == null) { 33 | return; 34 | } 35 | res.add(root); 36 | preOrder(root.left); 37 | preOrder(root.right); 38 | } 39 | 40 | } 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /Leetcode/二叉树/124. 二叉树中的最大路径和.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | // 空间复杂度O(N) 递归的深度,最坏是节点的个数 18 | // 时间复杂度O(H) 树的高度 19 | // 方法: 递归 + 后序遍历 20 | // 对于一个节点,他能向上层节点提供的最大值是当前节点的值 + max(左,右子树的最大路径) 21 | // 当前节点嘴和路径中点的最大路径和 = 左右子树的的最大路径 + 当前节点的值 22 | class Solution { 23 | private int maxSum = Integer.MIN_VALUE; 24 | 25 | public int maxPathSum(TreeNode root) { 26 | maxGain(root); 27 | return maxSum; 28 | } 29 | 30 | private int maxGain(TreeNode node) { 31 | if (node == null) { 32 | return 0; 33 | } 34 | int leftGain = Math.max(maxGain(node.left), 0); 35 | int rightGain = Math.max(maxGain(node.right), 0); 36 | maxSum = Math.max(maxSum, node.val + leftGain + rightGain); // 更新最大值 37 | return node.val + Math.max(leftGain, rightGain); // 返回不作为路径终点时能贡献的最大值 38 | } 39 | } 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /Leetcode/二叉树/226. 翻转二叉树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 层序遍历,遍历过程中对每一个节点的位置进行交换 19 | public TreeNode invertTree(TreeNode root) { 20 | if (root == null) { 21 | return root; 22 | } 23 | Queue que = new LinkedList<>(); 24 | que.offer(root); 25 | while (!que.isEmpty()) { 26 | int len = que.size(); 27 | while (len > 0) { 28 | TreeNode tmp = que.poll(); 29 | TreeNode tmpSon = tmp.left; 30 | tmp.left = tmp.right; 31 | tmp.right = tmpSon; 32 | if (tmp.left != null) { 33 | que.offer(tmp.left); 34 | } 35 | if (tmp.right != null) { 36 | que.offer(tmp.right); 37 | } 38 | len--; 39 | } 40 | } 41 | return root; 42 | } 43 | } 44 | ``` 45 | 46 | -------------------------------------------------------------------------------- /Leetcode/二叉树/236. 二叉树的最近公共祖先.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode(int x) { val = x; } 9 | * } 10 | */ 11 | class Solution { 12 | // 递归 13 | // 递归检查左子树 是否包含节点 p 或 q。 14 | // 递归检查右子树 是否包含节点 p 或 q。 15 | // 如果当前节点自身是 p 或 q 中的一个,且其左子树或右子树包含另一个节点,那么该节点就是最近的公共祖先。 16 | // 如果当前节点的左右子树各包含一个目标节点(p 和 q),那么当前节点就是最近的公共祖先。 17 | // 如果上述情况都不成立,返回找到的包含目标节点的子树(可能为空,表示当前子树不包含目标节点)。 18 | public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { 19 | if(root == null || root == p || root == q) { 20 | return root; 21 | } 22 | TreeNode left = lowestCommonAncestor(root.left, p, q); 23 | TreeNode right = lowestCommonAncestor(root.right, p, q); 24 | if (left!= null && right != null) {//都不为空,说明公共祖先在根节点 25 | return root; 26 | }else if (left != null && right == null) { //一边为空,一边不为空,说明在公共祖先在不为空的一边 27 | return left; 28 | }else if (left == null && right != null) { 29 | return right; 30 | } 31 | return null; 32 | } 33 | } 34 | ``` 35 | 36 | -------------------------------------------------------------------------------- /Leetcode/二叉树/297. 二叉树的序列化与反序列化.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode(int x) { val = x; } 9 | * } 10 | */ 11 | // 一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构 12 | // 序列化,层序遍历。反序列化,去除首尾"[]",将字符串按照","划分,采用层序遍历的方式,还原树 13 | // 层序遍历 14 | public class Codec { 15 | 16 | // 序列化,层序遍历树。需要注意的是:在结尾删除"," 17 | public String serialize(TreeNode root) { 18 | if (root == null) { 19 | return "[]"; 20 | } 21 | Queue que = new LinkedList<>(); 22 | StringBuffer sb = new StringBuffer(); 23 | sb.append("["); 24 | que.offer(root); 25 | while (!que.isEmpty()) { 26 | TreeNode tmp = que.poll(); 27 | if (tmp != null) { 28 | que.offer(tmp.left); 29 | que.offer(tmp.right); // 这里最终的结果会多出几个null值,对于序列化二叉树没有任何影响 30 | sb.append(tmp.val + ","); 31 | } else { 32 | sb.append("null,"); 33 | } 34 | } 35 | sb.deleteCharAt(sb.length() - 1); 36 | sb.append("]"); 37 | return sb.toString(); 38 | } 39 | 40 | // Decodes your encoded data to tree. 41 | // 用split方法划分,将字符串划分为一个字符串数组 42 | // 字符串转为Integer的方法 Integer.parseInt(); 43 | public TreeNode deserialize(String data) { 44 | if (data.equals("[]")) { 45 | return null; 46 | } 47 | String[] val = data.substring(1, data.length() - 1).split(","); 48 | TreeNode root = new TreeNode(Integer.parseInt(val[0])); 49 | Queue que = new LinkedList<>(); 50 | que.offer(root); 51 | int i = 1; 52 | while (!que.isEmpty()) { 53 | TreeNode tmp = que.poll(); 54 | if (!val[i].equals("null")) { 55 | tmp.left = new TreeNode(Integer.parseInt(val[i])); 56 | que.offer(tmp.left); 57 | } 58 | i++; 59 | if (!val[i].equals("null")) { 60 | tmp.right = new TreeNode(Integer.parseInt(val[i])); 61 | que.offer(tmp.right); 62 | } 63 | i++; 64 | } 65 | return root; 66 | } 67 | } 68 | // Your Codec object will be instantiated and called as such: 69 | // Codec ser = new Codec(); 70 | // Codec deser = new Codec(); 71 | // TreeNode ans = deser.deserialize(ser.serialize(root)); 72 | ``` 73 | 74 | -------------------------------------------------------------------------------- /Leetcode/二叉树/437. 路径总和 III.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | //计算所有路径和为targetSym的路径:深度优先搜索 19 | //定义函数rootSum 用来计算以某个节点为起点时的路径和为targetSum的条数 20 | //pathSum函数递归调用自身,遍历所有节点; 21 | // 时间复杂度O(N^2),空间复杂度(N) 22 | // 采用前缀和的方式,时间复杂度O(N),空间复杂度O(N) 23 | // 采用先序遍历的方式用hashmap记录所有路径和,若遍历当前节点时,hashmap中存在路径和-targetsum 24 | // 说明此路径中存在某个节点到当前节点的路径和为target 25 | // 最后需要回溯,防止影响到其他节点 26 | public int pathSum(TreeNode root, long targetSum) { // taegetSum 会溢出,需要改为long 27 | Map prefix = new HashMap<>(); 28 | prefix.put(0L, 1); // 为了处理路径和刚好等于targetSum的特殊情况 29 | return dfs(root, prefix, 0, targetSum); 30 | } 31 | public int dfs(TreeNode node, Map prefix, long curr,long targetSum) { 32 | if (node == null) { 33 | return 0; 34 | } 35 | int count = 0; 36 | curr += node.val; 37 | count = prefix.getOrDefault(curr - targetSum, 0); 38 | prefix.put(curr, prefix.getOrDefault(curr, 0) + 1); 39 | count += dfs(node.left, prefix, curr, targetSum); 40 | count += dfs(node.right, prefix, curr, targetSum); 41 | prefix.put(curr, prefix.get(curr) - 1); // 递归返回后,减少技术,防止影响其他路径的搜索 42 | return count; 43 | } 44 | } 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /Leetcode/二叉树/538. 把二叉搜索树转换为累加树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 解决方案使用了一个递归的方法。算法的关键步骤如下: 19 | // 如果当前节点为空,则返回 null。 20 | // 首先递归处理右子树,因为我们需要先处理比当前节点大的节点。 21 | // 处理完右子树后,累加当前节点的值到 sum 变量中,并将当前节点的值更新为 sum。 22 | // 最后递归处理左子树 23 | int sum = 0; 24 | public TreeNode convertBST(TreeNode root) { 25 | if (root == null) { 26 | return null; 27 | } 28 | convertBST(root.right); 29 | sum += root.val; 30 | root.val = sum; 31 | convertBST(root.left); 32 | return root; 33 | } 34 | 35 | } 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /Leetcode/二叉树/543. 二叉树的直径.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | //对每一个节点求左子树的最大深度 + 右子树的最大深度 19 | // 他们之和的最大值即为直径。 20 | // 直径为所有子节点的最大 21 | int sum = 0; 22 | public int diameterOfBinaryTree(TreeNode root) { 23 | maxDepth(root); 24 | return sum; 25 | 26 | } 27 | public int maxDepth(TreeNode node) { 28 | if (node == null) { 29 | return 0; 30 | } 31 | int leftDepth = maxDepth(node.left); 32 | int rightDepth = maxDepth(node.right); 33 | sum = Math.max(sum, leftDepth + rightDepth); 34 | return Math.max(leftDepth, rightDepth) + 1; //左右节点的最大深度 + 1,因为要包括自身 35 | } 36 | 37 | } 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /Leetcode/二叉树/617. 合并二叉树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | //层序遍历 两棵树的对应位置都不为空则值相加 19 | //以返回第一颗数为例,如果第一颗树和第二颗数对应位置都不为空,值相加;第一颗树为空,第二颗不空,让第一颗的对应的位置指向第二颗的 20 | //对应位置;第二颗为空,第一颗为空,不操作; 21 | public TreeNode mergeTrees(TreeNode root1, TreeNode root2) { 22 | if (root1 == null) { 23 | return root2; 24 | } 25 | if (root2 == null) { 26 | return root1; 27 | } 28 | Queue que = new LinkedList<>(); 29 | que.offer(root1); 30 | que.offer(root2); 31 | while (!que.isEmpty()) { 32 | TreeNode tmp1 = que.poll(); 33 | TreeNode tmp2 = que.poll(); 34 | tmp1.val += tmp2.val; 35 | if (tmp1.left != null && tmp2.left != null) { 36 | que.offer(tmp1.left); // 层序遍历,每次poll两棵树对应位置的两个点 37 | que.offer(tmp2.left); 38 | } else if (tmp1.left == null && tmp2.left != null) { 39 | tmp1.left = tmp2.left; 40 | } 41 | if (tmp1.right != null && tmp2.right != null) { 42 | que.offer(tmp1.right); 43 | que.offer(tmp2.right); 44 | } else if (tmp1.right == null && tmp2.right != null) { 45 | tmp1.right = tmp2.right; 46 | } 47 | } 48 | return root1; 49 | } 50 | } 51 | ``` 52 | 53 | -------------------------------------------------------------------------------- /Leetcode/二叉树/94. 二叉树的中序遍历.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | // 两种方法 : 1. 递归 2.迭代 18 | class Solution { 19 | List res; 20 | public List inorderTraversal(TreeNode root) { 21 | res = new ArrayList<>(); 22 | Stack stack = new Stack(); 23 | TreeNode cur = root; 24 | while (!stack.isEmpty() || cur != null) { 25 | while (cur != null) { 26 | stack.push(cur); 27 | cur = cur.left; // 左 28 | } 29 | cur = stack.pop(); 30 | res.add(cur.val); // 中 31 | cur = cur.right; // 右 32 | } 33 | return res; 34 | } 35 | 36 | 37 | 38 | 39 | } 40 | 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /Leetcode/二叉树/96. 不同的二叉搜索树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int numTrees(int n) { 4 | int[] dp = new int[n+1]; //dp[i]表示n个节点组成的二叉搜索数的种数 5 | dp[1] = 1; 6 | dp[0] = 1; 7 | //递推公式:选定任意点i作为根节点,则左子树为[1,i-1];右子树为[i+1,n]; 8 | //dp[i] = ∑(dp[i-1] * dp[n-i]) 9 | // dp[1] = 1 10 | // 遍历顺序:从前向后 11 | for (int i = 2; i <=n; i++) { // 总共有多少个节点 12 | for (int j = 1; j <= i; j++) { // 选哪一个做根节点 13 | dp[i] += dp[j-1]*dp[i-j]; 14 | } 15 | } 16 | 17 | return dp[n]; 18 | } 19 | } 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /Leetcode/二叉树/98. 验证二叉搜索树.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 两种方法:一种是递归,一种是中序遍历 19 | // 对二叉树中序遍历得到的结果是升序的, 20 | // 需要注意边界值,如果最左边的节点的值是最小值 21 | public boolean isValidBST(TreeNode root) { 22 | long max = Long.MAX_VALUE; 23 | long min = Long.MIN_VALUE; 24 | 25 | return isValid(root, min, max); 26 | } 27 | // Deque stack = new LinkedList<>(); 28 | // long inorder = Long.MIN_VALUE; 29 | // while (!stack.isEmpty() || root != null) { 30 | // while (root != null) { 31 | // stack.push(root); 32 | // root = root.left; 33 | // } 34 | // TreeNode tmp = stack.pop(); 35 | // if ((long)tmp.val <= inorder) { 36 | // return false; 37 | // } 38 | // root = tmp.right; 39 | // inorder = tmp.val; 40 | // } 41 | // return true; 42 | // } 43 | 44 | public boolean isValid(TreeNode node, long min, long max) { 45 | if (node == null) { 46 | return true; 47 | } 48 | if (node.val <= min || node.val >= max) { 49 | return false; 50 | } 51 | return isValid(node.left, min, node.val) && isValid(node.right, node.val, max); 52 | } 53 | } 54 | ``` 55 | 56 | -------------------------------------------------------------------------------- /Leetcode/二进制/338. 比特位计数.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 二进制 4 | // 将当前元素与1按位与,计算二进制中1的个数 5 | public int[] countBits(int n) { 6 | int[] res= new int[n + 1]; 7 | for (int i = 0; i <=n; i++) { 8 | int count = 0; 9 | int tmp = i; 10 | while (tmp != 0) { 11 | if ((tmp & 1) == 1) { 12 | count++; 13 | } 14 | tmp >>= 1; 15 | } 16 | res[i] = count; 17 | } 18 | return res; 19 | } 20 | } 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /Leetcode/二进制/461. 汉明距离.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //检查x和y中的二进制不同的位数,可以先计算x异或y,x^y 4 | //再利用按位与判断尾数是否为1 xor&1 5 | public int hammingDistance(int x, int y) { 6 | int res = x^y, hammingDistance = 0; 7 | while (res != 0) { 8 | if ((res & 1) == 1) { 9 | hammingDistance++; 10 | } 11 | res >>= 1; 12 | } 13 | return hammingDistance; 14 | } 15 | } 16 | ``` 17 | 18 | -------------------------------------------------------------------------------- /Leetcode/优先队列/23. 合并 K 个升序链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | // 创建一个最小堆,存储所有的链表的头结点,每次从链表中取出一个节点,并将这个节点的下一个节点放入最小堆中 14 | // 这样就可以保证每次取得当前k个节点的最小值 15 | public ListNode mergeKLists(ListNode[] lists) { 16 | PriorityQueue pq = new PriorityQueue<>((a,b) -> a.val - b.val); 17 | ListNode dummy = new ListNode(-1); 18 | ListNode cur = dummy; 19 | for (ListNode node :lists) { 20 | if (node != null) { // 需要判断是否为空,否则会出现空指针异常,优先队列不能加入空值 21 | pq.offer(node); 22 | } 23 | } 24 | while (!pq.isEmpty()) { 25 | cur.next = pq.poll(); 26 | cur = cur.next; 27 | if (cur.next != null) { 28 | pq.offer(cur.next); 29 | } 30 | } 31 | return dummy.next; 32 | } 33 | } 34 | ``` 35 | 36 | -------------------------------------------------------------------------------- /Leetcode/优先队列/239. 滑动窗口最大值.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //优先队列存储当前元素值和索引,用大顶堆进行排序 4 | public int[] maxSlidingWindow(int[] nums, int k) { 5 | int n = nums.length; 6 | if (n == 1) { 7 | return nums; 8 | } 9 | int[] res = new int[n - k + 1]; 10 | PriorityQueue pq = new PriorityQueue<>((a,b) -> b[0] - a[0]); 11 | for (int i = 0; i < k; i++) { 12 | pq.offer(new int[]{nums[i],i}); 13 | } 14 | res[0] = pq.peek()[0]; 15 | int j = 1; 16 | for (int i = k ; i < n; i++) { 17 | pq.offer(new int[]{nums[i],i}); 18 | while (pq.peek()[1] < i - k + 1) { //滑动窗口的右边界k,左边界i-k+1 19 | pq.poll(); 20 | } 21 | res[j++] = pq.peek()[0]; 22 | } 23 | return res; 24 | } 25 | } 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /Leetcode/单调栈/739. 每日温度.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //o(n^2)会超时 4 | //用单调栈,单调递减栈,栈中只存储索引 5 | // 出现元素比栈顶元素更小时,一直弹出,直到温度更大才停止 6 | public int[] dailyTemperatures(int[] temperatures) { 7 | // int n = temperatures.length; 8 | // int[] res = new int[temperatures.length]; 9 | // Deque stack = new LinkedList<>(); //单调栈 10 | // for (int i = 0; i < n; i++) { 11 | // while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { 12 | // int index = stack.pop(); 13 | // res[index] = i - index; 14 | // } 15 | // stack.push(i); 16 | // } 17 | // while (!stack.isEmpty()) { 18 | // res[stack.pop()] = 0; 19 | // } 20 | // return res; 21 | // int n = temperatures.length; 22 | // int[] res = new int[n]; 23 | // Deque stack = new LinkedList(); 24 | // for (int i = 0; i < n; i++) { 25 | // while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { 26 | // int index = stack.pop(); 27 | // res[index] = i - index; 28 | // } 29 | // stack.push(i); 30 | // } 31 | // return res; 32 | int n = temperatures.length; 33 | Stack stack = new Stack<>(); 34 | int[] res = new int[n]; 35 | for (int i = 0; i < n; i++) { 36 | while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { 37 | int tmp = stack.pop(); 38 | res[tmp] = i - tmp; 39 | } 40 | stack.push(i); 41 | } 42 | return res; 43 | } 44 | } 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /Leetcode/单调栈/84. 柱状图中最大的矩形.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //面积,(索引的差 + 1)*(高度的最小值) 4 | // 单调栈 5 | // 对于index位置的最大矩形的款是到左右两边第一次出现比heights[index]小的位置, 6 | public int largestRectangleArea(int[] heights) { 7 | int n = heights.length; 8 | Deque stack = new LinkedList<>(); 9 | int maxArea = 0; 10 | for (int i = 0; i < heights.length; i++) { 11 | while (!stack.isEmpty() && heights[i] < heights[stack.peek()]) { 12 | int top = stack.pop(); 13 | int maxWidth = stack.isEmpty() ? i : i - stack.peek() - 1; // i 是右边届, stack.peek()是左边界 14 | maxArea = Math.max(maxWidth * heights[top], maxArea); 15 | } 16 | stack.push(i); 17 | } 18 | while (!stack.isEmpty()) { // 右边没有更小的高 19 | int top = stack.pop(); 20 | int maxWidth = stack.isEmpty() ? n : n - stack.peek() - 1; 21 | maxArea = Math.max(maxWidth * heights[top], maxArea); 22 | } 23 | return maxArea; 24 | } 25 | } 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /Leetcode/回溯/17. 电话号码的字母组合.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 所有可能,全排列,回溯 4 | List res = new ArrayList<>(); 5 | String[] numbers = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; 6 | public List letterCombinations(String digits) { 7 | if (digits.length() == 0) { 8 | return res; 9 | } 10 | backTracking(digits, 0); 11 | return res; 12 | } 13 | StringBuffer sb = new StringBuffer(); 14 | public void backTracking(String digits, int num) { 15 | if (num == digits.length()) { 16 | res.add(sb.toString()); 17 | return; 18 | } 19 | int index = digits.charAt(num) - '0'; // 这里-'0'得到数字,不能用Inter.valueOf(),这是将字符串转为数字 20 | String tmp = numbers[index]; 21 | for (int i = 0; i < tmp.length(); i++) { 22 | sb.append(tmp.charAt(i)); 23 | backTracking(digits, num + 1); 24 | sb.deleteCharAt(sb.length() - 1); 25 | } 26 | 27 | } 28 | 29 | } 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /Leetcode/回溯/22. 括号生成.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 回溯,全排列 4 | // stringBuffer的append方法和deleteCharAt()方法 5 | // 有效的括号组合,对于任意括号左边的左括号的数量大于等于右括号的数量 6 | // 空间复杂度:O(n) 7 | List res; 8 | int left, right; 9 | int count = 0; 10 | StringBuffer sb = new StringBuffer(); 11 | public List generateParenthesis(int n) { 12 | left = 0; 13 | right = 0; 14 | count = n; 15 | res = new ArrayList<>(); 16 | backTracking(); 17 | return res; 18 | } 19 | 20 | public void backTracking() { 21 | if (right == count) { 22 | res.add(sb.toString()); 23 | } 24 | if (left < count) { // append左括号 25 | sb.append('('); 26 | left++; 27 | backTracking(); 28 | sb.deleteCharAt(sb.length() - 1); 29 | left--; 30 | } 31 | if (right < left) { // append右括号,右括号的数量需要小于左括号的数量 32 | sb.append(')'); 33 | right++; 34 | backTracking(); 35 | sb.deleteCharAt(sb.length() - 1); 36 | right--; 37 | } 38 | } 39 | } 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /Leetcode/回溯/39. 组合总和.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 1.回溯 因为是需要返回列表 4 | // 如果是求个数那就是完全背包 5 | // 递归三要素:1.返回值的类型和传递的参数 2.递归终止的条件 3.单层递归的逻辑 6 | List> res = new ArrayList(); 7 | List path = new LinkedList(); 8 | public List> combinationSum(int[] candidates, int target) { 9 | backTracking(candidates,target,0,0); // 后面俩个0,0 对应sum 和 startIndex 避免重复遍历 10 | return res; 11 | } 12 | public void backTracking(int[] candidates, int target, int sum, int startIndex) { 13 | if (sum == target) { 14 | res.add(new ArrayList(path)); 15 | return; 16 | } 17 | if (sum > target) { 18 | return; 19 | } 20 | for (int i = startIndex; i < candidates.length; i++) { 21 | sum += candidates[i]; 22 | path.add(candidates[i]); 23 | backTracking(candidates,target,sum,i); 24 | sum -= candidates[i]; 25 | path.removeLast(); 26 | } 27 | } 28 | 29 | } 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /Leetcode/回溯/46. 全排列.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 回溯算法,用一个boolean类型的数组标记已经使用过的数字 4 | // 回溯三要素:回溯函数的参数和返回类型、回溯的终止条件、回溯搜索的遍历过程 5 | // 全排列:非常经典的回溯算法,回溯算法的本质上遍历所有的和能效 6 | List> res = new ArrayList(); 7 | List path = new LinkedList(); 8 | public List> permute(int[] nums) { 9 | boolean[] used = new boolean[nums.length]; 10 | backtracking(nums,used); 11 | return res; 12 | 13 | } 14 | public void backtracking(int[] nums, boolean[] used) { 15 | if (path.size() == nums.length) { 16 | res.add(new ArrayList(path)); 17 | } 18 | for (int i = 0; i < nums.length; i++) { 19 | if (used[i] == true) continue; 20 | path.add(nums[i]); 21 | used[i] = true; 22 | backtracking(nums,used); 23 | path.remove(path.size() - 1); // 后两步是在回溯,表明当前前缀数字的所有可能性都已经遍历完 24 | used[i] = false; 25 | } 26 | } 27 | // public void backtracking(int[] nums, boolean[] used){ 28 | // if(path.size() == nums.length){ 29 | // res.add(new ArrayList(path)); // 需要注意:这里是new ArrayList(path) ,不是直接path,因为path是一直在变化的 30 | // return; 31 | // } 32 | // for(int i = 0; i < nums.length ; i++){ 33 | // if(used[i] == true)continue; 34 | // path.add(nums[i]); 35 | // used[i] = true; 36 | // backtracking(nums,used); 37 | // path.removeLast(); 38 | // used[i] = false; 39 | // } 40 | // } 41 | } 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /Leetcode/回溯/78. 子集.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | List> res = new ArrayList(); 4 | List path = new LinkedList(); 5 | public List> subsets(int[] nums) { 6 | backTracking(nums,0); 7 | return res; 8 | } 9 | public void backTracking(int[] nums,int startIndex){ 10 | res.add(new ArrayList(path)); 11 | if(startIndex >= nums.length){ 12 | return; 13 | } 14 | for(int i = startIndex; i < nums.length ; i++){ 15 | path.add(nums[i]); 16 | backTracking(nums,i+1); // 这里是i + 1 17 | path.removeLast(); 18 | } 19 | 20 | } 21 | } 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /Leetcode/数组/1. 两数之和.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 1.暴力求解,两重for循环分别代表起始元素 4 | // 2.用HashMap,时间复杂度为O(n),空间复杂度为O(n); 5 | // 两个数之和 = target 那么就是target - 其中一个数 = 另外一个target 6 | // 一重for循环,遍历整个数组,将出现过的值加入到hashmap中,key为值,value为索引 7 | // 对于当前元素,hashmap.contiansKey(target - value) ,则找到两个数 8 | public int[] twoSum(int[] nums, int target) { 9 | int[] res = new int[2]; 10 | HashMap hs = new HashMap(); 11 | for (int i = 0; i < nums.length; i++) { 12 | int value = target - nums[i]; 13 | if (hs.containsKey(value)) { 14 | res[1] = i; 15 | res[0] = hs.get(value); 16 | break; 17 | } 18 | hs.put(nums[i],i); 19 | } 20 | return res; 21 | } 22 | 23 | } 24 | ``` 25 | 26 | -------------------------------------------------------------------------------- /Leetcode/数组/11. 盛最多水的容器.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int maxArea(int[] height) { 4 | // 暴力复杂度为O(n^2) 5 | // 双指针:时间复杂度为)(n) 6 | //最大容量:索引的差*两个索引最小高度; 7 | int left = 0, right = height.length - 1; 8 | int maxArea = 0; 9 | while (left < right) { 10 | int area = Math.min(height[left], height[right]) *(right - left); 11 | maxArea = Math.max(area, maxArea); 12 | if (height[left] > height[right]) { //宽已经是最大了,想要更大的面积只能是更大的高 13 | right--; 14 | } else { 15 | left++; 16 | } 17 | } 18 | return maxArea; 19 | } 20 | } 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /Leetcode/数组/15. 三数之和.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 排序之后,使用双指针,左右指针,注意去重 4 | // 时间复杂度O(n^2),空间复杂度:排序的复杂度O(logn) + 三数列表的个数O(m) 5 | public List> threeSum(int[] nums) { 6 | List> res = new ArrayList<>(); 7 | Arrays.sort(nums); // 排序 8 | int n = nums.length; 9 | for (int i = 0; i < n - 2; i++) { 10 | if (i > 0 && nums[i]== nums[i-1]) { //外部去除 11 | continue; 12 | } 13 | int target = -nums[i]; 14 | int left = i + 1, right = n - 1; 15 | while (left < right) { 16 | if (nums[left] + nums[right] < target) { 17 | left++; 18 | } else if (nums[left] + nums[right] > target) { 19 | right--; 20 | } else { 21 | res.add(Arrays.asList(nums[i], nums[left], nums[right])); 22 | while (left < right && nums[right] == nums[right - 1]) { //内部去重 23 | right--; 24 | } 25 | while (left < right && nums[left] == nums[left + 1]) { 26 | left++; 27 | } 28 | left++; 29 | right--; 30 | } 31 | } 32 | } 33 | return res; 34 | } 35 | } 36 | 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /Leetcode/数组/215. 数组中的第K个最大元素.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //用堆排序实现,删除前k-1个元素皆可以得到第k大的元素 4 | // 要求O(n),但是O(nlogn)可以通过 5 | public int findKthLargest(int[] nums, int k) { 6 | // int n = nums.length; 7 | // buildMaxHeap(nums); 8 | // for (int i = n - 1; i > n - k; i--) { 9 | // swap(nums,0,i); 10 | // heapify(nums,i,0); 11 | // } 12 | // return nums[0]; 13 | PriorityQueue pq = new PriorityQueue<>((a,b)-> b-a); 14 | for (int i = 0; i < nums.length; i++) { 15 | pq.offer(nums[i]); 16 | } 17 | for (int i = 0; i < k - 1; i++) { 18 | pq.poll(); 19 | } 20 | 21 | int val = pq.poll(); 22 | return val; 23 | } 24 | // public void buildMaxHeap(int[] arr) { 25 | // int n = arr.length; 26 | // for (int i = n / 2 - 1; i >= 0; i--) { 27 | // heapify(arr,n,i); 28 | // } 29 | // } 30 | // public void heapify(int[] arr, int n ,int i) { 31 | // int largest = i; 32 | // int left = 2 * i + 1; 33 | // int right = 2 *i + 2; 34 | // if (left < n && arr[left] > arr[largest]) { 35 | // largest = left; 36 | // } 37 | // if (right < n && arr[right] > arr[largest]) { 38 | // largest = right; 39 | // } 40 | // if (largest != i) { 41 | // swap(arr,largest,i); 42 | // heapify(arr,n,largest); 43 | // } 44 | // } 45 | // public void swap(int[] arr, int i ,int j) { 46 | // int temp = arr[i]; 47 | // arr[i] = arr[j]; 48 | // arr[j] = temp; 49 | // } 50 | 51 | } 52 | ``` 53 | 54 | -------------------------------------------------------------------------------- /Leetcode/数组/238. 除自身以外数组的乘积.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 题目要求o(n)的时间复杂度 4 | // 题目类型dp 5 | public int[] productExceptSelf(int[] nums) { 6 | //除nums[i]之外的所有数的乘积也就是将nums[i]当做1 7 | // 可以计算这个数左乘积和右乘积 8 | // 用三个数组分别存储前缀、后缀和乘积 9 | // 对于元素nums[0],我们设定初始值left[0] = 1; 10 | // 对于元素nums[nums.length - 1] 设定初始值right[nums.length - 1] = 1; 11 | int n = nums.length; 12 | int[] left = new int[n]; 13 | int[] right = new int[n]; 14 | int[] res = new int[n]; 15 | left[0] = 1;right[n - 1] = 1; 16 | for (int i = 1; i < n; i++) { 17 | left[i] = left[i-1] * nums[i-1]; 18 | } 19 | for (int j = n - 2; j >= 0; j--) { 20 | right[j] = right[j + 1] * nums[j + 1]; 21 | } 22 | for (int i = 0; i < n; i++) { 23 | res[i] = left[i] * right[i]; 24 | } 25 | return res; 26 | 27 | } 28 | } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /Leetcode/数组/240. 搜索二维矩阵 II.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public boolean searchMatrix(int[][] matrix, int target) { 4 | //从矩阵的左下角或者右上角开始遍历,时间复杂度O(m + n) 5 | int m = matrix.length; 6 | int n = matrix[0].length; 7 | int i = m - 1; 8 | int j = 0; 9 | while (i >= 0 && j < n) { 10 | if (matrix[i][j] == target) { 11 | return true; 12 | } else if(matrix[i][j] > target) { 13 | i--; 14 | } else { 15 | j++; 16 | } 17 | } 18 | 19 | return false; 20 | } 21 | 22 | } 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /Leetcode/数组/283. 移动零.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 用一个指针标记此新的数组的当下元素的索引 4 | // 对整个数组进行遍历,遍历完之后剩下的部分全部赋值为0 5 | // 或者说快慢指针,快指针用来遍历数组,慢指针用来指向移动后当前索引的元素 6 | public void moveZeroes(int[] nums) { 7 | int slow = 0; 8 | for (int i = 0; i < nums.length; i++) { 9 | if (nums[i] != 0) { 10 | nums[slow++] = nums[i]; 11 | } 12 | } 13 | for (int i = slow; i < nums.length; i++) { 14 | nums[i] = 0; 15 | } 16 | 17 | 18 | } 19 | } 20 | ``` 21 | 22 | -------------------------------------------------------------------------------- /Leetcode/数组/287. 寻找重复数.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int findDuplicate(int[] nums) { 4 | // 1.二分查找 计算数组中小于等于该元素数量的个数,如果数量 > 该元素的值,说明,重复元素在l,元素区间内 5 | // 2.Floyd判圈算法 6 | // 快慢指针找相遇点 慢指针一次走一步,快指针一次走两步 7 | // 然后将其中一个指针移动到出发点 8 | // 两个指针的相遇点即为所求 9 | // 可以以i->nums[i]构建链表,有重复数字,说明有环 10 | int low = 0, high = nums.length -1; 11 | while (low < high) { 12 | int mid = low + (high - low) / 2; 13 | int count = 0; 14 | for (int i = 0; i < nums.length ; i++) { 15 | if (nums[i] <= mid) { 16 | count++; 17 | } 18 | } 19 | if (count <= mid) { 20 | low = mid + 1; 21 | } else { 22 | high = mid ; 23 | } 24 | } 25 | return low; 26 | // int slow = nums[0]; 27 | // int fast = nums[nums[0]]; 28 | // while (slow != fast) { 29 | // slow = nums[slow]; 30 | // fast = nums[nums[fast]]; 31 | // } 32 | // fast = 0; 33 | // while (slow != fast) { 34 | // slow = nums[slow]; 35 | // fast = nums[fast]; 36 | // } 37 | // return fast; 38 | } 39 | } 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /Leetcode/数组/309. 买卖股票的最佳时机含冷冻期.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //动态规划 4 | //动态规划四部曲:确定dp[]数组及下标对应的含义,递推公式、初始化、遍历顺序 5 | // 三种状态: 持有股票,不持有股票并且处于冷冻期,不持有股票并且不处于冷冻期对应dp[i][0],dp[i][1],dp[i][2]; 6 | // 递推公式:dp[i][0] = dp[i-1][2] - prices[0]; 7 | // dp[i][1] = dp[i][0] + prices[i] dp[i][2] = dp[i-1][1]; 8 | public int maxProfit(int[] prices) { 9 | int n = prices.length; 10 | int[][] dp = new int[n][3]; 11 | dp[0][0] = -prices[0]; 12 | dp[0][1] = 0; dp[0][2] = 0; 13 | for (int i = 1; i < prices.length; i++) { 14 | dp[i][0] = Math.max(dp[i-1][0], dp[i-1][2] - prices[i]); 15 | dp[i][1] = dp[i-1][0] + prices[i]; 16 | dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]); 17 | } 18 | return Math.max(dp[n-1][1],dp[n-1][2]); 19 | 20 | } 21 | } 22 | ``` 23 | 24 | -------------------------------------------------------------------------------- /Leetcode/数组/31. 下一个排列.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public void nextPermutation(int[] nums) { 4 | //Arrays.sort(int[] nums, int fromIndex, int toIndex) 这个方法对nums中的fromIndex到toIndex - 1的部分进行排序 5 | // 从后向前遍历,一旦出现一个数大于前面的数,说明存在更大的数,记录该索引i 6 | // 下一个更大的数是,后面的数按从小到大排,将后面的数中第一个比i-1 大的数据与之交换 7 | int n = nums.length; 8 | for (int i = n - 1; i >= 0; i--) { 9 | if (i == 0) { 10 | Arrays.sort(nums); //当前排序是最大排序,返回最小排序 11 | } else if (nums[i] > nums[i-1]) { //nums[i] > nums[i-1] 说明存在下一个更大的排列 12 | Arrays.sort(nums, i, n); //并且nums[i]之后的部分满足最大排列,下一个更大的排列,即对nums[i]和之后的部分排序 13 | for (int index = i; index < n; index++) { // 并取出第一个比nums[i-1]大的部分,进行交换 14 | if (nums[index] > nums[i-1]) { 15 | int temp = nums[i - 1]; 16 | nums[i - 1] = nums[index]; 17 | nums[index] = temp; 18 | return; 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /Leetcode/数组/322. 零钱兑换.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 题目要求o(n)的时间复杂度 4 | // 题目类型dp 5 | public int[] productExceptSelf(int[] nums) { 6 | //除nums[i]之外的所有数的乘积也就是将nums[i]当做1 7 | // 可以计算这个数左乘积和右乘积 8 | // 用三个数组分别存储前缀、后缀和乘积 9 | // 对于元素nums[0],我们设定初始值left[0] = 1; 10 | // 对于元素nums[nums.length - 1] 设定初始值right[nums.length - 1] = 1; 11 | int n = nums.length; 12 | int[] left = new int[n]; 13 | int[] right = new int[n]; 14 | int[] res = new int[n]; 15 | left[0] = 1;right[n - 1] = 1; 16 | for (int i = 1; i < n; i++) { 17 | left[i] = left[i-1] * nums[i-1]; 18 | } 19 | for (int j = n - 2; j >= 0; j--) { 20 | right[j] = right[j + 1] * nums[j + 1]; 21 | } 22 | for (int i = 0; i < n; i++) { 23 | res[i] = left[i] * right[i]; 24 | } 25 | return res; 26 | 27 | class Solution { 28 | // 背包问题,完全背包 29 | public int coinChange(int[] coins, int amount) { 30 | int[] dp = new int[amount + 1]; // dp[i] 代表组成金额为i的最少硬币数 31 | Arrays.fill(dp, amount + 1); 32 | dp[0] = 0; 33 | for (int i = 0; i < coins.length; i++) { 34 | for (int j = coins[i]; j <= amount; j++) { 35 | dp[j] = Math.min(dp[j], dp[j-coins[i]] + 1); 36 | } 37 | } 38 | return dp[amount]== amount+1 ? - 1 : dp[amount]; 39 | 40 | } 41 | }} 42 | } 43 | ``` 44 | 45 | -------------------------------------------------------------------------------- /Leetcode/数组/33. 搜索旋转排序数组.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // o(log n) 二分法 4 | // 二分法的难点在于等号 5 | // 判断nums[mid] 与 nums[left] 的关系,如果nums[mid] >= nums[left] ,说明 left 到 mid 有序 6 | // if (nums[mid] < nums[left]) 说明 mid 到right 有序, 7 | // 判断target是否落在有序部分,若不在则切换子数组 8 | // 据此缩小范围 9 | public int search(int[] nums, int target) { 10 | int left = 0, right = nums.length - 1; 11 | while (left <= right) { 12 | int mid = left + (right - left) / 2; 13 | if (nums[mid] == target) { 14 | return mid; 15 | } else if (nums[mid] >= nums[left]) { 16 | if (target >= nums[left] && target < nums[mid]) { 17 | right = mid - 1; 18 | } else { 19 | left = mid + 1; 20 | } 21 | } else { 22 | if (target > nums[mid] && target <= nums[right]) { 23 | left = mid + 1; 24 | } else { 25 | right = mid - 1; 26 | } 27 | } 28 | } 29 | return -1; 30 | } 31 | } 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /Leetcode/数组/34. 在排序数组中查找元素的第一个和最后一个位置.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 时间复杂度O(log n) 空间复杂度O(1) 常量空间存放变量 4 | // 二分法 5 | // 寻找第一次target的位置,更新右边界 6 | // 最后一次target的位置,更新左边界 7 | public int[] searchRange(int[] nums, int target) { 8 | int firstPos = findBoundary(nums, target, true); 9 | if (firstPos == -1) {return new int[]{-1, -1};} 10 | int lastPos = findBoundary(nums, target, false); 11 | return new int[]{firstPos, lastPos}; 12 | } 13 | public int findBoundary(int[] nums, int target, boolean first) { 14 | int left = 0, right = nums.length - 1; 15 | int pos = -1; 16 | while (left <= right) { 17 | int mid = left + (right - left) / 2; 18 | if (nums[mid] < target) { 19 | left = mid + 1; 20 | } else if (nums[mid] > target) { 21 | right = mid - 1; 22 | } else { // nums[mid] == target 23 | pos = mid; 24 | if (first) { 25 | right = mid - 1; // 寻找第一次出现target的位置,向左移动右边界 26 | } else { 27 | left = mid + 1; // 寻找最后一次出现target的位置,向右移动左边界 28 | } 29 | } 30 | } 31 | return pos; 32 | } 33 | 34 | } 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /Leetcode/数组/4. 寻找两个正序数组的中位数.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 算法的时间复杂度为O(log(min(m,n))) 4 | // 二分 5 | public double findMedianSortedArrays(int[] nums1, int[] nums2) { 6 | if (nums1.length > nums2.length) { 7 | return findMedianSortedArrays(nums2, nums1); 8 | } 9 | int m = nums1.length; 10 | int n = nums2.length; 11 | int left = 0, right = m; 12 | 13 | while (left <= right) { 14 | int i = left + (right - left) / 2; 15 | int j = (m + n + 1) / 2 - i; 16 | int nums1LeftMax = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]); 17 | int nums1RightMin = (i == m ? Integer.MAX_VALUE : nums1[i]); 18 | int nums2LeftMax = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]); 19 | int nums2RightMin = (j == n ? Integer.MAX_VALUE : nums2[j]); 20 | if (nums1LeftMax <= nums2RightMin && nums2LeftMax <= nums1RightMin) { 21 | if ((m + n) % 2 == 0) { 22 | return (Math.max(nums1LeftMax, nums2LeftMax) + Math.min(nums1RightMin, nums2RightMin)) / 2.0; 23 | } else { 24 | return Math.max(nums1LeftMax, nums2LeftMax); 25 | } 26 | } else if (nums1LeftMax > nums2RightMin) { 27 | right = i - 1; 28 | } else { 29 | left = i + 1; 30 | } 31 | } 32 | return -1; 33 | } 34 | } 35 | ``` 36 | 37 | -------------------------------------------------------------------------------- /Leetcode/数组/48. 旋转图像.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public void rotate(int[][] matrix) { 4 | // 题目要求需要原地旋转,不能新建数组 5 | // 直接matrix[col][n-1-row] = matrix[row][col]的交换会导致原位置的值被覆盖,后续的交换无法进行 6 | //两步实现:1.转置matrix,使得matrix[i][j]变为matrix[j][i] 7 | //2.将matrix[j][i] 与 matrix[j][n-1-i]交换 8 | // 先转置或者先交换行都一样,需要注意的是:交换和转置的边界 9 | int n = matrix.length; 10 | for (int row = 0; row < n / 2; row++) { 11 | for (int col = 0; col < n; col++) { 12 | int temp = matrix[row][col]; 13 | matrix[row][col] = matrix[n - 1 - row][col]; 14 | matrix[n - 1 - row][col] = temp; 15 | } 16 | } 17 | for (int row = 0; row < n ; row++) { 18 | for (int col = row; col < n; col++) { 19 | int temp = matrix[row][col]; 20 | matrix[row][col] = matrix[col][row]; 21 | matrix[col][row] = temp; 22 | } 23 | } 24 | 25 | 26 | } 27 | } 28 | ``` 29 | 30 | -------------------------------------------------------------------------------- /Leetcode/数组/55. 跳跃游戏.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //贪心算法,局部最优得全局最优 4 | //每一步取最大,看最终能否走到nums[nums.length - 1] 5 | // if (coverrange == i) 说明到i为止就不能购在跳动了 6 | public boolean canJump(int[] nums) { 7 | int coverrange = 0; 8 | for (int i = 0; i < nums.length; i++) { 9 | 10 | coverrange = Math.max(nums[i] + i, coverrange); 11 | if (coverrange == i) { 12 | break; 13 | } 14 | 15 | } 16 | return coverrange >= nums.length - 1 ? true : false; 17 | } 18 | } 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /Leetcode/数组/56. 合并区间.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 按照第数组中的第一个元素排序,排序之后 4 | // 后一个数组的左端点如果小于前一个数组的右端点则需要合并 5 | // 两种情况:1.后一个数组的左端点小于前一个数组的右端点,则直接合并前一个数组 6 | // 2.后一个数组的左端点大于于前一个数组的右端点,则取前一个数组的左端点和后一个数组的右端点 7 | // 由于合并之后的元素个数我们并不知道,所以一开始要用一个list存储结果 8 | public int[][] merge(int[][] intervals) { 9 | int n = intervals.length; // 列已知为2 10 | Arrays.sort(intervals, (a,b) -> (a[0] == b[0] ? a[1] - b[1] : a[0] - b[0])); 11 | List merge = new ArrayList<>(); 12 | for (int i = 0; i < n; i++) { // intevlas[i][1]与merge中最后一个数组的[1]进行比较 13 | if (merge.isEmpty() || intervals[i][0] > merge.get(merge.size() - 1)[1]) { 14 | merge.add(intervals[i]); 15 | } else { 16 | merge.get(merge.size() - 1)[1] = Math.max(merge.get(merge.size() - 1)[1], intervals[i][1]); 17 | } 18 | } 19 | int row = merge.size(); 20 | int[][] res = new int[row][2]; 21 | for (int i = 0; i < row; i++) { 22 | for (int j = 0; j < 2; j++) { 23 | res[i][j] = merge.get(i)[j]; 24 | } 25 | } 26 | return res; 27 | } 28 | } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /Leetcode/数组/62. 不同路径.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | 4 | // 动态规划 5 | // dp[i][j] 代表从(0,0)出发到(i,j)有多少条不同的路径 6 | // dp[i][j] = dp[i-1][j] + dp[i][j-1]; 7 | // 左边界和上边界都只有1种可能 8 | // 从上向下,从左往右遍历 9 | int res = 0; 10 | public int uniquePaths(int m, int n) { 11 | int[][] dp = new int[m][n]; 12 | dp[0][0] = 1; 13 | 14 | for(int i = 1; i < m;i++){ 15 | dp[i][0] = 1; 16 | } 17 | for(int j = 1; j < n; j++){ 18 | dp[0][j] = 1; 19 | } 20 | for(int i = 1 ; i < m; i++){ 21 | for(int j = 1; j < n; j++){ 22 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 23 | } 24 | } 25 | return dp[m-1][n-1]; 26 | } 27 | 28 | } 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /Leetcode/数组/75. 颜色分类.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 原地排序 冒泡排序 选择排序 快速排序 插入排序 4 | // 快排 平均 O(nlogn) 最差O(n^2) 最好是O(nlogn) 5 | public void sortColors(int[] nums) { 6 | quickSort(nums, 0, nums.length - 1); 7 | } 8 | public void quickSort(int[] nums, int low, int high) { 9 | if (low < high) { 10 | int index = partition(nums, low ,high); 11 | quickSort(nums, low ,index - 1); 12 | quickSort(nums, index + 1,high); 13 | } 14 | } 15 | public int partition(int[] nums, int low, int high) { // partition分区 16 | int pivot = nums[high]; // pivot基准 17 | int j = low; 18 | for (int i = low; i < high; i++) { 19 | if (nums[i] < pivot) { 20 | swap(nums, i, j); 21 | j++; 22 | } 23 | } 24 | swap(nums,j, high); 25 | return j; 26 | } 27 | public void swap(int[] arr, int i, int j) { 28 | int tmp = arr[i]; 29 | arr[i] = arr[j]; 30 | arr[j] = tmp; 31 | } 32 | } 33 | ``` 34 | 35 | -------------------------------------------------------------------------------- /Leetcode/构建数据结构/207. 课程表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | List> edges; //存储邻接矩阵 4 | int[] visited; 5 | boolean valid = true; 6 | public boolean canFinish(int numCourses, int[][] prerequisites) { 7 | edges = new ArrayList<>(); 8 | for (int i = 0; i < numCourses; i++) { 9 | edges.add(new ArrayList()); 10 | } 11 | for (int[] info : prerequisites) { 12 | edges.get(info[1]).add(info[0]); 13 | } 14 | visited = new int[numCourses]; 15 | for (int i = 0; i < numCourses && valid ; i++) { 16 | if (visited[i] == 0) { 17 | dfs(i); 18 | } 19 | } 20 | return valid; 21 | 22 | } 23 | public void dfs(int u) { 24 | visited[u] = 1; //表示正在被访问 25 | for (int v : edges.get(u)) { 26 | if (visited[v] == 0) { 27 | dfs(v); 28 | if (!valid) { 29 | return; 30 | } 31 | 32 | }else if(visited[v] == 1) { 33 | valid = false; 34 | return; 35 | } 36 | } 37 | visited[u] = 2; //代表该门课程可以学习完成 38 | } 39 | 40 | 41 | } 42 | ``` 43 | 44 | -------------------------------------------------------------------------------- /Leetcode/构建数据结构/208. 实现 Trie (前缀树).md: -------------------------------------------------------------------------------- 1 | ```java 2 | // 定义前缀树的节点,每个节点有一个子数组和标志位,标志位代表该节点是否为最终节点 3 | // insert方法插入字符串,若当前节点的子数组中的对应索引位置为空,则新建前缀树的节点 4 | // search 和 startWiths的区别在于search需要判断返回节点的isEnd属性 5 | class TrieNode { 6 | TrieNode[] children; 7 | boolean isEnd; 8 | TrieNode() { 9 | children = new TrieNode[26]; 10 | isEnd = false; 11 | } 12 | } 13 | class Trie { 14 | TrieNode root; 15 | public Trie() { 16 | root = new TrieNode(); 17 | } 18 | 19 | public void insert(String word) { 20 | TrieNode node = root; 21 | for (int i = 0; i < word.length(); i++) { 22 | int index = word.charAt(i) - 'a'; 23 | if (node.children[index] == null){ 24 | node.children[index] = new TrieNode(); 25 | } 26 | node = node.children[index]; 27 | } 28 | node.isEnd = true; 29 | 30 | } 31 | 32 | public boolean search(String word) { 33 | TrieNode node = searchPrefix(word); 34 | return node != null && node.isEnd; 35 | } 36 | 37 | public boolean startsWith(String prefix) { 38 | TrieNode node = searchPrefix(prefix); 39 | return node != null; 40 | } 41 | public TrieNode searchPrefix(String word) { 42 | TrieNode node = root; 43 | for (int i = 0; i < word.length(); i++) { 44 | int index = word.charAt(i) - 'a'; 45 | if (node.children[index] == null) { 46 | return null; 47 | } 48 | node = node.children[index]; 49 | } 50 | return node; 51 | } 52 | 53 | } 54 | 55 | /** 56 | * Your Trie object will be instantiated and called as such: 57 | * Trie obj = new Trie(); 58 | * obj.insert(word); 59 | * boolean param_2 = obj.search(word); 60 | * boolean param_3 = obj.startsWith(prefix); 61 | */ 62 | ``` 63 | 64 | -------------------------------------------------------------------------------- /Leetcode/构建数据结构/399. 除法求值.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //使用构建图,使用HashMap存储节点和比率 4 | //对每个查询使用DFS查询两个节点是否存在连接,并计算比率 5 | 6 | // putIfAbsent方法 V putIfAbsent(K key, V value); 7 | // 如果之前 Map 中没有这个键,那么返回 null。 8 | // 如果之前已经有这个键了,那么返回该键之前关联的值,并且不会替换已有的值。 9 | HashMap> hs = new HashMap<>(); 10 | public double[] calcEquation(List> equations, double[] values, List> queries) { 11 | for (int i = 0; i < equations.size(); i++) { 12 | String u = equations.get(i).get(0); 13 | String v = equations.get(i).get(1); 14 | double k = values[i]; 15 | // 构建图 16 | hs.putIfAbsent(u, new HashMap<>()); 17 | hs.putIfAbsent(v, new HashMap<>()); 18 | hs.get(u).put(v, k); 19 | hs.get(v).put(u, 1 / k); 20 | 21 | } 22 | double[] res = new double[queries.size()]; 23 | for (int i = 0; i < queries.size(); i++) { 24 | String u = queries.get(i).get(0); 25 | String v = queries.get(i).get(1); 26 | if (!hs.containsKey(u) || !hs.containsKey(v)) { 27 | res[i] = -1.0; 28 | } else { 29 | res[i] = dfs(u, v, new HashSet()); 30 | } 31 | } 32 | return res; 33 | } 34 | 35 | public double dfs(String start, String end, HashSet visited) { 36 | if (hs.get(start).containsKey(end)) { //直接包含 37 | return hs.get(start).get(end); 38 | } 39 | visited.add(start); 40 | for (Map.Entry neighbor : hs.get(start).entrySet()) { 41 | if (!visited.contains(neighbor.getKey())) { 42 | double path = dfs(neighbor.getKey(), end, visited); 43 | if (path != -1.0) { 44 | return path*neighbor.getValue(); 45 | } 46 | } 47 | } 48 | return -1.0; 49 | } 50 | 51 | } 52 | 53 | ``` 54 | 55 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/128. 最长连续序列.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // O(n)就不能是排序 4 | // 用HashSet存储所有元素 5 | // 对于一个最长序列hashSet中一定不包含当前的nums[i] - 1; 6 | // 判断是否存在nums[i] + 1; 7 | public int longestConsecutive(int[] nums) { 8 | if (nums.length == 0) { 9 | return 0; 10 | } 11 | HashSet hs = new HashSet<>(); 12 | for (int i = 0; i < nums.length; i++) { 13 | hs.add(nums[i]); 14 | } 15 | int max = 1; 16 | 17 | for (int num : nums) { 18 | if (hs.contains(num - 1)) { 19 | continue; 20 | } 21 | int maxTmp = 1; 22 | while (hs.contains(num + 1)) { 23 | num += 1; 24 | maxTmp++; 25 | } 26 | max = Math.max(maxTmp, max); 27 | } 28 | return max; 29 | } 30 | } 31 | ``` 32 | 33 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/169. 多数元素 - 副本 (3).md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int majorityElement(int[] nums) { 4 | //hashmap 使用HashMap,若其中存在,则value + 1,不存在则将相应的key添加进去 5 | Map map = new HashMap(); 6 | for(int num: nums){ 7 | map.put(num,map.getOrDefault(num,0)+1); 8 | if(map.get(num) > nums.length /2){ 9 | return num; 10 | } 11 | } 12 | return -1; 13 | } 14 | } 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/169. 多数元素.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public int majorityElement(int[] nums) { 4 | //hashmap 使用HashMap,若其中存在,则value + 1,不存在则将相应的key添加进去 5 | Map map = new HashMap(); 6 | for(int num: nums){ 7 | map.put(num,map.getOrDefault(num,0)+1); 8 | if(map.get(num) > nums.length /2){ 9 | return num; 10 | } 11 | } 12 | return -1; 13 | } 14 | } 15 | ``` 16 | 17 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/20. 有效的括号.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | public boolean isValid(String s) { 4 | 5 | // 字符串中出现左括号就将右括号压入栈中, 6 | // 出现右括号就与栈顶的元素匹配,判断栈顶的元素是不是对应的右括号 7 | // 不匹配则返回false 8 | // 最终需要检查栈是否为空 9 | // 不为空说明还有未匹配的左括号 10 | 11 | Deque stack = new LinkedList<>(); 12 | for (int i = 0; i < s.length(); i++) { 13 | if (s.charAt(i) == '(' || s.charAt(i) == '{' || s.charAt(i) == '[') { 14 | stack.push(s.charAt(i)); 15 | } else { 16 | if (stack.isEmpty()) { 17 | return false; 18 | } 19 | char top = stack.pop(); 20 | if (s.charAt(i) == ']' && top != '[' || (s.charAt(i) == '}' && top != '{') || (s.charAt(i) == ')' && top != '(')) { 21 | return false; 22 | } 23 | } 24 | } 25 | return stack.isEmpty(); //不匹配说明有多余的左括号 26 | 27 | 28 | } 29 | } 30 | ``` 31 | 32 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/3. 无重复字符的最长子串.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 时间复杂度O(n),空间复杂度O(1) 4 | public int lengthOfLongestSubstring(String s) { 5 | if (s.length() == 0) { 6 | return 0; 7 | } 8 | // HashSet + 滑动窗口 or HashMap + 滑动窗口 9 | HashMap hs = new HashMap<>(); 10 | int left = 0, right = 0, max = Integer.MIN_VALUE; 11 | while (right < s.length()) { 12 | char c = s.charAt(right); 13 | if (hs.containsKey(c) && hs.get(c) >= left) { //发现重复字符且重复字符的位置 > 左边界 14 | left = hs.get(c) + 1; // left 的下一个位置是上一次出现重复的位置加1 15 | } 16 | hs.put(c, right); 17 | max = Math.max(right - left + 1, max); 18 | right++; 19 | 20 | } 21 | return max; 22 | 23 | 24 | } 25 | } 26 | ``` 27 | 28 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/394. 字符串解码.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | int ptr = 0; 4 | // 栈 5 | // 对于数字、字符串和左括号将其压入栈中 6 | // 遇到右括号时,将相邻的左括号和字符串数字都弹出 7 | // 并将解码后的字符串在压入栈中 8 | // 定义一个全局变量用来遍历字符串 9 | // 使用了StringBuffer的insert方法 StringBuffer insert(int offset, String str) 在指定位置插入字符串 10 | public String decodeString(String s) { 11 | Stack stack = new Stack<>(); 12 | while (ptr < s.length()) { 13 | char c = s.charAt(ptr); 14 | if (Character.isDigit(c)) { //数字 15 | String digits = getDigits(s); 16 | stack.push(digits); 17 | } else if (Character.isLetter(c) || c == '[') { 18 | stack.push(String.valueOf(c)); 19 | ptr++; 20 | } else { // 右括号 21 | StringBuffer sb = new StringBuffer(); 22 | while (!stack.peek().equals("[")) { 23 | sb.insert(0, stack.pop()); 24 | } 25 | String str = sb.toString(); // 26 | stack.pop(); // 删除左括号 27 | StringBuffer tmp = new StringBuffer(); 28 | int repeat = Integer.parseInt(stack.pop()); 29 | for (int j = 0; j < repeat; j++) { 30 | tmp.append(str); 31 | } 32 | stack.push(tmp.toString()); 33 | ptr++; 34 | } 35 | } 36 | StringBuffer sb = new StringBuffer(); 37 | while (!stack.isEmpty()) { 38 | sb.insert(0, stack.pop()); 39 | } 40 | return sb.toString(); 41 | } 42 | 43 | public String getDigits(String s) { // 获得数字 44 | StringBuffer sb = new StringBuffer(); 45 | while (Character.isDigit(s.charAt(ptr)) && ptr < s.length()) { 46 | sb.append(s.charAt(ptr++)); 47 | } 48 | return sb.toString(); 49 | } 50 | 51 | 52 | 53 | } 54 | 55 | 56 | ``` 57 | 58 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/438. 找到字符串中所有字母异位词.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 这一题和最小覆盖子串有点相似,但是这里需要找的子串不能包括其他元素,需要加一个长度限制的检查 4 | // 异位词就是不同的排列方式 5 | // 用哈希表存储字符串中每个字符出现的次数 6 | // 维护一个p.length()长度的滑动窗口 7 | // 如果滑动窗口内的元素出现频率等于p的频率,则为字母异位词 8 | // 时间复杂度O(m + (n - m + 1)*26) 9 | // 改进的时间复杂度O(m + n) 10 | public List findAnagrams(String s, String p) { 11 | // List res = new ArrayList(); 12 | // int[] pCount = new int[26]; 13 | // for (int i = 0; i < p.length(); i++) { 14 | // pCount[p.charAt(i) - 'a']++; 15 | // } 16 | // int left = 0; int right = 0; 17 | // int[] sCount = new int[26] ; 18 | // while (right < s.length()) { 19 | // if(right - left + 1 > p.length()) { 20 | // sCount[s.charAt(left) - 'a']--; 21 | // left++; 22 | // } 23 | // sCount[s.charAt(right) - 'a']++; 24 | // if(right - left + 1 == p.length()) { 25 | // boolean isEqual = true; 26 | // for (int i = 0; i < 26; i++) { 27 | // if(pCount[i] != sCount[i]) { 28 | // isEqual = false; 29 | // break; 30 | // } 31 | // } 32 | // if (isEqual) { 33 | // res.add(left); 34 | // } 35 | // } 36 | // right++; 37 | // } 38 | // return res; 39 | List res = new ArrayList<>(); 40 | int sLen = s.length(), pLen = p.length(); 41 | if (sLen < pLen) { 42 | return res; 43 | } 44 | HashMap hs = new HashMap<>(); 45 | for (int i = 0; i < pLen; i++) { 46 | char c = p.charAt(i); 47 | hs.put(c, hs.getOrDefault(c, 0) + 1); 48 | } 49 | int required = hs.size(), current = 0; 50 | HashMap window = new HashMap<>(); 51 | int left = 0, right = 0; 52 | while (right < sLen) { 53 | char word = s.charAt(right); 54 | if (hs.containsKey(word)) { 55 | window.put(word, window.getOrDefault(word, 0) + 1); 56 | if (window.get(word).equals(hs.get(word))) { 57 | current++; 58 | } 59 | } 60 | while (right - left + 1 >= pLen) { 61 | char tmp = s.charAt(left); 62 | if (current == required) { 63 | res.add(left); 64 | } 65 | if (hs.containsKey(tmp)) { 66 | if (window.get(tmp).equals(hs.get(tmp))) { 67 | current--; 68 | } 69 | window.put(tmp, window.get(tmp) - 1); 70 | } 71 | left++; 72 | } 73 | right++; 74 | } 75 | return res; 76 | } 77 | } 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /Leetcode/栈、队列、HashMap()、HashSet()/560. 和为 K 的子数组.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 方法1:暴力,两重for循环,第一重for循环代表开头,第二重for循环代表结尾,遍历求和, 时间复杂度O(n^2) 4 | // 方法2:前缀和+HashMap,类似于两数之和,用hashmap可以降时间复杂度 5 | // 6 | public int subarraySum(int[] nums, int k) { 7 | int pre = 0; 8 | int count = 0; 9 | HashMap hs = new HashMap<>(); // hashput存储的是前缀和 10 | hs.put(0,1); //连续子数组,什么都没有默认为0 11 | for (int i = 0; i < nums.length; i++) { 12 | pre += nums[i]; 13 | if (hs.containsKey(pre - k)) { 14 | count += hs.get(pre - k); 15 | } 16 | hs.put(pre, hs.getOrDefault(pre, 0) + 1); 17 | } 18 | return count; 19 | } 20 | } 21 | ``` 22 | 23 | -------------------------------------------------------------------------------- /Leetcode/深度优先搜索和广度优先搜索/200. 岛屿数量.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 深度优先搜索:往一个方向遍历,走到头就回溯,再往另外一个方向遍历 4 | // 广度优先搜索:从一个位置开始,向他的相邻位置搜索,逐层进行,用队列实现,需要用一个boolean数组进行标记,避免重复 5 | 6 | 7 | // 岛屿数量这个题是深度优先搜索和广度优先搜索的基础题 8 | // 深度优先代码相对简单,遍历字符数组,过程碰到'1'就进行深搜,深搜的过程只要判断是否越界,元素是否为'1',对相邻'1'进行深度优先 9 | int[][] pos = {{1,0}, {-1,0},{0,1},{0,-1}}; 10 | public int numIslands(char[][] grid) { 11 | int m = grid.length; 12 | int n = grid[0].length; 13 | int num = 0; 14 | boolean[][] visited = new boolean[m][n]; 15 | for (int i = 0; i < grid.length; i++) { 16 | for (int j = 0; j < grid[0].length; j++) { 17 | if ( grid[i][j] == '1') { 18 | dfs(grid,i,j); 19 | num++; 20 | } 21 | } 22 | } 23 | return num; 24 | } 25 | public void dfs(char[][] grid, int row, int col) { 26 | if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col] == '0') { 27 | return; 28 | } 29 | 30 | grid[row][col] = '0'; 31 | dfs(grid, row + 1, col); 32 | dfs(grid, row - 1, col); 33 | dfs(grid, row, col + 1); 34 | dfs(grid, row, col - 1); 35 | } 36 | 37 | // public void dfs (char[][] grid, int row, int col) { 38 | // if (row < 0 || row >= grid.length || col < 0 || col >= grid[0].length || grid[row][col] == '0') { 39 | // return; 40 | // } 41 | // grid[row][col] = '0'; 42 | // dfs(grid, row - 1, col); 43 | // dfs(grid, row + 1, col); 44 | // dfs(grid, row, col + 1); 45 | // dfs(grid, row, col - 1); 46 | // } 47 | public void bfs(char[][] grid, int row, int col, boolean[][] visited) { 48 | grid[row][col] = '0'; 49 | visited[row][col] = true; 50 | Queue que = new LinkedList<>(); 51 | que.offer(new int[]{row,col}); 52 | while (!que.isEmpty()) { 53 | int[] cur = que.poll(); 54 | int x = cur[0]; 55 | int y = cur[1]; 56 | for (int[] tmp : pos) { 57 | int curX = tmp[0] + x; 58 | int curY = tmp[1] + y; 59 | if (curX < 0 || curX >= grid.length || curY < 0 || curY >= grid[0].length || grid[curX][curY] == '0' || visited[curX][curY]) { 60 | continue; 61 | } 62 | que.offer(new int[]{curX, curY}); 63 | visited[curX][curY] = true; 64 | } 65 | 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | -------------------------------------------------------------------------------- /Leetcode/深度优先搜索和广度优先搜索/79. 单词搜索.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | //深度优先+回溯, 4 | 5 | int[][] directions = {{0,1},{0,-1},{1,0},{-1,0}}; 6 | public boolean exist(char[][] board, String word) { 7 | int row = board.length; 8 | int col = board[0].length; 9 | boolean[][] visited = new boolean[row][col]; 10 | for (int i = 0; i < row; i++) { 11 | for (int j = 0; j < col; j++) { 12 | if (dfs(i,j,board,word,0,visited)) { 13 | return true; 14 | } 15 | } 16 | } 17 | return false; 18 | } 19 | 20 | public boolean dfs(int row, int col, char[][] board, String word, int index, boolean[][] visited) { 21 | if (index == word.length()) { 22 | return true; 23 | } 24 | if (row < 0 || row >= board.length || col < 0 || col >= board[0].length || word.charAt(index) != board[row][col] || visited[row][col] == true) { 25 | return false; 26 | } 27 | visited[row][col] = true; // 对当前的路径进行标记,避免重复访问 28 | for (int[] dir : directions) { 29 | int curRow = row + dir[0]; 30 | int curRol = col + dir[1]; 31 | if (dfs(curRow, curRol, board, word, index + 1, visited)) { 32 | return true; 33 | } 34 | } 35 | visited[row][col] = false; // 回溯,使得其他路径可以访问当前路径的元素 36 | return false; 37 | 38 | } 39 | } 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /Leetcode/贪心/253. 会议室II.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 矮个子插队,高个子看不见;所以我们可以先安排高个子的位置,再通过插队的方式安排矮个子的位置 4 | // 身高从大到小排(身高相同k小的站前面) 5 | //先按照身高h降序排序,这样处理时可以确保当前插入的元素之前的所有元素身高都不小于它,从而简化位置的确定。 6 | //如果身高相同,则按照序号k升序排序,因为身高相同的情况下,序号小的人应该排在前面 7 | public int[][] reconstructQueue(int[][] people) { 8 | Arrays.sort(people, (a,b) -> b[0] == a[0]? a[1] - b[1] : b[0] - a[0]); 9 | List list = new ArrayList<>(); 10 | for (int i = 0; i < people.length; i++) { 11 | list.add(people[i][1], people[i]); 12 | } 13 | 14 | return list.toArray(new int[list.size()][2]); // list.toArray将list转成array 15 | } 16 | } 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /Leetcode/贪心/406. 根据身高重建队列.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 矮个子插队,高个子看不见;所以我们可以先安排高个子的位置,再通过插队的方式安排矮个子的位置 4 | // 身高从大到小排(身高相同k小的站前面) 5 | //先按照身高h降序排序,这样处理时可以确保当前插入的元素之前的所有元素身高都不小于它,从而简化位置的确定。 6 | //如果身高相同,则按照序号k升序排序,因为身高相同的情况下,序号小的人应该排在前面 7 | public int[][] reconstructQueue(int[][] people) { 8 | Arrays.sort(people, (a,b) -> b[0] == a[0]? a[1] - b[1] : b[0] - a[0]); 9 | List list = new ArrayList<>(); 10 | for (int i = 0; i < people.length; i++) { 11 | list.add(people[i][1], people[i]); 12 | } 13 | 14 | return list.toArray(new int[list.size()][2]); // list.toArray将list转成array 15 | } 16 | } 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /Leetcode/贪心/621. 任务调度器.md: -------------------------------------------------------------------------------- 1 | ```java 2 | class Solution { 3 | // 贪心 4 | // 先执行次数最多的任务,可以充分利用任务之间的间隔。时间最短 5 | // 每次执行次数最多的任务,考虑优先队列,大根堆 6 | public int leastInterval(char[] tasks, int n) { 7 | PriorityQueue pq = new PriorityQueue<>((a,b) -> b-a); 8 | int[] count = new int[26]; 9 | int res = 0; 10 | for (int i = 0; i < tasks.length; i++) { //对任务计数 11 | count[tasks[i] - 'A']++; 12 | } 13 | for (int tmp : count) { // 出现次数大于0的元素才意味着任务需要完成 14 | if (tmp > 0) { 15 | pq.offer(tmp); 16 | } 17 | 18 | } 19 | while (!pq.isEmpty()) { 20 | List list = new ArrayList<>(); // list记录一个间隔内完成的任务 21 | for (int i = 0; i <= n; i++) { 22 | if (!pq.isEmpty()) { 23 | list.add(pq.poll()); 24 | } 25 | } 26 | for (int t : list) { 27 | if ( --t > 0) { 28 | pq.offer(t); 29 | } 30 | } 31 | res += pq.isEmpty() ? list.size() : n + 1; 32 | } 33 | return res; 34 | } 35 | // public int leastInterval(char[] tasks, int n) { 36 | // // 统计每种任务的出现次数 37 | // int[] count = new int[26]; 38 | // for (char task : tasks) { 39 | // count[task - 'A']++; 40 | // } 41 | 42 | // // 创建一个优先队列,存储每种任务的下一次执行时间 43 | // PriorityQueue pq = new PriorityQueue<>((a, b) -> b - a); 44 | // for (int c : count) { 45 | // if (c > 0) { 46 | // pq.offer(c); 47 | // } 48 | // } 49 | 50 | // int time = 0; 51 | // while (!pq.isEmpty()) { 52 | // // 创建一个临时列表,存储当前时间可以执行的任务 53 | // java.util.List temp = new java.util.ArrayList<>(); 54 | // for (int i = 0; i <= n; i++) { 55 | // if (!pq.isEmpty()) { 56 | // temp.add(pq.poll()); 57 | // } 58 | // } 59 | 60 | // // 将当前时间可以执行的任务加回优先队列,并更新它们的下一次执行时间 61 | // for (int t : temp) { 62 | // if (--t > 0) { 63 | // pq.offer(t); 64 | // } 65 | // } 66 | 67 | // // 如果优先队列为空,说明所有任务已经完成,返回当前时间 68 | // time += pq.isEmpty() ? temp.size() : n + 1; 69 | // } 70 | 71 | // return time; 72 | // } 73 | } 74 | ``` 75 | 76 | -------------------------------------------------------------------------------- /Leetcode/链表/114. 二叉树展开为链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for a binary tree node. 4 | * public class TreeNode { 5 | * int val; 6 | * TreeNode left; 7 | * TreeNode right; 8 | * TreeNode() {} 9 | * TreeNode(int val) { this.val = val; } 10 | * TreeNode(int val, TreeNode left, TreeNode right) { 11 | * this.val = val; 12 | * this.left = left; 13 | * this.right = right; 14 | * } 15 | * } 16 | */ 17 | class Solution { 18 | // 用一个列表存储先序遍历的结果,再构建单链表,将列表中的节点的左指针赋值为空,右指针指向列表中的下一个位置 19 | List res = new ArrayList<>(); 20 | public void flatten(TreeNode root) { 21 | if (root == null) { 22 | return; 23 | } 24 | preOrder(root); 25 | for (int i = 0; i < res.size() - 1; i++) { // 这里是小于 res.size() - 1,确保不会数组越界 26 | TreeNode node = res.get(i); 27 | node.left = null; 28 | node.right = res.get(i + 1); 29 | } 30 | } 31 | public void preOrder(TreeNode root) { 32 | if (root == null) { 33 | return; 34 | } 35 | res.add(root); 36 | preOrder(root.left); 37 | preOrder(root.right); 38 | } 39 | 40 | } 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /Leetcode/链表/141. 环形链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode(int x) { 8 | * val = x; 9 | * next = null; 10 | * } 11 | * } 12 | */ 13 | //环形链表,快慢指针 14 | public class Solution { 15 | // Floyd判圈法 16 | public boolean hasCycle(ListNode head) { 17 | if (head == null || head.next == null) { 18 | return false; 19 | } 20 | ListNode fast = head; 21 | ListNode slow = head; 22 | while (fast != null && fast.next != null) { 23 | fast = fast.next.next; 24 | slow = slow.next; 25 | if (fast == slow) { 26 | return true; 27 | } 28 | } 29 | return false; 30 | } 31 | } 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /Leetcode/链表/142. 环形链表 II.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode(int x) { 8 | * val = x; 9 | * next = null; 10 | * } 11 | * } 12 | */ 13 | public class Solution { 14 | // floyd判圈法,快慢指针 15 | // 设置快慢两个指针都指向头结点 16 | // 让一个快指针一次走两步,慢指针一次走一步 17 | // 两个指针相遇后,将快指针移到起点,一次走一步,相遇的位置就是链表中环的起始位置 18 | public ListNode detectCycle(ListNode head) { 19 | if (head == null || head.next == null) { 20 | return null; 21 | } 22 | ListNode fast = head; 23 | ListNode slow = head; 24 | while (fast != null && fast.next != null) { // fast需要走两步,所以需要判断fast != null && fast.next != null 25 | fast = fast.next.next; 26 | slow = slow.next; 27 | if (slow == fast) { 28 | fast = head; 29 | while (fast != slow) { 30 | fast = fast.next; 31 | slow = slow.next; 32 | } 33 | return slow; 34 | } 35 | } 36 | return null; 37 | } 38 | } 39 | ``` 40 | 41 | -------------------------------------------------------------------------------- /Leetcode/链表/146. LRU 缓存.md: -------------------------------------------------------------------------------- 1 | ```java 2 | // LRU缓存机制可以通过哈希表辅以双向链表实现 3 | // 使用双向链表的理由:方便删除节点,O(1)时间 4 | // 双向链表中存储key的理由:size > capcity 时可以O(1)的时间删除哈希表中的键值对 5 | // O(1)的get和put,用hashmap实现 6 | // 基础还是链表的增删改查,只是这里是双向链表作为hashmap的value 7 | // 链表的中节点的移动和删除修改通过双向链表可以在O(1)的时间内实现 8 | // 每访问一个节点都要将其移动链表头。整个过程分为两步:1.双向链表中删除该节点,2.添加到头部 9 | // 链表中的元素个数超过capcity后需要删除链表尾的节点 10 | class LRUCache { 11 | class DLinkedNode { 12 | int key; 13 | int value; 14 | DLinkedNode prev; 15 | DLinkedNode next; 16 | DLinkedNode() {} 17 | DLinkedNode(int key, int value) { 18 | this.key = key; 19 | this.value = value; 20 | } 21 | } 22 | private int size; 23 | private int capacity; 24 | private DLinkedNode head = new DLinkedNode(); 25 | private DLinkedNode tail = new DLinkedNode(); 26 | private HashMap hs = new HashMap<>(); 27 | LRUCache(int capacity) { 28 | size = 0; 29 | this.capacity = capacity; 30 | head.next = tail; 31 | tail.prev = head; 32 | } 33 | public int get (int key) { 34 | DLinkedNode node = hs.get(key); 35 | if (node == null) { 36 | return -1; 37 | } 38 | moveToHead(node); 39 | return node.value; 40 | } 41 | public void put(int key, int value) { 42 | DLinkedNode node = hs.get(key); 43 | if (node == null) { 44 | node = new DLinkedNode(key, value); 45 | hs.put(key, node); 46 | addToHead(node); 47 | size++; 48 | if (size > capacity) { 49 | DLinkedNode tail = removeTail(); 50 | hs.remove(tail.key); 51 | --size; 52 | } 53 | } else { 54 | node.value = value; 55 | moveToHead(node); 56 | 57 | } 58 | } 59 | public void addToHead(DLinkedNode node) { // 添加到头结点,意味着新建了一个节点 60 | node.next = head.next; 61 | node.prev = head; 62 | head.next.prev = node; 63 | head.next = node; 64 | } 65 | public void removeNode(DLinkedNode node) { // 删除节点 66 | node.prev.next = node.next; 67 | node.next.prev = node.prev; 68 | } 69 | public void moveToHead(DLinkedNode node) { // 节点已经存在,改变他的值,需要将该节点移动到头的位置 70 | removeNode(node); 71 | addToHead(node); 72 | } 73 | public DLinkedNode removeTail() { // size > capacity 需要删除末尾节点 74 | DLinkedNode node = tail.prev; 75 | removeNode(node); 76 | return node; 77 | } 78 | } 79 | 80 | // java自带的LinkedHashMap已经实现了LRU缓存 81 | // class LRUCache extends LinkedHashMap { 82 | // private int capacity; 83 | // public LRUCache(int capacity) { 84 | // super(capacity, 0.75F, true); //调用父类的构造方法 85 | // this.capacity = capacity; 86 | // } 87 | // public int get(int key) { 88 | // return super.getOrDefault(key, -1); 89 | // } 90 | // public void put(int key, int value) { 91 | // super.put(key, value); 92 | // } 93 | // @Override 94 | // protected boolean removeEldestEntry(Map.Entry eldest){ 95 | // return size() > capacity; 96 | // } 97 | 98 | // } 99 | 100 | 101 | /** 102 | * Your LRUCache object will be instantiated and called as such: 103 | * LRUCache obj = new LRUCache(capacity); 104 | * int param_1 = obj.get(key); 105 | * obj.put(key,value); 106 | */ 107 | ``` 108 | 109 | -------------------------------------------------------------------------------- /Leetcode/链表/148. 排序链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | // 排序,归并排序 13 | class Solution { 14 | // 归并排序注意中点的划分 15 | public ListNode sortList(ListNode head) { 16 | if (head == null || head.next == null) { 17 | return head; 18 | } 19 | ListNode fast = head.next; 20 | ListNode slow = head; 21 | while ( fast!= null && fast.next != null ) { 22 | fast = fast.next.next; 23 | slow = slow.next; 24 | } 25 | ListNode tmp = slow.next; 26 | slow.next = null; 27 | ListNode left = sortList(head); 28 | ListNode right = sortList(tmp); 29 | ListNode dummy = new ListNode(-100001); 30 | ListNode res = dummy; 31 | while (left != null && right != null) { 32 | if (left.val <= right.val) { 33 | res.next = left; 34 | left = left.next; 35 | } else { 36 | res.next = right; 37 | right = right.next; 38 | } 39 | res = res.next; 40 | } 41 | res.next = left == null ? right : left; 42 | return dummy.next; 43 | } 44 | } 45 | ``` 46 | 47 | -------------------------------------------------------------------------------- /Leetcode/链表/160. 相交链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode(int x) { 8 | * val = x; 9 | * next = null; 10 | * } 11 | * } 12 | */ 13 | public class Solution { 14 | // 计算两个链表的长度,让长的链表先走Math.abs(lenA - lenB) 15 | // 之后两个链表的指针每次都指向下一个位置 16 | // return 指针指向同一个位置的节点。 17 | public ListNode getIntersectionNode(ListNode headA, ListNode headB) { 18 | //链表相交节点代表值相等 19 | int lenA = 1, lenB = 1; 20 | ListNode curA = headA; 21 | ListNode curB = headB; 22 | while (curA != null) { 23 | curA = curA.next; 24 | lenA++; 25 | } 26 | while (curB != null) { 27 | curB = curB.next; 28 | lenB++; 29 | } 30 | curA = headA; 31 | curB = headB; 32 | int sub = lenA > lenB ? lenA - lenB : lenB - lenA; 33 | if (lenA > lenB) { 34 | int count = 0; 35 | while (count < sub) { 36 | curA = curA.next; 37 | count++; 38 | } 39 | while (curA != curB && curA != null && curB != null) { 40 | curA = curA.next; 41 | curB = curB.next; 42 | } 43 | return curA; 44 | } else { 45 | int count = 0; 46 | while (count < sub) { 47 | curB= curB.next; 48 | count++; 49 | } 50 | while (curA != curB && curA != null && curB != null) { 51 | curA = curA.next; 52 | curB = curB.next; 53 | } 54 | return curB; 55 | } 56 | } 57 | } 58 | ``` 59 | 60 | -------------------------------------------------------------------------------- /Leetcode/链表/19. 删除链表的倒数第 N 个结点.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | // 思路:1. 得出链表的长度,然后走size - n - 1,接着进行删除 14 | // 2. 快慢指针,让快指针先走n步,之后慢指针再出发, 15 | // 当快指针走到结尾的时候,慢指针指向的就是倒数第n个节点的前一个节点 16 | public ListNode removeNthFromEnd(ListNode head, int n) { 17 | ListNode dummy = new ListNode(-1,head); 18 | ListNode fast = dummy; 19 | ListNode low = dummy; 20 | int count = 0; 21 | while (fast.next != null) { // fast走到最后一个节点需要停下来 22 | fast = fast.next; 23 | count++; 24 | if (count >= n + 1) { 25 | low = low.next; 26 | } 27 | } 28 | low.next = low.next.next; 29 | return dummy.next; 30 | // int size = 1; 31 | 32 | // ListNode cur = head; 33 | // while (cur.next != null) { 34 | // cur = cur.next; 35 | // size++; 36 | // } 37 | // if (n == size) return head.next; // 特判 n == size 的情况 38 | // cur = head; 39 | // for (int i = 1; i < size - n; i++) { 40 | // cur = cur.next; 41 | // } 42 | // cur.next = cur.next.next; 43 | // return head; 44 | } 45 | 46 | } 47 | ``` 48 | 49 | -------------------------------------------------------------------------------- /Leetcode/链表/2. 两数相加.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | // 用一个变量记录进位 14 | // 15 | public ListNode addTwoNumbers(ListNode l1, ListNode l2) { 16 | ListNode dummyRes = new ListNode(); 17 | ListNode cur = dummyRes; 18 | int yushu = 0, sum = 0; 19 | while (l1 != null || l2 != null) { 20 | cur.next = new ListNode(); 21 | int var1 = (l1 == null) ? 0 : l1.val; 22 | int var2 = (l2 == null) ? 0 : l2.val; 23 | sum = var1 + var2 + yushu; 24 | cur.next = new ListNode(sum % 10); 25 | cur = cur.next; 26 | yushu = sum / 10; 27 | if (l1 != null) { 28 | l1 = l1.next; 29 | } 30 | if (l2 != null) { 31 | l2 = l2.next; 32 | } 33 | 34 | } 35 | if (yushu != 0) { 36 | cur.next = new ListNode(yushu); 37 | } 38 | return dummyRes.next; 39 | } 40 | } 41 | ``` 42 | 43 | -------------------------------------------------------------------------------- /Leetcode/链表/206. 反转链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | // 链表的题往往使用虚拟头结点的方法 ListNode dummy 让虚拟头结点指向head,避免对head特判 14 | // 链表的题删除链表、翻转节点、添加节点都要用双指针 15 | public ListNode reverseList(ListNode head) { 16 | ListNode pre = null; 17 | ListNode cur = head; 18 | while (cur != null) { 19 | ListNode tmp = cur.next; 20 | cur.next = pre; 21 | pre = cur; 22 | cur = tmp; 23 | } 24 | return pre; 25 | } 26 | } 27 | ``` 28 | 29 | -------------------------------------------------------------------------------- /Leetcode/链表/21. 合并两个有序链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | public ListNode mergeTwoLists(ListNode list1, ListNode list2) { 14 | //创建一个新的链表,比较两个链表将小的值加入这个链表; 15 | //创建一个新的链表,比较两个链表将小的值加入这个链表; 16 | // 类似于归并排序的合并过程 17 | ListNode dummy = new ListNode(); 18 | ListNode cur = dummy; 19 | while (list1 != null && list2 != null) { 20 | if (list1.val <= list2.val) { 21 | cur.next = list1; 22 | list1 = list1.next; 23 | } else { 24 | cur.next = list2; 25 | list2 = list2.next; 26 | } 27 | cur = cur.next; 28 | } 29 | cur.next = (list1 == null) ? list2 : list1; 30 | return dummy.next; 31 | 32 | } 33 | } 34 | ``` 35 | 36 | -------------------------------------------------------------------------------- /Leetcode/链表/23. 合并 K 个升序链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | // 创建一个最小堆,存储所有的链表的头结点,每次从链表中取出一个节点,并将这个节点的下一个节点放入最小堆中 14 | // 这样就可以保证每次取得当前k个节点的最小值 15 | public ListNode mergeKLists(ListNode[] lists) { 16 | PriorityQueue pq = new PriorityQueue<>((a,b) -> a.val - b.val); 17 | ListNode dummy = new ListNode(-1); 18 | ListNode cur = dummy; 19 | for (ListNode node :lists) { 20 | if (node != null) { // 需要判断是否为空,否则会出现空指针异常,优先队列不能加入空值 21 | pq.offer(node); 22 | } 23 | } 24 | while (!pq.isEmpty()) { 25 | cur.next = pq.poll(); 26 | cur = cur.next; 27 | if (cur.next != null) { 28 | pq.offer(cur.next); 29 | } 30 | } 31 | return dummy.next; 32 | } 33 | } 34 | ``` 35 | 36 | -------------------------------------------------------------------------------- /Leetcode/链表/234. 回文链表.md: -------------------------------------------------------------------------------- 1 | ```java 2 | /** 3 | * Definition for singly-linked list. 4 | * public class ListNode { 5 | * int val; 6 | * ListNode next; 7 | * ListNode() {} 8 | * ListNode(int val) { this.val = val; } 9 | * ListNode(int val, ListNode next) { this.val = val; this.next = next; } 10 | * } 11 | */ 12 | class Solution { 13 | public boolean isPalindrome(ListNode head) { 14 | //思路:创建一个链表和原链表相同,再翻转,然后比较;两条链表 15 | if (head == null) { 16 | return true; 17 | } 18 | ListNode headNew = new ListNode(head.val); 19 | ListNode curNew = headNew; 20 | ListNode cur = head; 21 | while (cur.next != null) { // 复制链表 22 | curNew.next = new ListNode(cur.next.val); 23 | cur = cur.next; 24 | curNew = curNew.next; 25 | } 26 | ListNode pre = null; 27 | curNew = headNew; 28 | while (curNew != null) { // 翻转链表 29 | ListNode tmp = curNew.next; 30 | curNew.next = pre; 31 | pre = curNew; 32 | curNew = tmp; 33 | } 34 | curNew = pre; 35 | cur = head; 36 | while (curNew != null) { 37 | if (curNew.val != cur.val) { 38 | return false; 39 | } else { 40 | curNew = curNew.next; 41 | cur = cur.next; 42 | } 43 | 44 | } 45 | return true; 46 | } 47 | } 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LeetCode_Hot100_Java_Solution 2 | ## 本文将Leetcode hot 100题目按照类型分类。部分题目可能属于多个类型。 3 | 刷leetcode hot 100以前需要了解一些基础知识: 4 | 5 | - 常见排序:冒泡排序,插入排序,选择排序,归并排序,快速排序,堆排序。 6 | 时间复杂度为O(nlogn):归并排序和堆排序。快速排序时间复杂度最好的情况下是O(nlogn),最坏的情况下是O(n^2)。 7 | - 字符串的常用方法:length(),substring(),spilt()。 StringBuffer()的常用方法:deleteCharAt(),reverse(),toString(),append(),insert。 8 | - 常用数据结构及其常用方法:List,HashMap,HashSet,PriorityQueue,stack,queue。 9 | - Arrays.sort()自定义排序,PriorityQueue()自定义排序。 10 | 11 | | 数据结构 | 常用方法 | 12 | | :---- | :---- | 13 | | List | add(),remove() | 14 | | HashMap | put(),size(),remove(),containsKey() | 15 | | HashSet | add(),remove(),size(),contains() | 16 | | PriorityQueue | poll(),offer(),isEmpty(),size(),peek() | 17 | | stack | push(),size(),pop(),isEmpty(),peek() | 18 | | queue | poll(),offer(),isEmpty(),size(),peek() | 19 | 20 | ### dp 21 | 22 | - dp数组和下标的含义,递推公式,初始化,遍历顺序。 23 | 24 | - 01背包和完全背包。 25 | 26 | | 题号 | 题目 | 题解 | 27 | | :--- | :------------------------------------------------------------ | :-------------------------------------------------------- | 28 | | 198 | [打家劫舍](https://leetcode.cn/problems/house-robber/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/198.%20打家劫舍.md) | 29 | | 238 | [除自身数组以外的乘积](https://leetcode.cn/problems/product-of-array-except-self/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/238.%20除自身以外数组的乘积.md) | 30 | | 221 | [最大正方形](https://leetcode.cn/problems/maximal-square/description/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/dp/221.%20最大正方形.md) | 31 | | 152 | [乘积最大子数组](https://leetcode.cn/problems/maximum-product-subarray/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/152.%20乘积最大子数组.md) | 32 | | 139 | [单词拆分](https://leetcode.cn/problems/word-break/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/139.%20单词拆分.md) | 33 | | 647 | [回文子串](https://leetcode.cn/problems/palindromic-substrings/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/647.%20回文子串.md) | 34 | | 322 | [零钱兑换](https://leetcode.cn/problems/coin-change/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/322.%20零钱兑换.md) | 35 | | 494 | [目标和](https://leetcode.cn/problems/coin-change/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/494.%20目标和.md) | 36 | | 416 | [分割等和子集](https://leetcode.cn/problems/partition-equal-subset-sum/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/416.%20分割等和子集.md) | 37 | | 337 | [打家劫舍 III](https://leetcode.cn/problems/house-robber-iii/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/337.%20打家劫舍%20III.md) | 38 | | 312 | [戳气球](https://leetcode.cn/problems/burst-balloons/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/312.%20戳气球.md) | 39 | | 309 | [买卖股票的最佳时期含冷冻期](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/309.%20买卖股票的最佳时机含冷冻期.md) | 40 | | 300 | [最长递增子序列](https://leetcode.cn/problems/longest-increasing-subsequence/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/300.%20最长递增子序列.md) | 41 | | 279 | [完全平方数](https://leetcode.cn/problems/perfect-squares/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/279.%20完全平方数.md) | 42 | | 42 | [接雨水](https://leetcode.cn/problems/trapping-rain-water/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/42.%20接雨水.md) | 43 | | 32 | [最长有效括号](https://leetcode.cn/problems/longest-valid-parentheses/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/32.%20最长有效括号.md) | 44 | | 10 | [正则表达式匹配](https://leetcode.cn/problems/regular-expression-matching/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/10.%20正则表达式匹配.md) | 45 | | 5 | [最长回文子串](https://leetcode.cn/problems/longest-palindromic-substring/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/5.%20最长回文子串.md) | 46 | | 96 | [不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/96.%20不同的二叉搜索树.md) | 47 | | 85 | [最大矩形](https://leetcode.cn/problems/maximal-rectangle/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/85.%20最大矩形.md) | 48 | | 72 | [编辑距离](https://leetcode.cn/problems/edit-distance/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/72.%20编辑距离.md) | 49 | | 70 | [爬楼梯](https://leetcode.cn/problems/climbing-stairs/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/70.%20爬楼梯.md) | 50 | | 62 | [不同路径](https://leetcode.cn/problems/shortest-unsorted-continuous-subarray/description/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/62.%20不同路径.md) | 51 | | 53 | [最大子数组和](https://leetcode.cn/problems/maximum-subarray/?envType=problem-list-v2&envId=2cktkvj&) | [Java](Leetcode/dp/53.%20最大子数组和.md) | 52 | 53 | ### 链表 54 | 55 | - 链表的题目往往使用虚拟头结点的方法,以避免对头结点为空的情况进行特判。 56 | - 做链表的题目是,我们需要记住的是链表的增删节点都是使用的双指针。 57 | 58 | | **题号** | **题目** | 题解 | 59 | | :-------- | :------------------------------------------------------------ | :-------------------------------------------------------- | 60 | | 160 | [相交链表](https://leetcode.cn/problems/intersection-of-two-linked-lists/description/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/160.%20相交链表.md) | 61 | | 234 | [回文链表](https://leetcode.cn/problems/palindrome-linked-list/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/234.%20回文链表.md) | 62 | | 206 | [反转链表](https://leetcode.cn/problems/reverse-linked-list/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/206.%20反转链表.md) | 63 | | 148 | [排序链表 ](https://leetcode.cn/problems/sort-list/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/148.%20排序链表.md) | 64 | | 146 | [LRU 缓存 ](https://leetcode.cn/problems/lru-cache/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/146.%20LRU%20缓存.md) | 65 | | 142 | [环形链表 II](https://leetcode.cn/problems/linked-list-cycle-ii/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/142.%20环形链表%20II.md) | 66 | | 141 | [环形链表](https://leetcode.cn/problems/linked-list-cycle-ii/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/141.%20环形链表.md) | 67 | | 23 | [ 合并 K 个升序链表 ](https://leetcode.cn/problems/merge-k-sorted-lists/description/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/23.%20合并%20K%20个升序链表.md) | 68 | | 21 | [合并两个有序链表](https://leetcode.cn/problems/merge-two-sorted-lists/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/链表/21.%20合并两个有序链表.md) | 69 | | 114 | [二叉树展开为链表](https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/) | [Java](Leetcode/链表/114.%20二叉树展开为链表.md) | 70 | | 19 | [删除链表的倒数第 N 个结点](https://leetcode.cn/problems/remove-nth-node-from-end-of-list/) | [Java](Leetcode/链表/19.%20删除链表的倒数第%20N%20个结点.md) | 71 | | 2 | [两数相加](https://leetcode.cn/problems/add-two-numbers/) | [Java](Leetcode/链表/2.%20两数相加.md) | 72 | 73 | ### 单调栈 74 | 75 | - 单调栈按照从栈底到栈顶的大小变化可以分为单调递增栈和单调递减栈。 76 | - 单调栈中存储的是元素的索引。 77 | 78 | | 题号 | 题目 | 题解 | 79 | | :---- | :------------------------------------------------------------ | :--------------------------------------------------- | 80 | | 739 | [ 每日温度 ](https://leetcode.cn/problems/daily-temperatures/submissions/543113746/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/单调栈/739.%20每日温度.md) | 81 | | 84 | [柱状图中最大的矩形](https://leetcode.cn/problems/largest-rectangle-in-histogram/) | [Java](Leetcode/单调栈/84.%20柱状图中最大的矩形.md) | 82 | 83 | ### 构建数据结构 84 | 85 | - 构建特殊数据结构。 86 | 87 | | 题号 | 题目 | 题解 | 88 | | :---- | :------------------------------------------------------------ | :---------------------------------------------------------- | 89 | | 207 | [课程表 ](https://leetcode.cn/problems/course-schedule/description/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/构建数据结构/207.%20课程表.md) | 90 | | 399 | [除法求值](https://leetcode.cn/problems/evaluate-division/) | [Java](Leetcode/构建数据结构/399.%20除法求值.md) | 91 | | 208 | [实现 Trie (前缀树)](https://leetcode.cn/problems/implement-trie-prefix-tree/) | [Java](Leetcode/构建数据结构/208.%20实现%20Trie%20(前缀树).md) | 92 | 93 | ### 二进制 94 | 95 | - ^异或:任何数^自身 = 0,^0不变。 96 | - &1:判断末尾数是否为1。 97 | - <<:向左位移1位,>>:向右位移1位。 98 | 99 | | 题号 | 题目 | 题解 | 100 | | :---- | :------------------------------------------------------------ | :-------------------------------------------- | 101 | | 461 | [汉明距离](https://leetcode.cn/problems/hamming-distance/description/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/二进制/461.%20汉明距离.md) | 102 | | 338 | [比特位计数](https://leetcode.cn/problems/counting-bits/) | [Java](Leetcode/二进制/338.%20比特位计数.md) | 103 | 104 | ### 贪心 105 | 106 | - 通过局部最优,得到全局最优,需要会自定义排序,建议使用lamda表达式。 107 | 108 | | 题号 | 题目 | 题解 | 109 | | :---- | :------------------------------------------------------------ | :------------------------------------------------ | 110 | | 406 | [根据身高重建队列](https://leetcode.cn/problems/queue-reconstruction-by-height/) | [Java](Leetcode/贪心/406.%20根据身高重建队列.md) | 111 | | 253 | [会议室II](https://leetcode.cn/problems/meeting-rooms-ii/description/?envType=problem-list-v2&envId=2cktkvj) | [Java](Leetcode/贪心/253.%20会议室II.md) | 112 | | 621 | [任务调度器](https://leetcode.cn/problems/task-scheduler/) | [Java](Leetcode/贪心/621.%20任务调度器.md) | 113 | 114 | ### 优先队列 115 | 116 | - PriorityQueue是底层是堆排序。 117 | 118 | | 题目 | 题号 | 题解 | 119 | | :---- | :------------------------------------------------------------ | :---------------------------------------------------- | 120 | | 239 | [滑动窗口最大值](https://leetcode.cn/problems/sliding-window-maximum/) | [Java](Leetcode/优先队列/239.%20滑动窗口最大值.md) | 121 | | 23 | [合并 K 个升序链表](https://leetcode.cn/problems/merge-k-sorted-lists/) | [Java](Leetcode/优先队列/23.%20合并%20K%20个升序链表.md) | 122 | 123 | ### 回溯 124 | 125 | - 回溯三要素:1.传递的参数和返回类型;2.终止条件;3.回溯的逻辑。 126 | 127 | | 题号 | 题目 | 题解 | 128 | | :---- | :------------------------------------------------------------ | :------------------------------------------------- | 129 | | 22 | [括号生成](https://leetcode.cn/problems/generate-parentheses/) | [Java](Leetcode/回溯/22.%20括号生成.md) | 130 | | 46 | [全排列](https://leetcode.cn/problems/permutations/) | [Java](Leetcode/回溯/46.%20全排列.md) | 131 | | 17 | [电话号码的字母组合](https://leetcode.cn/problems/letter-combinations-of-a-phone-number/) | [Java](Leetcode/回溯/17.%20电话号码的字母组合.md) | 132 | | 78 | [ 子集](https://leetcode.cn/problems/subsets/) | [Java](Leetcode/回溯/78.%20子集.md) | 133 | | 39 | [组合总和](https://leetcode.cn/problems/combination-sum/) | [Java](Leetcode/回溯/39.%20组合总和.md) | 134 | 135 | ### 数组 136 | 137 | - 需要会二分法的使用和双指针、滑动窗口。 138 | 139 | | 题号 | 题目 | 题解 | 140 | | :---- | :------------------------------------------------------------ | :------------------------------------------------------------ | 141 | | 48 | [旋转图像](https://leetcode.cn/problems/rotate-image/) | [Java](Leetcode/数组/48.%20旋转图像.md) | 142 | | 33 | [ 搜索旋转排序数组](https://leetcode.cn/problems/search-in-rotated-sorted-array/) | [Java](Leetcode/数组/33.%20搜索旋转排序数组.md) | 143 | | 34 | [在排序数组中查找元素的第一个和最后一个位置](https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/) | [Java](Leetcode/数组/34.%20在排序数组中查找元素的第一个和最后一个位置.md) | 144 | | 31 | [下一个排列](https://leetcode.cn/problems/next-permutation/) | [Java](Leetcode/数组/31.%20下一个排列.md) | 145 | | 15 | [三数之和](https://leetcode.cn/problems/3sum/) | [Java](Leetcode/数组/15.%20三数之和.md) | 146 | | 11 | [盛最多水的容器](https://leetcode.cn/problems/container-with-most-water/) | [Java](Leetcode/数组/11.%20盛最多水的容器.md) | 147 | | 4 | [寻找两个正序数组的中位数](https://leetcode.cn/problems/median-of-two-sorted-arrays/) | [Java](Leetcode/数组/4.%20寻找两个正序数组的中位数.md) | 148 | | 1 | [两数之和](https://leetcode.cn/problems/two-sum/) | [Java](Leetcode/数组/1.%20两数之和.md) | 149 | | 75 | [颜色分类](https://leetcode.cn/problems/sort-colors/) | [Java](Leetcode/数组/75.%20颜色分类.md) | 150 | | 56 | [合并区间](https://leetcode.cn/problems/merge-intervals/) | [Java](Leetcode/数组/56.%20合并区间.md) | 151 | | 55 | [跳跃游戏](https://leetcode.cn/problems/jump-game/) | [Java](Leetcode/数组/55.%20跳跃游戏.md) | 152 | | 215 | [ 数组中的第K个最大元素](https://leetcode.cn/problems/kth-largest-element-in-an-array/) | [Java](Leetcode/数组/215.%20数组中的第K个最大元素.md) | 153 | | 287 | [寻找重复数](https://leetcode.cn/problems/find-the-duplicate-number/) | [Java](Leetcode/数组/287.%20寻找重复数.md) | 154 | | 283 | [移动零](https://leetcode.cn/problems/move-zeroes/) | [Java](Leetcode/数组/283.%20移动零.md) | 155 | | 240 | [搜索二维矩阵 II](https://leetcode.cn/problems/search-a-2d-matrix-ii/) | [Java](Leetcode/数组/240.%20搜索二维矩阵%20II.md) | 156 | 157 | ### 二叉树 158 | 159 | - 需要会前中后序遍历的递归、迭代实现和层序遍历。 160 | 161 | | 题号 | 题目 | 题解 | 162 | | :---- | :------------------------------------------------------------ | :------------------------------------------------------------ | 163 | | 236 | [二叉树的最近公共祖先](https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/) | [Java](Leetcode/二叉树/236.%20二叉树的最近公共祖先.md) | 164 | | 226 | [翻转二叉树](https://leetcode.cn/problems/invert-binary-tree/) | [Java](Leetcode/二叉树/226.%20翻转二叉树.md) | 165 | | 124 | [二叉树中的最大路径和](https://leetcode.cn/problems/binary-tree-maximum-path-sum/) | [Java](Leetcode/二叉树/124.%20二叉树中的最大路径和.md) | 166 | | 297 | [二叉树的序列化与反序列化](https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/) | [Java](Leetcode/二叉树/297.%20二叉树的序列化与反序列化.md) | 167 | | 543 | [二叉树的直径](https://leetcode.cn/problems/diameter-of-binary-tree/) | [Java](Leetcode/二叉树/543.%20二叉树的直径.md) | 168 | | 538 | [把二叉搜索树转换为累加树](https://leetcode.cn/problems/convert-bst-to-greater-tree/) | [Java](Leetcode/二叉树/538.%20把二叉搜索树转换为累加树.md) | 169 | | 114 | [二叉树展开为链表](https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/) | [Java](Leetcode/二叉树/114.%20二叉树展开为链表.md) | 170 | | 617 | [合并二叉树](https://leetcode.cn/problems/merge-two-binary-trees/) | [Java](Leetcode/二叉树/617.%20合并二叉树.md) | 171 | | 105 | [从前序与中序遍历序列构造二叉树](https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/) | [Java](Leetcode/二叉树/105.%20从前序与中序遍历序列构造二叉树.md) | 172 | | 104 | [二叉树的最大深度](https://leetcode.cn/problems/maximum-depth-of-binary-tree/) | [Java](Leetcode/二叉树/104.%20二叉树的最大深度.md) | 173 | | 102 | [二叉树的层序遍历](https://leetcode.cn/problems/binary-tree-level-order-traversal/) | [Java](Leetcode/二叉树/102.%20二叉树的层序遍历.md) | 174 | | 101 | [对称二叉树](https://leetcode.cn/problems/symmetric-tree/) | [Java](Leetcode/二叉树/101.%20对称二叉树.md) | 175 | | 98 | [验证二叉搜索树](https://leetcode.cn/problems/validate-binary-search-tree/) | [Java](Leetcode/二叉树/98.%20验证二叉搜索树.md) | 176 | | 96 | [不同的二叉搜索树](https://leetcode.cn/problems/unique-binary-search-trees/) | [Java](Leetcode/二叉树/96.%20不同的二叉搜索树.md) | 177 | | 437 | [路径总和 III](https://leetcode.cn/problems/path-sum-iii/) | [Java](Leetcode/二叉树/437.%20路径总和%20III.md) | 178 | | 94 | [二叉树的中序遍历](https://leetcode.cn/problems/binary-tree-inorder-traversal/) | [Java](Leetcode/二叉树/94.%20二叉树的中序遍历.md) | 179 | 180 | ### Stack、Queue、HashMap()、HashSet() 181 | 182 | - Stack、Queue、HashMap和HashSet的常见方法需要会使用。 183 | 184 | | 题号 | 题目 | 题解 | 185 | | :---- | :------------------------------------------------------------ | :------------------------------------------------------------ | 186 | | 169 | [多数元素](https://leetcode.cn/problems/majority-element/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/169.%20多数元素.md) | 187 | | 128 | [最长连续序列](https://leetcode.cn/problems/longest-consecutive-sequence/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/128.%20最长连续序列.md) | 188 | | 438 | [找到字符串中所有字母异位词](https://leetcode.cn/problems/find-all-anagrams-in-a-string/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/438.%20找到字符串中所有字母异位词.md) | 189 | | 394 | [字符串解码](https://leetcode.cn/problems/decode-string/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/394.%20字符串解码.md) | 190 | | 560 | [和为 K 的子数组](https://leetcode.cn/problems/subarray-sum-equals-k/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/560.%20和为K的子数组.md) | 191 | | 20 | [有效的括号](https://leetcode.cn/problems/valid-parentheses/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/20.%20有效的括号.md) | 192 | | 3 | [无重复字符的最长子串](https://leetcode.cn/problems/longest-substring-without-repeating-characters/) | [Java](Leetcode/栈、队列、HashMap()、HashSet()/3.%20无重复字符的最长子串.md) | 193 | 194 | ### 深度优先搜索和广度优先搜索 195 | 196 | - 深度优先搜索:按照既定的搜索顺序,在一个方向搜索到头之后,回溯搜索下一个方向。 197 | 198 | - 广度优先搜索:从起始位置开始,逐层遍历(按照距离)所有可能到达的位置。 199 | 200 | | 题号 | 题目 | 题解 | 201 | | :---- | :----------------------------------------------------------- | :------------------------------------------------------------ | 202 | | 79 | [单词搜索](https://leetcode.cn/problems/word-search/) | [Java](Leetcode/深度优先搜索和广度优先搜索/79.%20单词搜索.md) | 203 | | 200 | [岛屿数量](https://leetcode.cn/problems/number-of-islands/) | [Java](Leetcode/深度优先搜索和广度优先搜索/200.%20岛屿数量.md) | 204 | 205 | --------------------------------------------------------------------------------