HOT100合集(除去前面做过的题)
最长回文子串
题目 中等
和最长回文子序列类似
自己的做法:
class Solution {
public String longestPalindrome(String s) {
int len = s.length();
int max = 1;
int left = 0, right = 0;
int[][] dp = new int[len][len];
dp[0][0] = 1;
for (int j = 1; j < len; j++) {
dp[j][j] = 1;
for (int i = j - 1; i >= 0; i--) {
if (s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1] == j - 1 - i) {
dp[i][j] = dp[i + 1][j - 1] + 2;
if (dp[i][j] > max) {
max = dp[i][j];
left = i;
right = j;
}
}
else
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
}
}
return s.substring(left, right + 1);
}
}
官方解法:
public class Solution {
public String longestPalindrome(String s) {
// 特殊用例判断
int len = s.length();
if (len < 2) {
return s;
}
int maxLen = 1;
int begin = 0;
// dp[i][j] 表示 s[i, j] 是否是回文串
boolean[][] dp = new boolean[len][len];
char[] charArray = s.toCharArray();
for (int i = 0; i < len; i++) {
dp[i][i] = true;
}
for (int j = 1; j < len; j++) {
for (int i = 0; i < j; i++) {
if (charArray[i] != charArray[j]) {
dp[i][j] = false;
} else {
if (j - i < 3) {
dp[i][j] = true;
} else {
dp[i][j] = dp[i + 1][j - 1];
}
}
// 只要 dp[i][j] == true 成立,就表示子串 s[i..j] 是回文,此时记录回文长度和起始位置
if (dp[i][j] && j - i + 1 > maxLen) {
maxLen = j - i + 1;
begin = i;
}
}
}
return s.substring(begin, begin + maxLen);
}
}
作者:liweiwei1419
链接:https://leetcode.cn/problems/longest-palindromic-substring/solutions/7792/zhong-xin-kuo-san-dong-tai-gui-hua-by-liweiwei1419/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
无重复字符的最长子串
题目 中等
很简单,滑动窗口,自己的代码已经很好理解了,右窗口不停的扩,遇到重复字符就缩左窗口直到遇到左边的重复字符,然后左窗口再移到该重复字符的右边。移动窗口过程中再去比较并记录最大值即可:
class Solution {
public int lengthOfLongestSubstring(String s) {
int left = 0, right = 0, max = 0;
Set<Character> set = new HashSet<>();
for (; right < s.length(); right++) {
char c = s.charAt(right);
if (!set.contains(c)) {
set.add(c);
max = Math.max(max, right - left + 1);
} else {
while (left <= right && s.charAt(left) != c) {
set.remove(s.charAt(left));
left++;
}
left++;
}
}
return max;
}
}
最长有效括号
题目 困难
自己的解法略有点繁琐,我是去计算当前右括号与被 pop 出来的左括号之间的长度,一开始这么做忽略了左边并列的括号,所以加了如下 14、15 行的操作。而题解是计算当前右括号与左括号被 pop 出来后的栈顶括号之间的长度(最开始会放入 -1 位置,如果 pop 后栈为空就放入当前右括号的位置),不需要额外的操作,比较方便。
举个例子比较好理解,()() 这种,我计算的话就是 (3 - 2 + 1) + 2,我用了一个 lens 数组来记录以 i 为结尾的最长有效括号的长度,因为左边 2 个括号之前已经 pop 掉,单纯计算右边括号的 3 - 2 + 1 = 2 的话就错了,所以要加上 lens[1] = 2,结果就为 4。而题解代码事先直接放入 -1,在遍历到最后一个括号的时候就是 3 - (-1) = 4,就一步到位了。对于题解方法的话,如果是 ())(()) 这种,在遍历到第 3 个括号的时候,因为是右括号,所以把最底层的 -1 pop 掉了,这个时候栈为空了,就得放入当前位置 2,以便后续计算。即当遍历到最后一个括号的时候,就能计算最长长度为 6 - 2 = 4 了。
自己代码:
class Solution {
public int longestValidParentheses(String s) {
Deque<Integer> stack = new LinkedList<>();
char[] cs = s.toCharArray();
int[] lens = new int[cs.length];
int max = 0;
for (int i = 0; i < cs.length; i++) {
if (cs[i] == '(')
stack.push(i);
else {
if (!stack.isEmpty()) {
int l = stack.pop();
lens[i] = i - l + 1;
if (i > lens[i])
lens[i] += lens[i - lens[i]];
max = Math.max(max, lens[i]);
}
}
}
return max;
}
}
题解代码:
class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
Deque<Integer> stack = new LinkedList<Integer>();
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push(i);
} else {
stack.pop();
if (stack.isEmpty()) {
stack.push(i);
} else {
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/longest-valid-parentheses/solutions/314683/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
合并 K 个升序链表
题目 困难
用优先级队列来做(一开始没想到),属于使用优先级队列的典型例题。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
PriorityQueue<ListNode> queue = new PriorityQueue<>(lists.length, new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
if (o1.val < o2.val) return -1;
else if (o1.val == o2.val) return 0;
else return 1;
}
});
ListNode dummy = new ListNode(0);
ListNode p = dummy;
for (ListNode node : lists) {
if (node != null) queue.add(node);
}
while (!queue.isEmpty()) {
p.next = queue.poll();
p = p.next;
if (p.next != null) queue.add(p.next);
}
return dummy.next;
}
}
作者:powcai
链接:https://leetcode.cn/problems/merge-k-sorted-lists/solutions/3787/leetcode-23-he-bing-kge-pai-xu-lian-biao-by-powcai/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
路径总和 III
题目 中等
用前缀和(到达当前元素的路径上,之前所有元素的和),具体看题解:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int pathSum(TreeNode root, int sum) {
// key是前缀和, value是大小为key的前缀和出现的次数
Map<Long, Integer> prefixSumCount = new HashMap<>();
// 前缀和为0的一条路径
prefixSumCount.put(0L, 1);
// 前缀和的递归回溯思路
return recursionPathSum(root, prefixSumCount, sum, 0L);
}
/**
* 前缀和的递归回溯思路
* 从当前节点反推到根节点(反推比较好理解,正向其实也只有一条),有且仅有一条路径,因为这是一棵树
* 如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
* 所以前缀和对于当前路径来说是唯一的,当前记录的前缀和,在回溯结束,回到本层时去除,保证其不影响其他分支的结果
* @param node 树节点
* @param prefixSumCount 前缀和Map
* @param target 目标值
* @param currSum 当前路径和
* @return 满足题意的解
*/
private int recursionPathSum(TreeNode node, Map<Long, Integer> prefixSumCount, int target, long currSum) {
// 1.递归终止条件
if (node == null) {
return 0;
}
// 2.本层要做的事情
int res = 0;
// 当前路径上的和
currSum += node.val;
//---核心代码
// 看看root到当前节点这条路上是否存在节点前缀和加target为currSum的路径
// 当前节点->root节点反推,有且仅有一条路径,如果此前有和为currSum-target,而当前的和又为currSum,两者的差就肯定为target了
// currSum-target相当于找路径的起点,起点的sum+target=currSum,当前点到起点的距离就是target
res += prefixSumCount.getOrDefault(currSum - target, 0);
// 更新路径上当前节点前缀和的个数
prefixSumCount.put(currSum, prefixSumCount.getOrDefault(currSum, 0) + 1);
//---核心代码
// 3.进入下一层
res += recursionPathSum(node.left, prefixSumCount, target, currSum);
res += recursionPathSum(node.right, prefixSumCount, target, currSum);
// 4.回到本层,恢复状态,去除当前节点的前缀和数量
prefixSumCount.put(currSum, prefixSumCount.get(currSum) - 1);
return res;
}
}
作者:失火的夏天
链接:https://leetcode.cn/problems/path-sum-iii/solutions/100992/qian-zhui-he-di-gui-hui-su-by-shi-huo-de-xia-tian/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
正则表达式匹配
题目 困难
二维 dp,没啥好说的,主要注意当字符为 * 时的操作。
class Solution {
public boolean isMatch(String ss, String pp) {
// 技巧:往原字符头部插入空格,这样得到 char 数组是从 1 开始,而且可以使得 f[0][0] = true,可以将 true 这个结果滚动下去
int n = ss.length(), m = pp.length();
ss = " " + ss;
pp = " " + pp;
char[] s = ss.toCharArray(), p = pp.toCharArray();
// f(i,j) 代表考虑 s 中的 1~i 字符和 p 中的 1~j 字符 是否匹配
boolean[][] f = new boolean[n + 1][m + 1];
f[0][0] = true;
for (int i = 0; i <= n; i++) {
for (int j = 1; j <= m; j++) {
// 如果下一个字符是 '*',则代表当前字符不能被单独使用,跳过
if (j + 1 <= m && p[j + 1] == '*') continue;
if (i - 1 >= 0 && p[j] != '*') {
// 对应了 p[j] 为普通字符和 '.' 的两种情况
f[i][j] = f[i - 1][j - 1] && (s[i] == p[j] || p[j] == '.');
} else if (p[j] == '*') {
// 对应了 p[j] 为 '*' 的情况
f[i][j] = (j - 2 >= 0 && f[i][j - 2]) || (i - 1 >= 0 && f[i - 1][j] && (s[i] == p[j - 1] || p[j - 1] == '.'));
}
}
}
return f[n][m];
}
}
作者:宫水三叶
链接:https://leetcode.cn/problems/regular-expression-matching/solutions/572931/shua-chuan-lc-dong-tai-gui-hua-jie-fa-by-zn9w/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
盛最多水的容器
题目 中等
双指针,移动短板。移动长板的话面积是必定减小的
class Solution {
public int maxArea(int[] height) {
int i = 0, j = height.length - 1, res = 0;
while(i < j) {
res = height[i] < height[j] ?
Math.max(res, (j - i) * height[i++]):
Math.max(res, (j - i) * height[j--]);
}
return res;
}
}
作者:Krahets
链接:https://leetcode.cn/problems/container-with-most-water/solutions/11491/container-with-most-water-shuang-zhi-zhen-fa-yi-do/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
旋转图像
题目 中等
自己的解法,由外向内一层一层翻转:
class Solution {
private int len;
public void rotate(int[][] matrix) {
len = matrix.length;
for (int i = len; i > 1; i -= 2)
rotateLayer(matrix, i);
}
private void rotateLayer(int[][] matrix, int n) {
int pad = (len - n) / 2;
for (int i = pad; i < pad + n - 1; i++) {
int tmp = matrix[pad][i];
matrix[pad][i] = matrix[len - 1 - i][pad];
matrix[len - 1 - i][pad] = matrix[len - 1 - pad][len - 1 - i];
matrix[len - 1 - pad][len - 1 - i] = matrix[i][len - 1 - pad];
matrix[i][len - 1 - pad] = tmp;
}
}
}
官方解法,将原矩阵切分成四个小矩阵(奇数的话最中间一个点不算在内),然后进行翻转:
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < (n + 1) / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/rotate-image/solutions/526980/xuan-zhuan-tu-xiang-by-leetcode-solution-vu3m/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
岛屿数量
题目 中等
DFS。岛屿类问题通用解法请看:岛屿类问题的通用解法、DFS框架
class Solution {
public int numIslands(char[][] grid) {
int count = 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);
count++;
}
}
}
return count;
}
private void dfs(char[][] grid, int i, int j){
if(i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == '0') return;
grid[i][j] = '0';
dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
}
}
作者:Krahets
链接:https://leetcode.cn/problems/number-of-islands/solutions/16884/number-of-islands-shen-du-you-xian-bian-li-dfs-or-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最小路径和
题目 中等
典型的二维 dp, 的值代表直到走到 (i, j) 的最小路径和。
class Solution {
public int minPathSum(int[][] grid) {
for(int i = 0; i < grid.length; i++) {
for(int j = 0; j < grid[0].length; j++) {
if(i == 0 && j == 0) continue;
else if(i == 0) grid[i][j] = grid[i][j - 1] + grid[i][j];
else if(j == 0) grid[i][j] = grid[i - 1][j] + grid[i][j];
else grid[i][j] = Math.min(grid[i - 1][j], grid[i][j - 1]) + grid[i][j];
}
}
return grid[grid.length - 1][grid[0].length - 1];
}
}
作者:Krahets
链接:https://leetcode.cn/problems/minimum-path-sum/solutions/25943/zui-xiao-lu-jing-he-dong-tai-gui-hua-gui-fan-liu-c/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。