刷穿力扣(1~30)
更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验
1. 两数之和
- 哈希表
- 遍历数组,同时用
HashMap维护已出现过的数及其下标 - 若当前的数
nums[i]满足target - nums[i]曾经出现过,则直接返回 - 否则将其加入到哈希表中。
class Solution {public int[] twoSum(int[] nums, int target) {HashMap<Integer, Integer> st = new HashMap<>();for (int i = 0; i < nums.length; i ++) {if (st.containsKey(target - nums[i])) {return new int[]{st.get(target - nums[i]), i};}else st.put(nums[i], i);}return null;}
}
2. 两数相加
- 链表
- 使用
res维护链表,ans指向res头结点 - 遍历链表
l1和l2,取得当前节点的值分别为a,b,并用base记录进位 - 则,新的
res节点为a + b + base % 10,base = (a + b + base) / 10 - 不断将
res更新为res.next,最后若base != 0则补上进位即可
/*** 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 addTwoNumbers(ListNode l1, ListNode l2) {ListNode res = new ListNode();ListNode ans = res;int base = 0;while (l1 != null || l2 != null) {int a = l1 == null ? 0 : l1.val;int b = l2 == null ? 0 : l2.val;int sum = a + b + base;base = sum / 10;res.next = new ListNode(sum % 10);res = res.next;if (l1 != null) l1 = l1.next;if (l2 != null) l2 = l2.next;}if (base != 0) res.next = new ListNode(base);return ans.next;}
}
3. 无重复字符的最长子串
- 滑动窗口
- 维护一个集合或布尔数组,保存当前出现过的字符
- 设窗口左边界为
i,右边界为j,当前字符为s[j] - 当出现重复字符时窗口左边界
i向右移动,并不断将vis[i]移除,直到vis[i] === s[j]被排除 - 持续更新窗口最大长度
j - i + 1即为答案
class Solution {public int lengthOfLongestSubstring(String s) {int ans = 0;HashSet<Character> st = new HashSet<>();for (int i = 0, j = 0; i < s.length(); i++) {while (st.contains(s.charAt(i)) && j < i) {st.remove(s.charAt(j++));}st.add(s.charAt(i));ans = Math.max(ans, st.size());}return ans;}
}
优化:
- 上述两种实现滑窗的方式都需要“持续性地移动边界”这个操作
- 不如换一种思路——使用
HashMap来更新每个字符最近一次出现的索引 - 当我们遍历字符串
s时,仍使用i和j来表示当前无重复字符子串的起始位置和结束位置,初始时i = j = 0。 - 在每一步迭代中,检查当前字符
s[j]是否已经在HashMap中存在:- 如果存在,则更新左指针
i移动到重复字符的下一个位置,保证左指针i始终指向当前无重复字符子串的起始位置。
- 如果存在,则更新左指针
- 将
s[j]其加入HashMap中,并更新窗口最大长度j - i + 1。 - 最后,右指针
j向右移动一位,继续遍历字符串s,直到右指针j到达字符串的末尾 - 这样只需更新字符出现的索引即可,无需重复遍历。
class Solution {public int lengthOfLongestSubstring(String s) {int ans = 0;HashMap<Character, Integer> vis = new HashMap<>();int n = s.length();int i = 0, j = 0;while (i < n && j < n) {if (vis.containsKey(s.charAt(j))) {i = Math.max(vis.get(s.charAt(j)) + 1, i); // 更新索引,取较大值为新的左指针位置}vis.put(s.charAt(j), j); ans = Math.max(ans, j - i + 1);j ++;}return ans;}
}
4. 寻找两个正序数组的中位数
- 暴力
- 合并两个有序数组,然后取中位数即可
class Solution {public double findMedianSortedArrays(int[] nums1, int[] nums2) {int n = nums1.length;int m = nums2.length;int[] nums = new int[n + m];int idx = 0, idx1 = 0, idx2 = 0;while (idx1 < n && idx2 < m) {if (nums1[idx1] < nums2[idx2]) nums[idx ++] = nums1[idx1 ++];else nums[idx ++] = nums2[idx2 ++];}while (idx1 < n) nums[idx ++] = nums1[idx1 ++];while (idx2 < m) nums[idx ++] = nums2[idx2 ++];if (((n + m) & 1) == 1) return nums[(n + m) >> 1];else return (double)(nums[(n + m) >> 1] + nums[(n + m - 1) >> 1]) / 2;}
}
5. 最长回文子串
- dp
- 在这种情况下,我们可以定义一个二维数组
dp,其中dp[i][j]表示从索引i到索引j的子串是否是回文。 - 根据回文的定义,我们可以得出以下状态转移方程:
dp[i][j] = true,如果i == j(单个字符是回文)dp[i][j] = true,如果s[i] == s[j]且dp[i + 1][j - 1] == true(首尾字符相等且去掉首尾后的子串是回文)dp[i][j] = false,其他情况
- 基于这个状态转移方程,我们可以使用动态规划来填充
dp数组。然后,我们可以根据dp数组找到最长回文子串。
class Solution {public String longestPalindrome(String s) {int n = s.length();boolean[][] dp = new boolean[n][n];String ans = "";// // 单个字符或相同的两个是回文for (int i = 0; i < n; i++) {if (ans.length() <= 1) ans = s.substring(i, i + 1);dp[i][i] = true;if (i < n - 1 && s.charAt(i) == s.charAt(i + 1)) {dp[i][i + 1] = true;ans = s.substring(i, i + 2);}}// 长度大于2的子串for (int len = 3; len <= n; len ++) {for (int i = 0; i <= n - len; i ++) {int j = i + len - 1;if (j - 1 >= 0 && s.charAt(i) == s.charAt(j) && dp[i + 1][j - 1]) {dp[i][j] = true;ans = s.substring(i, j + 1);}}}return ans;}
}
6. N 字形变换
-
模拟
-
首先排除特殊情况,当
numRows <= 1 || s.length() <= numRows直接返回s -
其他情况可以利用偏移量对字符坐标进行模拟,将结果存到二维数组中
若使用
StringBuilder来构造模拟结果集,此时发现仅需要行数变化即可 -
则最终将结果集转换为字符串返回即可
class Solution {public String convert(String s, int numRows) {int n = s.length();if (numRows <= 1 || n <= numRows) return s; // 特殊情况直接返回int l = 0, d = 0; // l 记录当前所在行数,d 记录当前的方向StringBuilder mp[] = new StringBuilder[numRows]; // 结果集for (int i = 0; i < numRows; i ++) { // 初始化结果集,每一行都是一个 StringBuildermp[i] = new StringBuilder();}mp[0].append(s.charAt(0)); // 将第一个字符先加入for (int i = 1; i < n; i ++) { // 从第二个字符开始遍历if ((d == 0 && l == numRows - 1) || (d == 1 && l == 0)) d = 1 - d; // 判断方向改变条件l += d == 0 ? 1 : -1; mp[l].append(s.charAt(i));}StringBuilder ans = new StringBuilder();for (StringBuilder st : mp) {ans.append(st.toString());}return ans.toString();}
}
7. 整数反转
- 模拟
- 循环倒除得到每一位,再反向乘加即可
- 最大值通过
Integer.MAX_VALUE获取,最小值可以通过Integer.MIN_VALUE获取 - 当
ans大于最值除以 10 10 10 的时候,下一步计算会溢出 - 由于输入的数在
Integer范围内,可以证明,在计算结束前,不存在ans超过最值的情况
class Solution {public int reverse(int x) {int ans = 0;int maxn = Integer.MAX_VALUE / 10;int minn = Integer.MIN_VALUE / 10;while (x != 0) {if (ans > maxn || ans < minn) return 0;int t = x % 10;ans = ans * 10 + t;x /= 10;}return ans;}
}
8. 字符串转换整数 (atoi)
- 模拟
- 首先去除空格,然后判断起始字符是否为
+或- - 判断溢出时,最大值通过
Integer.MAX_VALUE获取,最小值可以通过Integer.MIN_VALUE获取 - 当
ans大于最值除以 10 10 10 的时候,下一步计算会溢出 - 当
ans等于最值除以 10 10 10 的时候,用最值模 10 10 10 的余数判断下一步计算是否溢出
class Solution {public int myAtoi(String s) {String st = s.trim();int ans = 0;boolean flag = false; // 结果符号: false 为 +,true 为 -// 计算最值 / 10int maxn = Integer.MAX_VALUE / 10;int minn = Integer.MIN_VALUE / 10;// 计算最值 % 10int maxt = Integer.MAX_VALUE % 10;int mint = Integer.MIN_VALUE % 10;for (int i = 0; i < st.length(); i ++) {char p = st.charAt(i);if (p == '-' && i == 0) flag = true;else if (p == '+' && i == 0) continue;else if (p >= '0' && p <= '9') {int t = p - '0';// 判断是否溢出if (flag && (- ans < minn || (- ans == minn && - t < mint))) return Integer.MIN_VALUE;if (!flag && (ans > maxn || (ans == maxn && t > maxt))) return Integer.MAX_VALUE; ans = ans * 10 + t;}else break;}if (flag) ans *= -1;return ans;}
}
9. 回文数
- 暴力
class Solution {public boolean isPalindrome(int x) {if (x < 0) return false;int ans = 0;int y = x;while(x != 0) {ans = ans * 10 + (x % 10);x /= 10;}return y == ans;}
}
10. 正则表达式匹配
- dp
- 状态表示:
- 状态
dp[i][j]表示字符串s的前i个字符和字符规律p的前j个字符是否匹配。
- 状态
- 状态计算:
- 当
s的第i个字符和p的第j个字符相等,或者p的第j个字符为.时说明当前字符匹配,即dp[i][j] = dp[i-1][j-1]。 - 当
p的第j个字符为*时,需要考虑两种情况,满足其一即匹配:*匹配零个前面的元素,即dp[i][j] = dp[i][j-2]。*匹配一个或多个前面的元素时,要满足s.charAt(i) == p.charAt(j - 1) || s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.',即dp[i][j] = dp[i-1][j]。
- 当
- 初始化:
- 两者为空也为匹配,即
dp[0][0] = true - 对于
p,若p.charAt(i) == '*'说明当前星号可以匹配前面的字符零次,即dp[0][i] = dp[0][i - 2]。
- 两者为空也为匹配,即
class Solution {public boolean isMatch(String s, String p) {int n = s.length(), m = p.length();boolean[][] dp = new boolean[n + 1][m + 1];dp[0][0] = true;for (int i = 1; i <= m; i ++) {if (p.charAt(i - 1) == '*') dp[0][i] = dp[0][i - 2];}for (int i = 1; i <= n; i ++) {for (int j = 1; j <= m; j ++) {// i, j 从 1 开始,s, p 需要偏移 -1if (s.charAt(i - 1) == p.charAt(j - 1) || p.charAt(j - 1) == '.') {dp[i][j] = dp[i - 1][j - 1];}else if (p.charAt(j - 1) == '*') {dp[i][j] = dp[i][j - 2] || (dp[i - 1][j] && (s.charAt(i - 1) == p.charAt(j - 2) || p.charAt(j - 2) == '.'));}}}return dp[n][m];}
}
11. 盛最多水的容器
- 双指针
- 设
l为容器的左边界,r为右边界,初始化l = 0, r = height.length - 1 - 当
height[l] < height[l + 1]或height[r] < height[r - 1]时,容器体积可能增大
class Solution {public int maxArea(int[] height) {int n = height.length;int l = 0, r = n - 1;int ans = Math.min(height[l], height[r]) * (n - 1);while(l < r) {// 优化:通过两个 while 快速找到大于等于当前最低边界的 l 和 r 所在位置,减少不必要的计算int t = Math.min(height[l], height[r]);while (l < r && height[l] <= t) l ++;while (r > l && height[r] <= t) r --;ans = Math.max(ans, Math.min(height[l], height[r]) * (r - l));}return ans;}
}
12. 整数转罗马数字
- 模拟
- 按照题目规则转换
- 每次都选择尽量大的位数进行转换即可
public class Solution {public String intToRoman(int num) {int[] nums = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};String[] romans = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};StringBuilder stringBuilder = new StringBuilder();for (int i = 0; i < nums.length; i++) {while (num >= nums[i]) {stringBuilder.append(romans[i]);num -= nums[i];}}return stringBuilder.toString();}
}
13. 罗马数字转整数
- 模拟
- 反过来模拟即可
- 从右至左,若当前罗马字母比右边的小,则说明需要减去,否则做加法即可
class Solution {public int romanToInt(String s) {int[] nums = {1000, 500, 100, 50, 10, 5, 1};char[] romans = {'M', 'D', 'C', 'L', 'X', 'V', 'I'};int n = s.length();int flag = getIdx(s.charAt(n - 1), romans);int num = nums[flag];for (int i = n - 2; i >= 0; i --) {int t = getIdx(s.charAt(i), romans);if (nums[t] < nums[flag]) num -= nums[t];else num += nums[t];flag = t;}return num;}private static int getIdx(char p, char[] romans) {for (int i = 0; i < romans.length; i ++) {if (p == romans[i]) return i;}return -1;}
}
14. 最长公共前缀
- 模拟
- 初始化公共前缀为
res = strs[0] - 从
i = 1循环枚举strs[i]判断是否满足strs[i].startsWith(res) - 若不满足则循环找到
res的字串,直到满足为止
class Solution {public String longestCommonPrefix(String[] strs) {String res = strs[0];for (int i = 1; i < strs.length; i ++) {while (!strs[i].startsWith(res)){res = res.substring(0, res.length() - 1);}}return res;}
}
15. 三数之和
- 双指针
- 首先将数组从小到大排序,使得数组单调
- 则要寻找三数之和为 0 0 0,必须保证
nums[i] < 0,左指针j = i + 1,右指针k = n - 1,即sum = nums[i] + nums[j] + nums[k] - 当
sum < 0说明nums[j]太小,则指针j ++ - 当
sum > 0说明nums[k]太大,则指针k -- - 当
sum == 0说明满足条件,此时需要将重复的nums[j]和nums[k]排除,即两个指针收缩,直到不再重复。
class Solution {public List<List<Integer>> threeSum(int[] nums) {int n = nums.length;List<List<Integer>> ans = new ArrayList<>();// 排除特殊情况if (n < 3) return ans;Arrays.sort(nums);for (int i = 0; i < n; i ++) {// nums[i] > 0 之后不再存在满足条件的结果,直接返回if (nums[i] > 0) return ans;int j = i + 1, k = n - 1;while (j < k) {int sum = nums[i] + nums[j] + nums[k];if (sum < 0) j ++;else if (sum > 0) k --;else {List<Integer> list = new ArrayList<>();list.add(nums[i]); list.add(nums[j]); list.add(nums[k]);ans.add(list);// 收缩指针,去重while (j + 1 <= k && nums[j] == nums[j + 1]) j ++; while (j + 1 <= k && nums[k] == nums[k - 1]) k --;j ++; k --;}}// 去除重复的 nums[i]while (i + 1 < n && nums[i + 1] == nums[i]) i ++;}return ans;}
}
16. 最接近的三数之和
- 双指针
- 和上一题思路相近,不同的是我们需要维护一个
sum = nums[i] + nums[j] + nums[k]和target差值的最小值
class Solution {public int threeSumClosest(int[] nums, int target) {Arrays.sort(nums);int n = nums.length;int flag = Integer.MAX_VALUE;int ans = 0;for (int i = 0; i < n; i++) {int j = i + 1, k = n - 1;while (j < k) {int sum = nums[i] + nums[j] + nums[k];int t = Math.abs(sum - target);if (t < flag) {flag = t;ans = sum;}if (sum < target) j ++;else k --;}while (i + 1 < n && nums[i] == nums[i + 1]) i ++;}return ans;}
}
17. 电话号码的字母组合
- DFS
- 递归的每一层视作对
digits[0]所对应映射"xxx"的枚举 - 每一层中,利用循环枚举
"xxx"的每一位,然后继续递归 - 当递归的层数
u == digits.length()时将当前累计的字符串加入答案列表并回溯
class Solution {private static String vis[] = new String[]{"", "", // 空出 0, 1 的位置"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};public List<String> letterCombinations(String digits) {List<String> ans = new ArrayList<>();if (digits.length() == 0) return ans;// res 记录当前累计的字符StringBuilder res = new StringBuilder();dfs(digits, 0, res, ans);return ans;}private void dfs(String digits, int u, StringBuilder res, List<String> ans) {// 满足长度 ans 添加 res 并返回if (u == digits.length()) {ans.add(res.toString());return ;}int digit = digits.charAt(u) - '0';String letters = vis[digit];for (int i = 0; i < letters.length(); i ++) {res.append(letters.charAt(i)); // 将枚举的 xxx 的第 i 个字符累计dfs(digits, u + 1, res, ans); // 递归到 u + 1 层res.deleteCharAt(res.length() - 1); // 恢复现场}}
}
18. 四数之和
- 双指针
- 三数之和基础上,枚举第一个数
nums[i],剩下三个数计算三数之和为target - nums[i] - 注意数据范围会爆
int - 有意思吗力扣出题人出这种脑瘫题目😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅😅
class Solution {public List<List<Integer>> fourSum(int[] nums, int target) {List<List<Integer>> ans = new ArrayList<>();int n = nums.length;// 排除特殊情况if (n < 4) return ans;Arrays.sort(nums);for (int i = 0; i < n; i ++) {int ftarget = target - nums[i];for (int j = i + 1; j < n; j ++) {int k = j + 1, l = n - 1;while (k < l) {long sum = (long)nums[j] + nums[k] + nums[l]; // 使用 long 类型存储和if (sum < ftarget) k ++;else if (sum > ftarget) l --;else {List<Integer> list = new ArrayList<>();list.add(nums[i]); list.add(nums[j]); list.add(nums[k]); list.add(nums[l]);ans.add(list);while (k + 1 <= l && nums[k] == nums[k + 1]) k ++;while (k + 1 <= l && nums[l] == nums[l - 1]) l --;k ++; l --;}}while (j + 1 < n && nums[j + 1] == nums[j]) j ++;} while (i + 1 < n && nums[i + 1] == nums[i]) i ++;}return ans;}
}
19. 删除链表的倒数第 N 个结点
- 模拟
- 遍历一遍链表得到链表长度
size - 特判:当
size <= 1或n - size == 0时返回null - 遍历链表,当
idx == n - size - 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 removeNthFromEnd(ListNode head, int n) {int size = 0;for (ListNode i = head; i != null; i = i.next) size ++;if (size <= 1) return null;if (n - size == 0) return head.next;int idx = 0;for (ListNode i = head; i != null; i = i.next) {if (idx == size - n - 1) {i.next = i.next.next;}idx ++;}return head;}
}
优化:
- 双指针
- 为防止删除头结点的情况出现,利用
ListNode ans指向head - 设
ListNode指针l和r都指向ans - 将
r先移动n + 1的距离,然后再让l和r同时移动 - 当
r.next == null说明l位于要被删除节点的前一个节点 - 删除操作即为
l.next = l.next.next
/*** 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 removeNthFromEnd(ListNode head, int n) {ListNode ans = new ListNode();ans.next = head;ListNode l = ans, r = ans;for (int i = 0; i < n + 1; i ++) r = r.next;while (r != null) {l = l.next; r = r.next;}l.next = l.next.next; // 删除操作return ans.next;}
}
20. 有效的括号
- 模拟
- 用一个栈维护当前存在的左括号
- 若
s.length()为奇数则不可能匹配,否则遍历s - 当前字符为左括号:直接进栈
- 当前字符为右括号:若栈为空或栈顶不匹配直接返回
false,否则弹出栈顶 - 最后判断栈是否为空,若不为空说明还有剩余的左未匹配,返回
false
class Solution {public boolean isValid(String s) {if ((s.length() & 1) == 1) return false;Stack<Character> st = new Stack<>();for (char p : s.toCharArray()) {if (p == '(') st.push(')');else if (p == '[') st.push(']');else if (p == '{') st.push('}');else {if (st.isEmpty()) return false;if (p == ')' || p == ']' || p == '}') {if (st.peek() == p) st.pop();else return false;}}}return st.isEmpty();}
}
21. 合并两个有序链表
- 模拟
- 比较节点的值,较小的尾插
- 有剩余的全部合并
/*** 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 mergeTwoLists(ListNode list1, ListNode list2) {ListNode ans = new ListNode();ListNode st = ans;while (list1 != null && list2 != null) {if (list1.val < list2.val) {st.next = list1;list1 = list1.next;} else {st.next = list2;list2 = list2.next;}st = st.next;}st.next = list1 != null ? list1 : list2;return ans.next;}
}
22. 括号生成
- DFS
- 递归的每一层视作添加的括号类型
- 由于必须保证括号匹配,则必须满足起始括号为
( - 添加
(的数量l必须满足l < n,添加)的数量r必须满足r < l
class Solution {public List<String> generateParenthesis(int n) {List<String> ans = new ArrayList<>();dfs(ans, "", 0, 0, n);return ans;}private void dfs(List<String> ans, String current, int l, int r, int n) {// 满足长度返回if (l + r == n * 2) {ans.add(current);return;}if (l < n) dfs(ans, current + "(", l + 1, r, n);if (r < l) dfs(ans, current + ")", l, r + 1, n);}
}
23. 合并 K 个升序链表
- 模拟
- 循环枚举链表数组里的链表,一一合并,时间复杂度 O ( n k 2 ) \mathcal{O}(nk^2) O(nk2)
- 特判,当
lists.length == 0时返回null
/*** 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 mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0) return null;// 全部合并到 lists[0] 上面ListNode ans = lists[0];for (int i = 1; i < n; i ++) {ans = mergeTwoLists(ans, lists[i]);}return ans;}// 合并两个链表private static ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode ans = new ListNode();ListNode st = ans;while (list1 != null && list2 != null) {if (list1.val < list2.val) {st.next = list1;list1 = list1.next;} else {st.next = list2;list2 = list2.next;}st = st.next;}st.next = list1 != null ? list1 : list2;return ans.next;}
}
优化:
- 归并
- 用两两归并代替循环合并,时间复杂度 O ( n k log 2 k ) \mathcal{O}(nk \log_2^k) O(nklog2k)
- 每一轮的合并中,从第一个链表开始,将它与它后面的第
cnt个链表合并,然后将合并结果存储在第一个链表的位置上。 - 然后继续从第一个链表开始,将它与它后面的第
2 * cnt个链表合并,再将合并结果存储在第一个链表的位置上。 - 以此类推,直到所有链表都被合并即
cnt >= n为止。
/*** 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 mergeKLists(ListNode[] lists) {int n = lists.length;if (n == 0) return null;// 合并步长,每一轮合并后 cnt *= 2;int cnt = 1;while (cnt < n) {// 两两合并for (int i = 0; i + cnt < n; i += cnt * 2) {lists[i] = mergeTwoLists(lists[i], lists[i + cnt]);}cnt *= 2;}// 最终全部合并到 lists[0]return lists[0];}private static ListNode mergeTwoLists(ListNode list1, ListNode list2) {ListNode ans = new ListNode();ListNode st = ans;while (list1 != null && list2 != null) {if (list1.val < list2.val) {st.next = list1;list1 = list1.next;} else {st.next = list2;list2 = list2.next;}st = st.next;}st.next = list1 != null ? list1 : list2;return ans.next;}
}
24. 两两交换链表中的节点
- 模拟
- 用一个
ListNode t指向当前要交换的节点对中的左节点(下一次交换的前一个节点) - 则每次交换过程中,左右节点分别为
l = t.next, r = t.next.next - 先用
t指向r,再用l指向r指向的节点完成l替换r - 再让
r指向l完成r替换l - 最后
t指向l,即下一次交换的前一个节点
/*** 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 swapPairs(ListNode head) {ListNode ans = new ListNode();ans.next = head;ListNode t = ans;while (t.next != null && t.next.next != null) {ListNode l = t.next; // t -> l -> r -> k -> ...ListNode r = t.next.next;t.next = r; // t -> rl.next = r.next; // l -> kr.next = l; // r -> lt = l; // r -> l(t) -> k -> ...}return ans.next;}
}
25. K 个一组翻转链表
- 模拟
- 本质上就是修改链表节点指向
- 即,每组的第一个节点指向本组最后节点指向的节点
- 然后每组第一个节点后的节点依次指向前节点
如图:
k = 3
node1 -> node2 -> node3 -> node4 -> null
node1 -> node4
node2 -> node1
node3 -> node2
/*** 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 reverseKGroup(ListNode head, int k) {ListNode ans = new ListNode();ans.next = head;ListNode t = ans;ListNode st = findNode(t, k);while (t.next != null && st.next != null) {ListNode l = t.next;ListNode r = st.next;// 修改节点指向for (int i = 0; i < k; i++) {ListNode temp = l.next;l.next = r; // 每组第一个节点指向本组最后的节点指向的节点r = l;l = temp;}// 为下一次循环做准备ListNode temp = t.next;t.next = r;temp.next = l;t = temp;st = findNode(t, k);}return ans.next;}// 找到每组最后一个节点private ListNode findNode(ListNode t, int k) {k --;while (k-- > 0) {if (t.next != null) t = t.next;else break;}return t;}
}
26. 删除有序数组中的重复项
- 模拟
- 由于数组单调且元素唯一,显然有
nums[i] < nums[i + 1] - 设
idx为去重后数组的下标,flag当前维护的值 - 若
flag < nums[i]说明元素不重复,则修改nums[idx ++] = nums[i]即可
class Solution {public int removeDuplicates(int[] nums) {int n = nums.length;int idx = 0, flag = Integer.MIN_VALUE;for (int i = 0; i < n; i ++) {if (flag < nums[i]) {flag = nums[i]; // 更新 flagnums[idx ++] = nums[i];}}return idx;}
}
27. 移除元素
- 模拟
- 设
idx为去重后的数组下标 - 若
nums[i] == val直接跳过 - 否则直接修改
nums[idx ++] = nums[i]即可
class Solution {public int removeElement(int[] nums, int val) {int n = nums.length;int idx = 0;for (int i = 0; i < n; i ++) {if (nums[i] == val) continue;nums[idx ++] = nums[i];}return idx;}
}
28. 找出字符串中第一个匹配项的下标
- AC自动机
- 这道题非常的难啊!
- 既然出题人本意不想让我们循环暴力、直接调库或者KMP这种简单的算法
- 那我们只好按出题人的意思来了,直接祭大招好了😅😅😅
class Solution {class Node {Node[] children; // 子节点数组Node fail; // 失败指针List<Integer> indices; // 匹配成功的模式串在原字符串中的起始位置列表public Node() {children = new Node[26]; // 26个英文字母fail = null;indices = new ArrayList<>();}}public int strStr(String haystack, String needle) {if (needle.isEmpty()) {return 0;}Node root = buildTrie(needle); // 构建Trie树buildFailure(root); // 构建失败指针Node curr = root;for (int i = 0; i < haystack.length(); i++) {char c = haystack.charAt(i);curr = getNextState(curr, c); // 获取下一个状态if (curr.indices.size() > 0) { // 匹配成功,返回模式串在原字符串中的起始位置return i - needle.length() + 1;}}return -1; // 未找到匹配的模式串}private Node buildTrie(String needle) {Node root = new Node();Node curr = root;for (int i = 0; i < needle.length(); i++) {char c = needle.charAt(i);if (curr.children[c - 'a'] == null) {curr.children[c - 'a'] = new Node();}curr = curr.children[c - 'a'];}curr.indices.add(0); // 将模式串的起始位置添加到当前节点的起始位置列表中return root;}private void buildFailure(Node root) {Queue<Node> queue = new LinkedList<>();for (int i = 0; i < 26; i++) { // 初始化根节点的子节点的失败指针if (root.children[i] != null) {root.children[i].fail = root;queue.offer(root.children[i]);} else {root.children[i] = root;}}while (!queue.isEmpty()) {Node curr = queue.poll();for (int i = 0; i < 26; i++) {if (curr.children[i] != null) {Node child = curr.children[i];Node fail = curr.fail;while (fail != null && fail.children[i] == null) { // 失败指针回溯fail = fail.fail;}child.fail = fail != null ? fail.children[i] : root; // 更新失败指针child.indices.addAll(child.fail.indices); // 将失败指针节点的起始位置列表合并到当前节点queue.offer(child);}}}}private Node getNextState(Node curr, char c) {while (curr != null && curr.children[c - 'a'] == null) { // 失败指针回溯curr = curr.fail;}if (curr == null) {return curr;} else {return curr.children[c - 'a'];}}
}
29. 两数相除
- 位运算
- 特殊情况:被除数
a等于Integer.MIN_VALUE且除数b等于 − 1 -1 −1 时结果会溢出,直接返回Integer.MAX_VALUE。 - 从第 31 31 31 位开始逐位检查被除数
a是否大于等于b的左移结果(b << i)。 - 如果被除数大于等于
(b << i),则执行以下操作:- 将被除数
a减去(b << i),相当于从被除数中减去当前位的除数。 - 检查结果变量
res是否大于Integer.MAX_VALUE - (1 << i),如果是,则结果溢出,直接返回Integer.MIN_VALUE。 - 将结果变量
res加上1 << i,相当于将当前位的商加到结果中。
- 将被除数
- 循环结束后,返回结果变量
res。
class Solution {public int divide(int a, int b) {if (a == 0) return 0;if (a == Integer.MIN_VALUE && b == -1) return Integer.MAX_VALUE;boolean flag = (a > 0) ^ (b > 0); // 判断符号是否相同,用于确定符号a = Math.abs(a); b = Math.abs(b);int res = 0;for (int i = 31; i >= 0; i--) {if ((a >>> i) - b >= 0) { // a >= (b << i)a -= (b << i);if (res > Integer.MAX_VALUE - (1 << i)) {return Integer.MIN_VALUE;}res += (1 << i);}}return flag ? -res : res;}
}
30. 串联所有单词的子串
- 滑窗
- 用
StringBuilder模拟窗口 - 当窗口长度
sb.length() == words.length * words[0].length()需要判断是否满足 - 用
HashMap<String, Integer>统计words里面的单词及其词频 - 然后依次截取
sb长度为words[0].length()的片段,判断是否存在且频数照应 - 若满足条件,将下标加入答案中
class Solution {public List<Integer> findSubstring(String s, String[] words) {int len = words.length * words[0].length(); // 计算目标子串的长度int n = s.length();StringBuilder sb = new StringBuilder(); // 用于存储当前窗口的子串List<Integer> ans = new ArrayList<>();HashMap<String, Integer> vis = new HashMap<>(); // 存储目标子串中每个单词的出现次数for (int i = 0; i < words.length; i++) {vis.put(words[i], vis.getOrDefault(words[i], 0) + 1);}for (int i = 0; i < n; i ++) {if (sb.length() == len) sb.deleteCharAt(0); // 长度足够,删除最左侧的字符sb.append(s.charAt(i)); // 将当前字符添加到窗口的子串中if (sb.length() == len) {HashMap<String, Integer> sbwords = new HashMap<>(vis);// 检查当前窗口的子串是否符合要求if (check(sb.toString(), sbwords, words[0].length())) ans.add(i - len + 1);}}return ans;}private static boolean check(String sb, HashMap<String, Integer> sbwords, int cnt) {// 以单词长度为步长,遍历当前窗口的子串for (int i = 0; i < sb.length(); i += cnt) { String res = sb.substring(i, i + cnt);// 如果目标子串中包含当前单词if (sbwords.containsKey(res)) {int t = sbwords.get(res);if (t > 0) sbwords.put(res, t - 1);else return false;} else return false;}return true;}
}
相关文章:
刷穿力扣(1~30)
更好的阅读体验 \huge{\color{red}{更好的阅读体验}} 更好的阅读体验 1. 两数之和 哈希表遍历数组,同时用 HashMap 维护已出现过的数及其下标若当前的数 nums[i] 满足 target - nums[i] 曾经出现过,则直接返回否则将其加入到哈希表中。 class Solution …...
栈和队列的基本操作
(一)实验类型:设计性 (二)实验目的: 1.掌握栈和队列的抽象数据类型。 2.掌握实现栈和队列的各种操作的算法。 3.理解栈与递归的关系。 4. 掌握队列的链式存贮结构及基…...
变压器绕组断股往往导致直流电阻不平衡率超标
变压器绕组断股往往导致直流电阻不平衡率超标, 例如, 某电厂 SFPSL—12000/220 型主变压器, 色谱分析结果发现总烃含量急剧增长, 测直流电阻, 其结果是高、 低压侧与制造厂及历年的数值相比较无异常, 但中压…...
stack和queque
1.stack 1.1定义 T 是容器内的数据类型; Container是数据类型的容器适配器 vector和list和stack的区别 1.2 stack的功能 注意这里没有迭代器;原因stack是先进后出的规律;这就规定该容器不可以随机访问; 2. queue...
信息学 学习/复习 抽签器(附源码)
问你一个问题,你考试前怎么复习呀? 效果图 以下是源代码,可自行修改 [C] #include<bits/stdc.h> #include<windows.h> using namespace std; vector<string>item; int main(void) {item.push_back("Manacher"…...
基于LADRC自抗扰控制的VSG三相逆变器预同步并网控制策略(Simulink仿真实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
[0xGame 2023] week1
整理一下,昨天该第二周了。今天应该9点结束提交,等我写完就到了。 PWN 找不到且不对劲的flag 第1题是个nc测试,但也不完全是,因为flag在隐含目录里 高端的syscall 程序使用了危险函数,并且没有canary阻止࿰…...
Matlab矩阵——矩阵行列互换
问题:如何将 1*n 的矩阵转换为指定 M*N 的矩阵,或者将 M*N 的矩阵转换为 1*n 的矩阵? 处理方法:使用 reshape 函数进行矩阵的行列互换 分两种情况如下: 一、将 1*n 的矩阵转换为指定 M*N 的矩阵 假如有4个坐标值&a…...
OpenMesh 网格面片随机赋色
文章目录 一、简介二、实现代码三、实现效果一、简介 OpenMesh中的赋色方式与Easy3D很是类似,它统一有一个属性数组来进行管理,我们在进行赋色等操作时,必须要首先添加该属性才能进行使用,这里也进行记录一下(法向量等特征也是类似的操作)。 二、实现代码 #define _USE_…...
SpringSecurity源码学习一:过滤器执行原理
目录 1. web过滤器Filter1.1 filter核心类1.2 GenericFilterBean1.3 DelegatingFilterProxy1.3.1 原理1.3.2 DelegatingFilterProxy源码 2. FilterChainProxy源码学习2.1 源码2.1.1 doFilterInternal方法源码2.1.1.1 getFilters()方法源码2.1.1.2 VirtualFilterChain方法源码 3…...
8.2 JUC - 4.Semaphore
目录 一、是什么?二、简单使用三、semaphore应用四、Semaphore原理 一、是什么? Semaphore:信号量,用来限制能同时访问共享资源的线程上限 二、简单使用 public class TestSemaphore {public static void main(String[] args) …...
前端try和catch
为什么要使用try catch 使用try...catch语句是为了处理和管理可能会在程序运行过程中发生的异常或错误情况。以下是一些使用try...catch的主要原因: 错误处理:在开发过程中,无法避免地会出现各种错误,如网络请求失败、数据解析错误…...
Unity可视化Shader工具ASE介绍——2、ASE的Shader创建和输入输出
大家好,我是阿赵,这里继续介绍Unity可视化写Shader的ASE插件的用法。上一篇介绍了ASE的安装和编辑器界面分布,这一篇主要是通过一个简单的例子介绍shader的创建和输入输出。 一、ASE的Shader创建 这里先选择Surface类型的Shader,…...
目标检测算法改进系列之Backbone替换为Swin Transformer
Swin Transformer简介 《Swin Transformer: Hierarchical Vision Transformer using Shifted Windows》作为2021 ICCV最佳论文,屠榜了各大CV任务,性能优于DeiT、ViT和EfficientNet等主干网络,已经替代经典的CNN架构,成为了计算机…...
【技术干货】如何通过 DP 实现支持经典蓝牙的联网单品设备与 App 配对
经典蓝牙模块(Classic Bluetooth)主要用于呼叫和音频传输,所以经典蓝牙最主要的特点就是功耗大,传输数据量大。蓝牙耳机、蓝牙音箱等场景大多采用经典蓝牙,因为蓝牙是为传输声音而设计的,是短距离音频传输的…...
【Unity Build-In管线的SurfaceShader剖析_PBS光照函数】
Unity Build-In管线的SurfaceShader剖析 在Unity Build-In 管线(Universal Render Pipeline)新建一个Standard Surface Shader文件里的代码如下:选中"MyPBR.Shader",在Inspector面板,打开"Show generat…...
thinkphp5实现ajax图片上传,压缩保存到服务器
<div class"warp"><input type"file" id"file" accept"image/*" onchange"upimg(this)" /></div> <img src"" /> <script>//上传图片方法function upimg(obj){var fileData obj.…...
王道考研计算机网络——传输层
一、传输层概述 复用:发送方不同的应用进程都可以使用同一个传输层的协议来传送数据 分用:接收方的传输层在去除报文段的首部之后能把数据交给正确的应用进程 熟知端口号就是知名端口号0-1023 客户端使用的端口号是动态变化的,不是唯一确定…...
08 集群参数配置(下)
Kafka Broker不需要太大的堆内存? Kafka Broker不需要太大的堆内存?应该把内存留给页缓存使用? kafka刷盘时宕机 kafka认为写入成功是指写入页缓存成功还是数据刷到磁盘成功算成功呢?还是上次刷盘宕机失败的问题,页…...
mac文件为什么不能拖进U盘?
对于Mac用户来说,可能会遭遇一些烦恼,比如在试图将文件从Mac电脑拖入U盘时,却发现文件无法成功传输。这无疑给用户带来了很大的不便。那么,mac文件为什么不能拖进U盘,看完这篇你就知道了。 一、U盘的读写权限问题 如果…...
铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
