20. 有效的括号
https://leetcode.cn/problems/valid-parentheses/description/?envType=problem-list-v2&envId=2cktkvj
java
class Solution {
public boolean isValid(String s) {
Deque<Character> deque = new LinkedList<>();
char[] arr = s.toCharArray();
int n = arr.length;
if (n % 2 != 0) return false;
for (char c : arr) {
if (c == '(' || c == '{' || c == '[') {
deque.push(c);
} else {
if (deque.isEmpty()) return false;
else {
Character ch = deque.pop();
if (!isValid(ch, c)) return false;
}
}
}
return deque.isEmpty();
}
static boolean isValid(char c1, char c2) {
if (c1 == '(' && c2 == ')') return true;
if (c1 == '{' && c2 == '}') return true;
if (c1 == '[' && c2 == ']') return true;
return false;
}
}
用一个栈就行
19. 删除链表的倒数第 N 个结点
java
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode cur = head;
// 计算一下总长度
int cnt = 0;
while (cur != null) {
cnt ++;
cur = cur.next;
}
// 找要删除的位置
int t = cnt - n + 1;
// 要删除的是头节点
if (t == 1) return head.next;
cur = head;
for (int i = 1; i < t - 1; i++) {
cur = cur.next;
}
// 现在cur指向的是 t - 1 的位置
cur.next = cur.next.next;
return head;
}
}
解题思路:先看一下总共有几个节点,然后推算出要删除的节点是下标,然后先找到要删除的节点的前一个节点,做一下cur.next = cur.next.next;就行
17. 电话号码的字母组合
java
class Solution {
static String[] map = new String[] {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
static List<String> res;
public List<String> letterCombinations(String digits) {
res = new ArrayList<>();
if (digits.isEmpty()) return res;
dfs(digits.toCharArray(), 0, new StringBuilder());
return res;
}
static void dfs(char[] arr, int idx, StringBuilder sb) {
if (idx == arr.length) {
res.add(sb.toString());
return;
}
int n = arr[idx] - '0';
String str = map[n];
int len = str.length();
for (int i = 0; i < len; i++) {
sb.append(str.charAt(i));
dfs(arr, idx + 1, sb);
sb.deleteCharAt(sb.length() - 1);
}
}
}
解题思路:回溯法,用idx标识当前是在解决digits中第几个字符的枚举情况
15. 三数之和
https://leetcode.cn/problems/3sum/description/?envType=problem-list-v2&envId=2cktkvj
java
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int n = nums.length;
Arrays.sort(nums);
for (int i = 0; i < n - 2; i++) {
// 去重
if (i > 0 && nums[i] == nums[i-1]) continue;
int l = i + 1;
int r = n - 1;
while(l < r) {
int sum = nums[i] + nums[l] + nums[r];
if (sum == 0) {
res.add(Arrays.asList(nums[i], nums[l], nums[r]));
// 去重
while (l < r && nums[l] == nums[l + 1]) l ++;
while (l < r && nums[r] == nums[r - 1]) r --;
l ++; r --;
}
else if (sum > 0) r --;
else l ++;
}
}
return res;
}
}
解题思路:相当于三个指针,第一个指针就是从头到尾扫一遍,另外两个指针分别在 i+1 和 n-1 的位置开始
11. 盛最多水的容器
java
class Solution {
public int maxArea(int[] height) {
int n = height.length;
int l = 0;
int r = n - 1;
int ans = 0;
while (l < r) {
int h = Math.min(height[l], height[r]);
ans = Math.max(ans, h * (r - l));
// 去掉一些无效选项
while(l < r && height[l] <= h) l ++;
while(l < r && height[r] <= h) r --;
}
return ans;
}
}
解题思路:这道题其实就是典型的双指针,我们只需要定义两个指针分别枚举容器的左右木板就行
每次取小的那个作为高度,就能计算出来了
然后进行指针移动的时候是不是可以手动的去掉一些无效的选项,比如指针下一步指向的高度还没当前的高度高,又因为指针在往中间移动,宽度肯定变小,那么这个选项是不是可以直接跳过
10. 正则表达式匹配
java
class Solution {
public boolean isMatch(String s, String p) {
int n = s.length();
int m = p.length();
s = " " + s;
p = " " + p;
boolean[][] dp = new boolean[n + 1][m + 1];
dp[0][0] = true;
for (int i = 0; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (j + 1 <= m && p.charAt(j + 1) == '*') continue;
if (p.charAt(j) == '*') {
dp[i][j] = (j >= 2 && dp[i][j-2]) || (i >= 1 && dp[i-1][j]) && (s.charAt(i) == p.charAt(j-1) || p.charAt(j-1) == '.');
} else {
dp[i][j] = (i >= 1 && j >= 1 && dp[i-1][j-1]) && (s.charAt(i) == p.charAt(j) || p.charAt(j) == '.');
}
}
}
return dp[n][m];
}
}
当 p[j] 不是 '*' 的时候就是直接看 dp[i-1][j-1] 是否为true且 s[i] 是否等于 p[j] 或是 p[j] 等于 '.'
当 p[j] 是 '*' 的时候,就需要推到一下地推公式
考虑一下 p 中的 这个 * 匹配 s 中 的字符个数,需要从匹配0次,匹配1次,匹配2次,......
那么就可以退出如下式子(然后之所以是取 j - 2 的原因是因为,*是和它前一个字符搭配的,不是单独去匹配):
式子1:f(i, j) = f(i, j - 2) || (f(i-1, j - 2) && (s[i] == p[j-1])) || (f(i-2, j-2) && (s[i-1] ==p[j-1]) && s[i] ==p[j-1])......
式子2:f(i-1, j) = f(i-1, j - 2) || (f(i-2, j - 2) && (s[i-1] == p[j-1])) || (f(i-3, j-2) && (s[i-2] ==p[j-1]) && s[i-1] ==p[j-1])......
可以发现 高亮部分是能对应上的,那么是不是可以退出 式子1后面部分可以替换成 f(i-1, j) && (s[i] ==p[j-1])
整合一下结论:f(i, j) = f(i, j - 2) || f(i-1, j) && (s[i] ==p[j-1])
最后要注意的就是出现下标的时候需要考虑越界的情况,代码里面也有具体的实现,可以直接看代码理解