LeetCode 热题 HOT 100 Java 题解 -- Part 2
练习地址
Part 1 : https://blog.csdn.net/qq_41080854/article/details/128829494
LeetCode 热题 HOT 100 Java 题解 -- Part 2
- 36. 二叉树的中序遍历 94
- 37. 不同的二叉搜索树 96
- 38. 验证二叉搜索树 98
- 39. 对称二叉树 101
- 40. 二叉树的层序遍历 102
- 41. 二叉树的最大深度 104
- 42. 从前序与中序遍历序列构造二叉树 105
- 43. 二叉树展开为链表 114
- 44. 买卖股票的最佳时机 121
- 45. 二叉树中的最大路径和 124
- 46. 最长连续序列 128
- 47. 只出现一次的数字 136
- 48. 单词拆分 139
- 49. 环形链表 141
- 50. 环形链表 II 142
- 51. LRU 缓存 146
- 52. 排序链表 148
- 53. 乘积最大子数组 152
- 54. 最小栈 155
- 55. 相交链表 160
- 56. 多数元素 169
- 57. 打家劫舍 198
- 58. 岛屿数量 200
- 59. 反转链表 206
- 60. 课程表 207
- 61. 实现 Trie (前缀树) 208
- 62. 数组中的第K个最大元素 215
- 63. 最大正方形 221
- 64. 翻转二叉树 226
- 65. 回文链表 234
- 66. 二叉树的最近公共祖先 236
- 67. 除自身以外数组的乘积 238
- 68. 滑动窗口最大值 239
- 69. 搜索二维矩阵 II 240
- 70. 完全平方数 279
- 71. 移动零 283
- 72. 寻找重复数 287
- 73. 二叉树的序列化与反序列化 297
- 74. 最长递增子序列 300
- 75. 删除无效的括号 301
36. 二叉树的中序遍历 94
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {List<Integer> res = new ArrayList<>();public List<Integer> inorderTraversal(TreeNode root) {dfs(root);return res;}private void dfs(TreeNode root){if(root == null) return;dfs(root.left);res.add(root.val);dfs(root.right);}
}
37. 不同的二叉搜索树 96
给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。
示例 1:
输入:n = 3
输出:5
示例 2:
输入:n = 1
输出:1
解析
假设 i 个节点存在二叉排序树的个数是 G (i),令 f(j) 为以 j 为根的二叉搜索树的个数,则
G(i)=f(1)+f(2)+...+f(j)G(i) = f(1) + f(2) + ... + f(j)G(i)=f(1)+f(2)+...+f(j)
当 j为根节点时,其左子树节点个数为 j-1 个,右子树节点为 i-j,则
f(j)=G(j−1)∗G(i−j)f(j) = G(j-1)*G(i - j)f(j)=G(j−1)∗G(i−j)
状态转移方程G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+…+G(n−1)∗G(0)G(n)=G(0) * G(n-1)+G(1) *(n-2)+\ldots+G(n-1) * G(0)G(n)=G(0)∗G(n−1)+G(1)∗(n−2)+…+G(n−1)∗G(0)
代码
class Solution {public int numTrees(int n) {int[] dp = new int[n + 1];dp[0] = 1; //因为要做乘法for(int i = 1; i < n + 1; i++){ //第几个节点for(int j = 1; j < i + 1; j++){ //选第几个为根dp[i] += dp[j -1] * dp[i - j];}}return dp[n];}
}
38. 验证二叉搜索树 98
给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
解析
对二叉树进行中序遍历,每遍历到一个节点都和当前已遍历的最后一个节点值比较,只要能满足递增关系就继续遍历,直到遍历所有节点。
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {long pre = Long.MIN_VALUE; //前一个的值public boolean isValidBST(TreeNode root) {if(root == null) return true;if(!isValidBST(root.left)) return false;if(pre >= root.val) return false;pre = root.val;return isValidBST(root.right);}
}
39. 对称二叉树 101
给你一个二叉树的根节点 root , 检查它是否轴对称。
示例 1:
输入:root = [1,2,2,3,4,4,3]
输出:true
示例 2:
输入:root = [1,2,2,null,3,null,3]
输出:false
解析
判断轴对称,可以看作是判断根节点的左右子树是否镜像,dfs 判断即可。
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public boolean isSymmetric(TreeNode root) {if(root == null) return true;return dfs(root.left, root.right);}private boolean dfs(TreeNode left, TreeNode right){if(left == null && right == null) return true;if(left == null || right == null || right.val != left.val) return false;return dfs(left.left, right.right) && dfs(left.right, right.left);}
}
40. 二叉树的层序遍历 102
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
示例 1:
输入:root = [3,9,20,null,null,15,7]
输出:[[3],[9,20],[15,7]]
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public List<List<Integer>> levelOrder(TreeNode root) {if(root == null) return new ArrayList<>();List<List<Integer>> res = new ArrayList<>();Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while(!queue.isEmpty()){int n = queue.size();List<Integer> temp = new ArrayList<>();while(n-- > 0){TreeNode node = queue.poll();temp.add(node.val);if(node.left != null) queue.offer(node.left);if(node.right != null) queue.offer(node.right);}res.add(new ArrayList(temp));}return res;}
}
41. 二叉树的最大深度 104
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public int maxDepth(TreeNode root) {if(root == null) return 0;return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;}
}
42. 从前序与中序遍历序列构造二叉树 105
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
解析
二叉树的根节点就是前序序列的第一个节点,这样就可以把中序序列拆分成两部分,根节点前面就是左子树,后面就是右子树,那么就知道了左右子树的节点数量,依此就可以对前序序列也进行拆分,这样左右子树的前序、中序序列都知道了,递归构建左右子树就可以了。
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {Map<Integer, Integer> map = new HashMap<>();public TreeNode buildTree(int[] preorder, int[] inorder) {int n = inorder.length;for(int i = 0; i < n; i++){map.put(inorder[i], i); //存放先序}return build(preorder, inorder, 0, n - 1, 0, n - 1); }private TreeNode build(int[] preorder, int[] inorder, int pl, int pr, int il, int ir){if(pl > pr || il > ir) return null;int k = map.get(preorder[pl]) - il;//中序遍历中根节点的位置TreeNode node = new TreeNode(preorder[pl]);//左子树node.left = build(preorder, inorder, pl + 1, pl + k, il, il + k - 1);//右子树node.right = build(preorder, inorder, pl + k + 1, pr, il + k + 1, ir);return node;}
}
43. 二叉树展开为链表 114
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
示例 1:
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
示例 2:
输入:root = []
输出:[]。
解析
先序遍历,使用一个pre来记录上一个位置进行修改指向
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {TreeNode pre = null; //上一个位置public void flatten(TreeNode root) {if(root == null) return;//先序遍历TreeNode l = root.left; //记录下当前节点的左右结点TreeNode r = root.right;if(pre == null) pre = root;else{pre.right = root; //先序遍历的前一个位置指向当前结点pre.left = null; pre = root;// 更新位置}flatten(l);flatten(r);}
}
44. 买卖股票的最佳时机 121
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
解析
枚举每个位置,在与历史最低价差值最大的价格卖出。贪心算法
代码
class Solution {public int maxProfit(int[] prices) {int res = 0, minPrice = prices[0];for(int i = 1; i < prices.length; i++){res = Math.max(res, prices[i] - minPrice);minPrice = Math.min(minPrice, prices[i]);}return res;}
}
45. 二叉树中的最大路径和 124
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
示例 1:
输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
解析
通过后序遍历递归得到左右子树的最大路径和,依此来更新结果。
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {int res = Integer.MIN_VALUE;public int maxPathSum(TreeNode root) {dfs(root);return res;}private int dfs(TreeNode node){if(node == null) return 0;//贡献大于0才被选中int left = Math.max(0, dfs(node.left));int right = Math.max(0, dfs(node.right));//计算最大路径和 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值res = Math.max(res, node.val + left + right);//返回路径的贡献(只能走一条)return node.val + Math.max(left, right);}
}
46. 最长连续序列 128
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
解析
使用一个哈希表来记录数组中的数字,然后来遍历哈希表,找到每个连续片段的起点,计算长度即可。
代码
class Solution {public int longestConsecutive(int[] nums) {HashSet<Integer> set = new HashSet<>();//连续序列不允许重复for(int i = 0; i < nums.length; i++){set.add(nums[i]);}int res = 0;for(int i = 0; i < nums.length; i++){if(!set.contains(nums[i] - 1)){//找到了起点int num = nums[i] + 1;while(set.contains(num)){num++;}res = Math.max(res, num - nums[i]);}}return res;}
}
47. 只出现一次的数字 136
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1]
输出:1
解析
将数组中所有数字做异或,两两消除,最终剩下的就是只出现一次的元素。
代码
class Solution {public int singleNumber(int[] nums) {int res = 0;for(int num : nums){res ^= num;}return res;}
}
48. 单词拆分 139
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。
示例 2:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以由 “apple” “pen” “apple” 拼接成。
注意,你可以重复使用字典中的单词。
示例 3:
输入: s = “catsandog”, wordDict = [“cats”, “dog”, “sand”, “and”, “cat”]
输出: false
解析
序列化DP,动态规划,自己找符合的单词
定义 f(i) 表示 s 的前 i 个字符能够由字典中单词拼接构成,则 f[i]=f[j−1]&&exists(s[j…i])f[i]=f[j-1] \& \& \operatorname{exists}(s[j \ldots i])f[i]=f[j−1]&&exists(s[j…i])
代码
class Solution {public boolean wordBreak(String s, List<String> wordDict) {HashSet<String> set = new HashSet<>();for(String str : wordDict){set.add(str);}int n = s.length();boolean[] dp = new boolean[n + 1];//定义 f(i) 表示 s 的前 i 个字符能够由字典中单词拼接构成dp[0] = true;for(int i = 1; i < n + 1; i++){for(int j = 1; j < n + 1; j++){if(dp[j - 1] && set.contains(s.substring(j - 1, i))){dp[i] = true;break;}}}return dp[n];}
}
49. 环形链表 141
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
解析
我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与 slow 指针在环中相遇。
代码
/*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/
public class Solution {public boolean hasCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;if(slow == fast) return true;}return false;}
}
50. 环形链表 II 142
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
解析
我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针重新移动到头部,fast和slow每次往后移动一个位置最终将再次与 slow 指针在环中相遇。
代码
/*** Definition for singly-linked list.* class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/
public class Solution {public ListNode detectCycle(ListNode head) {ListNode fast = head;ListNode slow = head;while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;if(slow == fast){fast = head;while(fast != slow){fast = fast.next;slow = slow.next;}return fast;}}return null;}
}
51. LRU 缓存 146
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
示例:
输入
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
解析
哈希表 + 双向链表,哈希表记录 key 和链表节点的映射关系,当需要淘汰时,从链表尾部删除节点;当需要更新时间戳时,通过哈希表获取节点,将其删除并插入到链表头。
代码
//hash表和双向链表
class DNode{ //get int key;int value;DNode pre;DNode next;public DNode(){this.key = 0;this.value = 0;this.pre = null;this.next = null;}public DNode(int key, int value){this.key = key;this.value = value;this.pre = null;this.next = null;}
}class LRUCache {HashMap<Integer, DNode> map; // putint capacity;int size;DNode head;DNode tail;public LRUCache(int capacity) {map = new HashMap<>(capacity);head = new DNode();tail = new DNode();this.capacity = capacity;this.size = 0;head.next = tail;tail.pre = head;}public int get(int key) {if(map.containsKey(key)){DNode node = map.get(key);moveToHead(node);return node.value; }else{return -1;}}public void put(int key, int value) {if(map.containsKey(key)){DNode node = map.get(key);moveToHead(node);node.value = value;}else{//判断是否超过容量DNode node = new DNode(key, value);map.put(key, node); //map放入对应键size++;addToHead(node);//双向链表插入node//超过容量if (this.size > this.capacity) {removeLast();//删除最后一个不用的size--;}}}private void moveToHead(DNode node){//删除结点node.pre.next = node.next;node.next.pre = node.pre;//移动到头部node.next = head.next;node.pre = head;head.next = node;node.next.pre = node;}private void addToHead(DNode node){node.next = head.next;node.pre = head;head.next = node;node.next.pre = node;}private void removeLast(){DNode node = tail.pre;map.remove(node.key);node.pre.next = node.next;node.next.pre = node.pre;node.next = null;node.pre = null;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/
52. 排序链表 148
给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。
示例 1:
输入:head = [4,2,1,3]
输出:[1,2,3,4]
解析
归并排序
代码
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode sortList(ListNode head) {if(head == null) return null;return sort(head);}public ListNode sort(ListNode node){if(node.next == null) return node;//单个结点ListNode fast = node;ListNode slow = fast; //中点位置ListNode pre = null;//找到中点while(fast != null && fast.next != null){pre = slow;fast = fast.next.next;slow = slow.next;}pre.next = null;ListNode left = sort(node);ListNode right = sort(slow);return merge(left, right);}public ListNode merge(ListNode left, ListNode right){if(left == null) return right;if(right == null) return left;ListNode dummy = new ListNode();ListNode help = dummy;while(left != null && right != null){if(left.val > right.val){help.next = right;right = right.next;}else{help.next = left;left = left.next;}help = help.next;}if(left != null) help.next = left;if(right != null) help.next = right;return dummy.next;}
}
53. 乘积最大子数组 152
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
解析
本题与 0053. 最大子序和 的思路相似,但由于是乘法运算,负数相乘可能得到最大值,因此在遍历的同时需要维护最大值和最小值两个变量。
代码
class Solution {public int maxProduct(int[] nums) {int res = nums[0]; int f = nums[0];// 最大值int g = nums[0];// 最小值for(int i = 1; i < nums.length; i++){int t = nums[i];int fa = f * t;int ga = g * t;f = Math.max(t, Math.max(fa, ga));g = Math.min(t, Math.min(fa, ga));res = Math.max(res, f);}return res;}
}
54. 最小栈 155
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
示例 1:
输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
解析
使用一个辅助栈,push 的同时在辅助栈中加入当前最小值。
代码
class MinStack {Deque<Integer> stack;Deque<Integer> minstack;public MinStack() {stack = new ArrayDeque<>();minstack = new ArrayDeque<>();}public void push(int val) {stack.push(val);if(minstack.isEmpty() || minstack.peek() > val){minstack.push(val);}else{minstack.push(minstack.peek());}}public void pop() {stack.pop();minstack.pop();}public int top() {return stack.peek();}public int getMin() {return minstack.peek();}
}/*** Your MinStack object will be instantiated and called as such:* MinStack obj = new MinStack();* obj.push(val);* obj.pop();* int param_3 = obj.top();* int param_4 = obj.getMin();*/
55. 相交链表 160
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
解析
统计长度,并且长的先走length,最后一起走就是答案
代码
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val = x;* next = null;* }* }*/
public class Solution {public ListNode getIntersectionNode(ListNode headA, ListNode headB) {int al = 0, bl = 0;ListNode cur1 = headA;ListNode cur2 = headB;while(cur1 != null){cur1 = cur1.next;al++;}while(cur2 != null){cur2 = cur2.next;bl++;}if(cur1 != cur2) return null;cur1 = al - bl < 0 ? headA : headB; // 短的那一节cur2 = cur1 == headA ? headB : headA;// 长的那一节// 长的走for(int i = 0; i < Math.abs(al - bl); i++){cur2 = cur2.next;}// 一起走while(cur1 != cur2){cur1 = cur1.next;cur2 = cur2.next;}return cur2;}
}
56. 多数元素 169
给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入:nums = [3,2,3]
输出:3
示例 2:
输入:nums = [2,2,1,1,1,2,2]
输出:2
解析
Boyer-Moore 投票算法:每次都找出一对不同的元素,从数组中删除,直到数组为空或只有一种元素。
不难证明,如果存在元素 e 出现频率超过半数,那么数组中最后剩下的就只有 e。
代码实现:使用 ans 记录已遍历元素中出现次数最多的元素,count 记录还未被抵消的数量,当遍历结束 ans 即为数组的众数。
代码
class Solution {public int majorityElement(int[] nums) {int res = 0, count = 0;for(int i = 0; i < nums.length; i++){if(count == 0) res = nums[i];if(nums[i] != res) count--;else count++;}return res;}
}
57. 打家劫舍 198
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
解析
动态规划,dp[i] 表示偷第 i 家 获得的最高金额
状态方程 dp[i] = max(dp[i - 1], nums[i -1] + dp[i -2]),不偷这家(累计上次金额)和偷这家(这家金额和累计上上次金额)
代码
class Solution {public int rob(int[] nums) {int[] dp = new int[nums.length + 1];dp[0] = 0; //表示偷第 i 家 获得的最高金额dp[1] = nums[0];for(int i = 2; i < nums.length + 1; i++){dp[i] = Math.max(dp[i - 1], nums[i - 1] + dp[i - 2]);}return dp[nums.length];}
}
58. 岛屿数量 200
给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
输入:grid = [
[“1”,“1”,“1”,“1”,“0”],
[“1”,“1”,“0”,“1”,“0”],
[“1”,“1”,“0”,“0”,“0”],
[“0”,“0”,“0”,“0”,“0”]
]
输出:1
解析
- step 1:优先判断空矩阵等情况。
- step 2:从上到下从左到右遍历矩阵每一个位置的元素,如果该元素值为1,统计岛屿数量。
- step 3:接着将该位置的1改为0,然后使用dfs判断四个方向是否为1,分别进入4个分支继续修改。
代码
class Solution {public int numIslands(char[][] grid) {int res = 0;for(int i = 0; i < grid.length; i++){for(int j = 0; j < grid[0].length; j++){if(grid[i][j] == '1'){dfs(grid, i, j);res++;}}}return res;}private void dfs(char[][] grid, int i, int j){if(i< 0 || i >= grid.length || j < 0 || j >= grid[0].length //边界条件|| grid[i][j] == '0') return;grid[i][j] = '0';dfs(grid, i + 1, j);dfs(grid, i - 1, j);dfs(grid, i, j + 1);dfs(grid, i, j - 1); }
}
59. 反转链表 206
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
代码
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode reverseList(ListNode head) {ListNode pre = null;ListNode next = null;while(head != null){next = head.next;head.next = pre;pre = head;head = next;}return pre;}
}
60. 课程表 207
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
解析
通过dfs,判断是否有环
代码
class Solution {List<List<Integer>> graph = new ArrayList<>();int[] isvisited;public boolean canFinish(int numCourses, int[][] prerequisites) {//创建图for(int i = 0; i < numCourses; i++){graph.add(new ArrayList<>());}isvisited = new int[numCourses];//添加节点for(int[] cp : prerequisites){graph.get(cp[1]).add(cp[0]);}//判断每个节点是否有环for(int i = 0; i < numCourses; i++){if(hasCircle(prerequisites, i)) return false;}return true;}private boolean hasCircle(int[][] prerequisites, int index){if(isvisited[index] == 1) return true;//走到自身,有环if(isvisited[index] == 2) return false;//走过了,没有环,跳过isvisited[index] = 1;for(Integer i : graph.get(index)){ //出度if(hasCircle(prerequisites, i)) return true;}isvisited[index] = 2; //走过无环标记return false;}
}
61. 实现 Trie (前缀树) 208
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie() 初始化前缀树对象。
void insert(String word) 向前缀树中插入字符串 word 。
boolean search(String word) 如果字符串 word 在前缀树中,返回 true(即,在检索之前已经插入);否则,返回 false 。
boolean startsWith(String prefix) 如果之前已经插入的字符串 word 的前缀之一为 prefix ,返回 true ;否则,返回 false 。
Trie 的应用场景,8 个字:一次建树,多次查询。
代码
class Trie {private TreeNode root;public Trie() {root = new TreeNode();}public void insert(String word) {TreeNode node = root;for(char c : word.toCharArray()){if(node.next[c - 'a'] == null){node.next[c - 'a'] = new TreeNode();}node = node.next[c - 'a']; //移动下一位}node.isEnd = true;}public boolean search(String word) {TreeNode node = root;for(char c : word.toCharArray()){node = node.next[c - 'a'];if(node == null) return false;}return node.isEnd;}public boolean startsWith(String prefix) {TreeNode node = root;for(char c : prefix.toCharArray()){node = node.next[c - 'a'];if(node == null) return false;}return true;}
}
class TreeNode{boolean isEnd;TreeNode[] next;public TreeNode(){isEnd = false; //是否到头next = new TreeNode[26]; //26个字母,默认为null}
}
/*** Your Trie object will be instantiated and called as such:* Trie obj = new Trie();* obj.insert(word);* boolean param_2 = obj.search(word);* boolean param_3 = obj.startsWith(prefix);*/
62. 数组中的第K个最大元素 215
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
解析
快排,转换为求前N最小
代码
class Solution {public int findKthLargest(int[] nums, int k) {return quickSort(nums, 0, nums.length - 1, nums.length - k);}public int quickSort(int[] nums, int l, int r, int idx){if(l == r) return nums[l];int k = l + (int)(Math.random() * (r - l + 1));swap(nums, r, k);int q = partion(nums, l, r);//小于等于区域的右边界if(q == idx) return nums[q];else if(q < idx) return quickSort(nums, q + 1, r, idx);else return quickSort(nums, l, q - 1, idx);}private void swap(int[] nums, int a, int b){int t = nums[a];nums[a] = nums[b];nums[b] = t;}private int partion(int[] nums, int l, int r){int cmp = nums[r];int left = l - 1;for(int i = l; i < r; i++){if(nums[i] <= cmp){swap(nums, i, ++left);}}swap(nums, r, left + 1);return left + 1;}
}
63. 最大正方形 221
在一个由 ‘0’ 和 ‘1’ 组成的二维矩阵内,找到只包含 ‘1’ 的最大正方形,并返回其面积。
示例 1:
输入:matrix = [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“1”,“1”,“1”,“1”],[“1”,“0”,“0”,“1”,“0”]]
输出:4
解析
动态规划,dp[i][j]dp[i][j]dp[i][j] 表示以 i, j 为右下角的正方形的最大边长
转移方程dp(i,j)=min(dp(i−1,j),dp(i−1,j−1),dp(i,j−1))+1dp(i,j)=min(dp(i−1,j),dp(i−1,j−1),dp(i,j−1))+1dp(i,j)=min(dp(i−1,j),dp(i−1,j−1),dp(i,j−1))+1
代码
class Solution {public int maximalSquare(char[][] matrix) {int r = matrix.length;int c = matrix[0].length;int[][] dp = new int[r + 1][c + 1];//dp[i][j] 表示以 i, j 为右下角的正方形的最大边长int maxSide = 0;for(int i = 1; i < r + 1; i++){for(int j = 1; j < c + 1; j++){if(matrix[i - 1][j - 1] == '1'){//成为正方形应该是斜上,上 和 左 都 1 , 并且短板效应dp[i][j] = Math.min(dp[i - 1][j - 1],Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;maxSide = Math.max(maxSide, dp[i][j]);}}}return maxSide * maxSide;}
}
64. 翻转二叉树 226
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
解析
先序遍历从顶向下交换即可
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/
class Solution {public TreeNode invertTree(TreeNode root) {if(root == null) return null;TreeNode tmp = root.left;root.left = root.right;root.right = tmp;invertTree(root.left);invertTree(root.right);return root;}
}
65. 回文链表 234
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
解析
利用快慢指针找到中点,将后半截反序,两个指针从两边遍历到中点,逐个比较
代码
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public boolean isPalindrome(ListNode head) {ListNode f = head;ListNode s = head;while(f != null && f.next != null){f = f.next.next;s = s.next;}s = reverse(s);f = head;while(s != null){if(s.val != f.val) return false;s = s.next;f = f.next;}return true;}public ListNode reverse(ListNode node){ListNode pre = null;ListNode next = null;while(node != null){next = node.next;node.next = pre;pre = node;node = next;}return pre;}
}
66. 二叉树的最近公共祖先 236
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点 5 和节点 1 的最近公共祖先是节点 3 。
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
class Solution {public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) return null;if(root.val == p.val || root.val == q.val) return root;//节点为自己的祖先TreeNode left = lowestCommonAncestor(root.left, p, q);TreeNode right = lowestCommonAncestor(root.right, p, q);if(left != null && right != null) return root;//从左右子树找到了return left == null ? right : left;}
}
67. 除自身以外数组的乘积 238
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
解析
左右累乘,记录每个元素的左右乘积
代码
class Solution {public int[] productExceptSelf(int[] nums) {int[] res = new int[nums.length];int left = 1;int right = 1;for(int i = 0; i < nums.length; i++){res[i] = left;left *= nums[i];}for(int j = nums.length - 1; j >= 0; j--){res[j] *= right;right *= nums[j];}return res;}
}
68. 滑动窗口最大值 239
给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
返回 滑动窗口中的最大值 。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
解析
单调队列维护窗口的最大值
代码
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {Deque<Integer> deque = new LinkedList<>();int[] res = new int[nums.length - k + 1];for(int i = 0; i < nums.length; i++){while(!deque.isEmpty() && nums[i] > nums[deque.peekLast()]){deque.pollLast();}deque.offerLast(i);while(!deque.isEmpty() && deque.peekFirst() < (i - k + 1)){//已经过了窗口deque.pollFirst();}if(i - k + 1 >= 0){res[i - k + 1] = nums[deque.peekFirst()];}}return res;}
}
69. 搜索二维矩阵 II 240
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
解析
首先以左下角为起点,若是它小于目标元素,则往右移动去找大的,若是他大于目标元素,则往上移动去找小的。
代码
class Solution {public boolean searchMatrix(int[][] matrix, int target) {int r = matrix.length;int c = matrix[0].length;int i = r - 1;int j = 0;while(i >= 0 && j < c){if(matrix[i][j] > target){i--;}else if(matrix[i][j] < target){j++;}else{return true;}}return false;}
}
70. 完全平方数 279
给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9
解析
完全背包,dp[i] 表示最小需要完全平方数的数量
代码
class Solution {public int numSquares(int n) {//完全背包int[] dp = new int[n + 1]; //dp[i] 表示最小需要完全平方数的数量 Arrays.fill(dp, Integer.MAX_VALUE);dp[0] = 0;//物品 和 容量 i * i 价值 为 1for(int i = 1; i * i < n + 1; i++){ //每一个平方数int weight = i * i; for(int j = weight; j < n + 1; j++){ // 整数 ndp[j] = Math.min(dp[j], dp[j - weight] + 1);}}return dp[n];}
}
71. 移动零 283
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:
输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:
输入: nums = [0]
输出: [0]
使用一个 idx 指针,指向非零元素的最后一个位置,遍历数组,当遇到非零数字时,将其移动到 idx 上,并把 idx 后移。
当遍历结束后,把 idx 后面的位置全部置为 0 即可。
代码
class Solution {public void moveZeroes(int[] nums) {int idx = 0;for(int i = 0; i < nums.length; i++){if(nums[i] != 0){nums[idx++] = nums[i];}}while(idx < nums.length) nums[idx++] = 0;}
}
72. 寻找重复数 287
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
解析
本题可以转换为已知一个有环链表,求出环的入口
代码
class Solution {public int findDuplicate(int[] nums) {int s = nums[0], f = nums[nums[0]];// nums[0] 看做是指针,0 也是指针// 有环链表while(s != f){s = nums[s];f = nums[nums[f]];}f = 0;while(s != f){s = nums[s];f = nums[f];}return s;}
}
73. 二叉树的序列化与反序列化 297
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示: 输入输出格式与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
解析
先序遍历
代码
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode(int x) { val = x; }* }*/
public class Codec {StringBuilder sb = new StringBuilder();Queue<String> queue = new LinkedList<>();// Encodes a tree to a single string.public String serialize(TreeNode root) {//先序遍历serializeHelp(root);return sb.toString();}private void serializeHelp(TreeNode node){if(node == null){sb.append("#_");return;}sb.append(node.val);sb.append("_");serialize(node.left);serialize(node.right);}// Decodes your encoded data to tree.public TreeNode deserialize(String data) {String[] res = data.split("_", -1);for(String str : res){queue.offer(str);}return deserializeHelp();}private TreeNode deserializeHelp(){//先序遍历String str = queue.poll();if(str.equals("#")) return null;TreeNode node = new TreeNode(Integer.valueOf(str));node.left = deserializeHelp();node.right = deserializeHelp();return node;}
}// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));
74. 最长递增子序列 300
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
解析
动态规划, p[i] 为考虑前 i 个元素,以第 i 个数字结尾的最长上升子序列的长度
代码
class Solution {public int lengthOfLIS(int[] nums) {int[] dp = new int[nums.length];Arrays.fill(dp, 1);int res = 1;for(int i = 0; i < nums.length; i++){for(int j = 0; j < i; j++){if(nums[i] > nums[j]){dp[i] = Math.max(dp[i], dp[j] + 1);res = Math.max(res, dp[i]);}}}return res;}
}
75. 删除无效的括号 301
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
示例 1:
输入:s = “()())()”
输出:[“(())()”,“()()()”]
示例 2:
输入:s = “(a)())()”
输出:[“(a())()”,“(a)()()”]
示例 3:
输入:s = “)(”
输出:[“”]
解析
回溯算法,确定最后的结果长度并决定要不要删除左右括号,边界判断,左括号大于等于右括号
代码
class Solution {HashSet<String> set = new HashSet<>();int len = 0;public List<String> removeInvalidParentheses(String s) {int lc = 0, rc = 0;//左右括号的数量for(char c : s.toCharArray()){if(c == '(') lc++;else if(c == ')'){if(lc == 0) rc++;else lc--;}}//输出字符串的长度len = s.length() - lc - rc;dfs(s, 0, 0, 0, "");return new ArrayList<String>(set);}private void dfs(String s, int l, int r, int idx, String p){if(l < r) return; //右括号 大于 左括号if(s.length() == idx){// 结果是否满足长度要求if(p.length() == len && l == r) set.add(p);return;}char c = s.charAt(idx);if(c == '('){dfs(s, l + 1, r, idx + 1, p + c);// 不删dfs(s, l, r, idx + 1, p);// 删}else if(c == ')'){dfs(s, l, r + 1, idx + 1, p + c);dfs(s, l, r, idx + 1, p);}else{ // 非括号,不删dfs(s, l, r, idx + 1, p + c);}}
}
相关文章:
LeetCode 热题 HOT 100 Java 题解 -- Part 2
练习地址 Part 1 : https://blog.csdn.net/qq_41080854/article/details/128829494 LeetCode 热题 HOT 100 Java 题解 -- Part 236. 二叉树的中序遍历 9437. 不同的二叉搜索树 9638. 验证二叉搜索树 9839. 对称二叉树 10140. 二叉树的层序遍历 10241. 二叉树的最大深度 10442.…...
【项目实战】IDEA常用快捷键汇总
一、修改为Eclipse的快捷键 相信很多朋友跟我一样, 都是习惯了eclipse的快捷键,没错,习惯这东西真的很难改!IDEA非常强大,支持我们修改IDEA中的keymap为Eclipse的快捷键!友好又贴心,有没有&…...
更新 TKK 失败,请检查网络连接。谷歌翻译 translation插件不能用解决办法 亲测有效
谷歌翻译无法使用,谷歌回应解释是,谷歌翻译使用率过低,所以选择停止服务。网上也有说法,指出根本原因为,提供API接口的googleapis被墙,这导致js文件和字体资源无法加载。 这里提供两种解决办法 方案一 修…...
SpringBoot整合MybatisPlus多数据源
相信在很多使用MybatisPlus框架的小伙伴都会遇到多数据源的配置问题,并且官网也给出了推荐使用多数据源 (dynamic-datasource-spring-boot-starter) 组件来实现。由于最近项目也在使用这个组件来实现多数据源切换,因此想了解一下该组件是如何运行的&…...
【教程】如何使用Java生成PDF文档?
在如今数字化时代,越来越多的人使用PDF文档进行信息传递和共享。而使用Java生成PDF文档也成为了一个非常重要的技能,因为Java作为一种通用的编程语言,可以在不同的操作系统和平台上运行。下面,我们将为您介绍如何使用Java生成PDF文…...
I.MX6ULL内核开发13:pinctrl子系统和gpio子系统-led实验
目录 一、pinctrl子系统 1.1 pinctrl子系统编写格式以及引脚属性介绍 1.1.1 iomux节点介绍 1.1.2 pinctrl子节点编写格式 1.1.3 引脚配置信息介绍 1.2 将RGB灯引脚添加到pinctrl子系统 1.2.1 查找RGB灯使用的引脚 1.2.2找到引脚宏定义 1.2.3 设置引脚属性 1.2.4 在…...
Linux系列 使用vi文本编辑器
作者简介:一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.vi文本编辑器 1.使用vi文本编辑器 2.vi编辑器的工作模式 3.命令模式中的…...
【java基础】接口(interface)
文章目录基础介绍接口的定义关于接口字段和方法的说明使用接口抽象类和接口接口方法冲突的一些说明方法相同名称和参数,返回值相同方法名称相同,参数不同,返回值相同方法返回值不同,名称参数相同方法完全相同,一个有默…...
ChatGPT(GPT3.5) OpenAI官方API正式发布
OpenAI社区今天凌晨4点多发送的邮件,介绍了ChatGPT官方API的发布。官方介绍文档地址为“OpenAI API”和“OpenAI API”。 ChatGPT(GPT3.5)官方API模型名称为“gpt-3.5-turbo”和“gpt-3.5-turbo-0301”。API调用价格比GPT text-davinci-003模型便宜10倍。调用费用为…...
CAD中如何将图形对象转换为三维实体?
有些小伙伴在CAD绘制完图纸后,想要将图纸中的某些图形对象转换成三维实体,但却不知道该如何操作,其实很简单,本节CAD绘图教程就和小编一起来了解一下浩辰CAD软件中将符合条件的对象转换为三维实体的相关操作步骤吧! 将…...
【K8S笔记】Kubernetes 集群架构与组件介绍
K8S 官方文档 https://kubernetes.io/zh/docs/home ##注重关注 概念和任务 板块。 K8S 集群架构 K8S也是运用了分布式集群架构: 管理节点/Master 整个集群的管理,任务协作。工作节点/Node 容器运行、删除。 K8S 组件介绍 管理节点/Master 相关组件 …...
9 怎么登录VNC
1)首先在ssh登录后启动vncserver。登陆后输入下面的指令来创建自己的VNC。 命令vncserver :16 –geometry 1900x1000 其中:16是分配的端口号,1900x1000是分辨率。如果没有响应,建议执行下面操作后再次重复上面操作。 命令…...
MPI ubuntu安装,mpicc,mpicxx,mpif90的区别
介绍 MPI是并行计算的一个支持库,支持对C、C、fortran语言进行并行计算。 安装基础环境 ubuntu进行gcc/g/gfortran的安装: gcc: ubuntu下自带gcc编译器。可以通过gcc -v命令来查看是否安装。 g: sudo apt-get install buil…...
移动端笔记
目录 一、移动端基础 二、视口 三、二倍图/多倍图 四、移动端开发 (一)开发选择 (二)常见布局 (三)移动端技术解决方案 五、移动WEB开发之flex布局 六、移动WEB开发之rem适配布局 #END(…...
操作系统笔记、面试八股(一)—— 进程、线程、协程
文章目录1. 进程、线程、协程1.1 进程1.1.1 进程间的通信方式1.1.2 进程同步方式1.1.3 进程的调度算法1.1.4 优先级反转1.1.5 进程状态1.1.6 PCB进程控制块1.1.7 进程的创建和撤销过程1.1.8 为什么要有进程1.2 线程1.2.1 为什么要有线程1.2.2 线程间的同步方式1.3 协程1.3.1 什…...
Python每日一练(20230302)
目录 1. 字符串统计 2. 合并两个有序链表 3. 下一个排列 附录 Python字典内置方法 增 删 改 查 其它 1. 字符串统计 从键盘输入一个包含有英文字母、数字、空格和其它字符的字符串,并分别实现下面的功能:统计字符串中出现2次的英文字母&#…...
Numpy课后练习
Numpy课后练习 文章目录 Numpy课后练习一、前言二、题目及答案一、前言 答案仅供参考,谢谢大家! 二、题目及答案 导入Numpy包并设置随机数种子为666 import numpy as np np.random.seed(666)创建并输出一个包含12个元素的随机整数数组r1,元素的取值范围在[30,100)之间 r1 …...
动态规划dp中的子序列、子数组问题总结
目录 定义dp数组 初始化dp数组 状态转移方程 最终结果 题目 定义dp数组 这类问题的共性是会提供两个数组,寻找他们共同的子序列、子数组。设第一个数组为s,第二个数组为t。则可以设二维dp数组,其大小为len(s + 1)*len(t + 1) dp[i][j]表示 s 前 i 个长度,...
Zookeeper3.5.7版本——Zookeeper的概述、工作机制、特点、数据结构及应用场景
目录一、Zookeeper的概述二、Zookeeper的工作机制三、Zookeeper的特点四、Zookeeper的数据结构五、Zookeeper的应用场景5.1、统一命名服务5.2、统一配置管理5.3、统一集群管理5.4、服务器动态上下线5.5、软负载均衡一、Zookeeper的概述 Zookeeper 是一个开源的分布式的&#x…...
安卓逆向学习及APK抓包(二)--Google Pixel一代手机的ROOT刷入面具
注意:本文仅作参考勿跟操作,root需谨慎,本次测试用的N手Pixel,因参考本文将真机刷成板砖造成的损失与本人无关 1 Google Pixel介绍 1.1手机 google Pixel 在手机选择上,优先选择谷歌系列手机,Nexus和Pixel系列&…...
线程池的基本认识与使用
线程池的基本认识与使用线程池线程池工作原理:优点:传统的创建线程方式线程池创建线程使用线程池 池化思想:线程池、字符串常量池、数据库连接池可以提高资源的利用率 线程池工作原理: 预先创建多个线程对象 放入线程池种&#…...
小家电品牌私域增长解决方案来了
小家电品牌的私域优势 01、行业线上化发展程度高 相对于大家电动辄上千上万元的价格,小家电的客单价较低。而且与大家电偏刚需属性不同的是,小家电的消费需求侧重场景化,用户希望通过购买小家电来提高自身的生活品质。这就决定了用户的决策…...
什么是让ChatGPT爆火的大语言模型(LLM)
什么是让ChatGPT爆火的大语言模型(LLM) 更多精彩内容: https://www.nvidia.cn/gtc-global/?ncidref-dev-876561 文章目录什么是让ChatGPT爆火的大语言模型(LLM)大型语言模型有什么用?大型语言模型如何工作?大型语言模型的热门应用在哪里可以找到大型语言…...
【监控】Linux部署postgres_exporter及PG配置(非Docker)
目录一、下载及部署二、postgres_exporter配置1. 停止脚本stop.sh2. 启动脚本start.sh3. queries.yaml三、PostgreSQL数据库配置1. 修改postgresql.conf配置文件2. 创建用户、表、扩展等四、参考一、下载及部署 下载地址 选一个amd64下载 上传至服务器,解压 tax…...
基于Java+SpringBoot+Vue+Uniapp(有教程)前后端分离健身预约系统设计与实现
博主介绍:✌全网粉丝3W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战✌ 博主作品:《微服务实战》专栏是本人的实战经验总结,《Spring家族及…...
【2023】DevOps、SRE、运维开发面试宝典之Redis相关面试题
文章目录 1、redis主从复制原理2、redis哨兵模式的原理3、reids集群原理4、Redis 哈希表进行的触发时机是什么?5、Redis 的 RDB 和 AOF 机制各自的优缺点是什么?这两种机制是否可以混合使用?6、Redis 经常被称为单线程的系统,你如何理解 Redis 的单线程模型7、redis 的事务…...
十五、MyBatis使用PageHelper
1.limit分页 limit分页原理 mysql的limit后面两个数字: 第一个数字:startIndex(起始下标。下标从0开始。) 第二个数字:pageSize(每页显示的记录条数) 假设已知页码pageNum,还有每页…...
【MySQL】B+ 树索引
一、索引是什么 ? 为什么需要索引 ? 索引就是目录,目录就是索引。 索引从 InnoDB 存储引擎数据存储结构上来看,就是为各个页建立的目录。保证我们在查询时,可以通过二分法快速定位到页,再在页内通过二分法…...
Android Gradle Plugin Version 和 Gradle Version 的对应关系
官网参考 以下是插件版本和Gradle 版本对应关系: 插件版本所需的最低 Gradle 版本Android Gradle Plugin VersionGradle Version1.0.0 - 1.1.32.2.1 - 2.31.2.0 - 1.3.12.2.1 - 2.91.5.02.2.1 - 2.132.0.0 - 2.1.22.10 - 2.132.1.3 - 2.2.32.14.1 - 3.52.3.03.33.0…...
更多单词/词组/短语补充和总结(二)
auto 美 /[ˈɔːtoʊ] n.汽车adj.与汽车有关的,汽车的。不要记成“自动的” mobile 美 /[ˈmoʊbl] adj.可移动的;流动的;不要记成“手机”,手机是mobile phone automobile 美 /[ˈɔːtəməbiːl] n.汽车adj.自动的 automatic 美 /[ˌɔːtəˈmtɪk]…...
如何做正版小说网站/武汉seo推广优化
0 引言 《信息安全技术 信息系统安全等级保护基本要求》(GB/T 22239-2008)在我国推行信息安全等级保护制度的过程中起到了非常重要的作用, 被广泛用于各行业或领域, 指导用户开展信息系统安全等级保护的建设整改、等级测评等工作[1]。随着信息技术的发展…...
监控做斗鱼直播网站/有道搜索引擎入口
作者 | Moses Olafenwa翻译 | 林椿眄出品 | 人工智能头条(公众号ID:AI_Thinker)作为人工智能的一个重要领域,计算机视觉是一门可以识别并理解图像和场景的计算机及软件系统科学。该领域主要包括图像识别&…...
做网站的职责/托管竞价推广公司
在jsp中,中文乱码常会让人心乱如麻。对于中文处理的常见对策,在网上经常可见的主要是下面2种:<% pagecontentType"text/html;charsetgb2312" %>或者:<%String Hi"你好";byt…...
炫酷网站界面设计/百度推广培训
有任何的书写错误、排版错误、概念错误等,希望大家包含指正。 在阅读本篇之前建议先学习: RNN 讲解 LSTM 讲解 3. GRU 3.1. 网络结构 GRU 是循环神经网络的一种,和 LSTM 一样,是为了解决长期依赖问题。GRU 单元结构如下。 图 1…...
哪个网站可以代做试题/新闻头条最新消息今天发布
process.cwd() 是当前执行node命令时候的文件夹地址 ——工作目录,保证了文件在不同的目录下执行时,路径始终不变__dirname 是被执行的js 文件的地址 ——文件所在目录 Nodejs官方文档上的解释: > process.cwd(): The process.cwd() metho…...
网站建设有哪些技术/百度推广公司电话
在移动端需要安全算法时,直接使用开源库可能不合适(开源库都比较大,也可以自己抽取需要的代码),本Demo是根据AES的原理来实现算法,采用ECB/PKCS5Padding,实现短小精悍!! 注意:本算法…...