diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md" "b/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md"
index 32a7f35ed..0f48f06d2 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \344\272\214\345\210\206\346\237\245\346\211\276.md"
@@ -7,6 +7,7 @@
* [4. 第一个错误的版本](#4-第一个错误的版本)
* [5. 旋转数组的最小数字](#5-旋转数组的最小数字)
* [6. 查找区间](#6-查找区间)
+ * [7. 两个有序数组的中位数](#7-两个有序数组的中位数)
@@ -314,3 +315,42 @@ nums = [2,2], target = 2
```
如果 h 的取值为 nums.length - 1,那么 last = findFirst(nums, target + 1) - 1 = 1 - 1 = 0。这是因为 findFirst 只会返回 [0, nums.length - 1] 范围的值,对于 findFirst([2,2], 3) ,我们希望返回 3 应该插入 nums 中的位置,也就是数组最后一个位置再往后一个位置,即 nums.length。所以我们需要将 h 取值为 nums.length,从而使得 findFirst 返回的区间更大,能够覆盖 target 大于 nums 最后一个元素的情况。
+
+## 7. 两个有序数组的中位数
+
+4\. Median of Two Sorted Arrays (Hard)
+
+[Leetcode](https://leetcode.com/problems/median-of-two-sorted-arrays/) / [力扣](https://leetcode.cn/problems/median-of-two-sorted-arrays/)
+
+题目描述:求两个有序数组合并后的中位数,要求时间复杂度为 O(log(m + n))。
+
+把问题转化为求两个数组合并后第 k 小的数。比较两个数组各自的第 k / 2 个元素,较小的那个元素以及它前面的元素都不可能是第 k 小,可以一次排除,然后在剩下的部分里寻找第 k - 排除个数 小的数。k 每轮减半,时间复杂度为 O(log(m + n))。
+
+```java
+public double findMedianSortedArrays(int[] nums1, int[] nums2) {
+ int total = nums1.length + nums2.length;
+ if (total % 2 == 1) {
+ return kth(nums1, 0, nums2, 0, total / 2 + 1);
+ }
+ return (kth(nums1, 0, nums2, 0, total / 2) + kth(nums1, 0, nums2, 0, total / 2 + 1)) / 2.0;
+}
+
+// nums1[i..] 和 nums2[j..] 合并后第 k 小的数,k 从 1 开始
+private int kth(int[] nums1, int i, int[] nums2, int j, int k) {
+ if (i == nums1.length) {
+ return nums2[j + k - 1];
+ }
+ if (j == nums2.length) {
+ return nums1[i + k - 1];
+ }
+ if (k == 1) {
+ return Math.min(nums1[i], nums2[j]);
+ }
+ int a = Math.min(i + k / 2, nums1.length) - 1;
+ int b = Math.min(j + k / 2, nums2.length) - 1;
+ if (nums1[a] <= nums2[b]) {
+ return kth(nums1, a + 1, nums2, j, k - (a - i + 1));
+ }
+ return kth(nums1, i, nums2, b + 1, k - (b - j + 1));
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md"
index 9d73c022f..a26d35ef5 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\212\250\346\200\201\350\247\204\345\210\222.md"
@@ -10,9 +10,11 @@
* [矩阵路径](#矩阵路径)
* [1. 矩阵的最小路径和](#1-矩阵的最小路径和)
* [2. 矩阵的总路径数](#2-矩阵的总路径数)
+ * [3. 最大正方形](#3-最大正方形)
* [数组区间](#数组区间)
* [1. 数组区间和](#1-数组区间和)
* [2. 数组中等差递增子区间的个数](#2-数组中等差递增子区间的个数)
+ * [3. 最长有效括号](#3-最长有效括号)
* [分割整数](#分割整数)
* [1. 分割整数的最大乘积](#1-分割整数的最大乘积)
* [2. 按平方数来分割整数](#2-按平方数来分割整数)
@@ -241,6 +243,40 @@ public int uniquePaths(int m, int n) {
}
```
+### 3. 最大正方形
+
+221\. Maximal Square (Medium)
+
+[Leetcode](https://leetcode.com/problems/maximal-square/) / [力扣](https://leetcode.cn/problems/maximal-square/)
+
+题目描述:在由 0 和 1 组成的矩阵中,找出只包含 1 的最大正方形的面积。
+
+dp[i][j] 表示以 (i, j) 为右下角、只包含 1 的最大正方形的边长。它的边长受左边、上边和左上角三个方向的限制,取三者的最小值再加 1:
+
+```text
+dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
+
+matrix: 1 1 1 dp: 1 1 1
+ 1 1 1 1 2 2
+ 1 1 1 1 2 3
+```
+
+```java
+public int maximalSquare(char[][] matrix) {
+ int m = matrix.length, n = matrix[0].length, side = 0;
+ int[][] dp = new int[m + 1][n + 1];
+ for (int i = 1; i <= m; i++) {
+ for (int j = 1; j <= n; j++) {
+ if (matrix[i - 1][j - 1] == '1') {
+ dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
+ side = Math.max(side, dp[i][j]);
+ }
+ }
+ }
+ return side * side;
+}
+```
+
## 数组区间
### 1. 数组区间和
@@ -336,6 +372,42 @@ public int numberOfArithmeticSlices(int[] A) {
}
```
+### 3. 最长有效括号
+
+32\. Longest Valid Parentheses (Hard)
+
+[Leetcode](https://leetcode.com/problems/longest-valid-parentheses/) / [力扣](https://leetcode.cn/problems/longest-valid-parentheses/)
+
+```text
+Input: ")()())"
+Output: 4
+```
+
+dp[i] 表示以 s[i] 结尾的最长有效括号的长度,只有 s[i] 为 ) 时才可能不为 0。以 s[i - 1] 结尾的有效括号长度为 dp[i - 1],跳过这一段之后的位置 j = i - dp[i - 1] - 1 如果是 (,就能和 s[i] 配对,长度为 dp[i - 1] + 2,再加上 j 前面紧挨着的有效括号长度 dp[j - 1]。
+
+```text
+s : ( ) ( ( ) )
+dp : 0 2 0 0 2 6
+```
+
+```java
+public int longestValidParentheses(String s) {
+ int n = s.length(), ret = 0;
+ int[] dp = new int[n];
+ for (int i = 1; i < n; i++) {
+ if (s.charAt(i) != ')') {
+ continue;
+ }
+ int j = i - dp[i - 1] - 1;
+ if (j >= 0 && s.charAt(j) == '(') {
+ dp[i] = dp[i - 1] + 2 + (j > 0 ? dp[j - 1] : 0);
+ ret = Math.max(ret, dp[i]);
+ }
+ }
+ return ret;
+}
+```
+
## 分割整数
### 1. 分割整数的最大乘积
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md"
index 5d5113eb8..7b44d2154 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\217\214\346\214\207\351\222\210.md"
@@ -8,6 +8,9 @@
* [5. 归并两个有序数组](#5-归并两个有序数组)
* [6. 判断链表是否存在环](#6-判断链表是否存在环)
* [7. 最长子序列](#7-最长子序列)
+ * [8. 三数之和](#8-三数之和)
+ * [9. 接雨水](#9-接雨水)
+ * [10. 最小覆盖子串](#10-最小覆盖子串)
@@ -290,3 +293,124 @@ private boolean isSubstr(String s, String target) {
return j == target.length();
}
```
+
+## 8. 三数之和
+
+15\. 3Sum (Medium)
+
+[Leetcode](https://leetcode.com/problems/3sum/) / [力扣](https://leetcode.cn/problems/3sum/)
+
+```text
+Input: nums = [-1, 0, 1, 2, -1, -4]
+Output: [[-1, -1, 2], [-1, 0, 1]]
+```
+
+题目描述:找出所有和为 0 且不重复的三元组。
+
+先排序,然后枚举第一个数 nums[i],在它右边用第 1 题的双指针方法寻找和为 -nums[i] 的两个数。去重的关键是跳过相同的值:i 跳过与前一个数相同的值,找到一组解之后,两个指针也跳过相同的值。时间复杂度为 O(N2)。
+
+```java
+public List> threeSum(int[] nums) {
+ Arrays.sort(nums);
+ List> ret = new ArrayList<>();
+ for (int i = 0; i < nums.length - 2; i++) {
+ if (i > 0 && nums[i] == nums[i - 1]) {
+ continue;
+ }
+ int l = i + 1, h = nums.length - 1;
+ while (l < h) {
+ int sum = nums[i] + nums[l] + nums[h];
+ if (sum < 0) {
+ l++;
+ } else if (sum > 0) {
+ h--;
+ } else {
+ ret.add(Arrays.asList(nums[i], nums[l], nums[h]));
+ while (l < h && nums[l] == nums[l + 1]) {
+ l++;
+ }
+ while (l < h && nums[h] == nums[h - 1]) {
+ h--;
+ }
+ l++;
+ h--;
+ }
+ }
+ }
+ return ret;
+}
+```
+
+## 9. 接雨水
+
+42\. Trapping Rain Water (Hard)
+
+[Leetcode](https://leetcode.com/problems/trapping-rain-water/) / [力扣](https://leetcode.cn/problems/trapping-rain-water/)
+
+```text
+Input: height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]
+Output: 6
+```
+
+位置 i 上方能存的水,取决于它左边最高的柱子和右边最高的柱子中较矮的那一个:water[i] = min(leftMax, rightMax) - height[i]。
+
+用两个指针从两端向中间移动,分别维护 leftMax 和 rightMax。如果 leftMax < rightMax,左指针处的水位只由 leftMax 决定,因为右边已经有一根不低于 rightMax 的柱子,可以直接结算左指针并右移;反之结算右指针并左移。
+
+
+
+```java
+public int trap(int[] height) {
+ int l = 0, r = height.length - 1;
+ int leftMax = 0, rightMax = 0, ret = 0;
+ while (l < r) {
+ leftMax = Math.max(leftMax, height[l]);
+ rightMax = Math.max(rightMax, height[r]);
+ if (leftMax < rightMax) {
+ ret += leftMax - height[l++];
+ } else {
+ ret += rightMax - height[r--];
+ }
+ }
+ return ret;
+}
+```
+
+## 10. 最小覆盖子串
+
+76\. Minimum Window Substring (Hard)
+
+[Leetcode](https://leetcode.com/problems/minimum-window-substring/) / [力扣](https://leetcode.cn/problems/minimum-window-substring/)
+
+```text
+Input: s = "ADOBECODEBANC", t = "ABC"
+Output: "BANC"
+```
+
+题目描述:找出 s 中包含 t 所有字符(包括重复字符)的最短子串。
+
+使用滑动窗口。need[c] 表示窗口还缺几个字符 c,missing 表示一共还缺几个字符。右指针不断扩大窗口,直到 missing 为 0,此时窗口包含了 t 的所有字符;然后移动左指针缩小窗口,在窗口仍然满足条件时更新答案。
+
+```java
+public String minWindow(String s, String t) {
+ int[] need = new int[128];
+ for (char c : t.toCharArray()) {
+ need[c]++;
+ }
+ int missing = t.length(), start = 0, minLen = Integer.MAX_VALUE;
+ for (int l = 0, r = 0; r < s.length(); r++) {
+ if (need[s.charAt(r)]-- > 0) {
+ missing--;
+ }
+ while (missing == 0) {
+ if (r - l + 1 < minLen) {
+ minLen = r - l + 1;
+ start = l;
+ }
+ if (++need[s.charAt(l++)] > 0) {
+ missing++;
+ }
+ }
+ }
+ return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md"
index 055a0fc6e..f535b8a26 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\223\210\345\270\214\350\241\250.md"
@@ -5,6 +5,7 @@
* [2. 判断数组是否含有重复元素](#2-判断数组是否含有重复元素)
* [3. 最长和谐序列](#3-最长和谐序列)
* [4. 最长连续序列](#4-最长连续序列)
+ * [5. 和为 K 的子数组](#5-和为-k-的子数组)
@@ -133,3 +134,32 @@ private int maxCount(Map countForNum) {
return max;
}
```
+
+## 5. 和为 K 的子数组
+
+560\. Subarray Sum Equals K (Medium)
+
+[Leetcode](https://leetcode.com/problems/subarray-sum-equals-k/) / [力扣](https://leetcode.cn/problems/subarray-sum-equals-k/)
+
+```text
+Input: nums = [1, 1, 1], k = 2
+Output: 2
+```
+
+题目描述:统计和为 k 的连续子数组个数,数组元素可能为负数。
+
+用 sum 表示从数组开头到当前位置的前缀和,以当前位置结尾、和为 k 的子数组,对应前面某个位置的前缀和为 sum - k。遍历时用哈希表记录每个前缀和出现的次数即可。数组中有负数,窗口变大时和不一定变大,因此不能用滑动窗口。
+
+```java
+public int subarraySum(int[] nums, int k) {
+ Map cnt = new HashMap<>();
+ cnt.put(0, 1);
+ int sum = 0, ret = 0;
+ for (int num : nums) {
+ sum += num;
+ ret += cnt.getOrDefault(sum - k, 0);
+ cnt.merge(sum, 1, Integer::sum);
+ }
+ return ret;
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md" "b/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md"
index 28d394828..fe94503e6 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \345\255\227\347\254\246\344\270\262.md"
@@ -10,6 +10,9 @@
* [7. 回文子字符串个数](#7-回文子字符串个数)
* [8. 判断一个整数是否是回文数](#8-判断一个整数是否是回文数)
* [9. 统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数](#9-统计二进制字符串中连续-1-和连续-0-数量相同的子字符串个数)
+ * [10. 最长回文子串](#10-最长回文子串)
+ * [11. 字符串相乘](#11-字符串相乘)
+ * [12. 比较版本号](#12-比较版本号)
@@ -237,3 +240,117 @@ public int countBinarySubstrings(String s) {
return count;
}
```
+
+## 10. 最长回文子串
+
+5\. Longest Palindromic Substring (Medium)
+
+[Leetcode](https://leetcode.com/problems/longest-palindromic-substring/) / [力扣](https://leetcode.cn/problems/longest-palindromic-substring/)
+
+```text
+Input: s = "babad"
+Output: "bab"("aba" 也是正确答案)
+```
+
+和第 7 题一样从中心扩展:回文串的中心可能是一个字符,也可能是两个字符之间,以每个位置为中心分别向两边扩展,记录最长的回文。时间复杂度为 O(N2),空间复杂度为 O(1)。
+
+```java
+private int start = 0, maxLen = 0;
+
+public String longestPalindrome(String s) {
+ for (int i = 0; i < s.length(); i++) {
+ extend(s, i, i); // 奇数长度
+ extend(s, i, i + 1); // 偶数长度
+ }
+ return s.substring(start, start + maxLen);
+}
+
+private void extend(String s, int l, int r) {
+ while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) {
+ l--;
+ r++;
+ }
+ if (r - l - 1 > maxLen) {
+ maxLen = r - l - 1;
+ start = l + 1;
+ }
+}
+```
+
+## 11. 字符串相乘
+
+43\. Multiply Strings (Medium)
+
+[Leetcode](https://leetcode.com/problems/multiply-strings/) / [力扣](https://leetcode.cn/problems/multiply-strings/)
+
+```text
+Input: num1 = "123", num2 = "45"
+Output: "5535"
+```
+
+题目描述:以字符串形式给出两个非负整数,返回它们的乘积,不能把字符串直接转换为整数。
+
+模拟竖式乘法。乘积最多有 m + n 位,用数组 digits 按从高位到低位的顺序保存。num1[i] 与 num2[j] 相乘的结果落在 digits[i + j + 1] 上,先把所有乘积累加到对应位置,最后从低位到高位统一处理进位。
+
+```java
+public String multiply(String num1, String num2) {
+ int m = num1.length(), n = num2.length();
+ int[] digits = new int[m + n];
+ for (int i = 0; i < m; i++) {
+ for (int j = 0; j < n; j++) {
+ digits[i + j + 1] += (num1.charAt(i) - '0') * (num2.charAt(j) - '0');
+ }
+ }
+ for (int k = m + n - 1; k > 0; k--) {
+ digits[k - 1] += digits[k] / 10;
+ digits[k] %= 10;
+ }
+ int k = 0;
+ while (k < m + n - 1 && digits[k] == 0) {
+ k++;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (; k < m + n; k++) {
+ sb.append(digits[k]);
+ }
+ return sb.toString();
+}
+```
+
+## 12. 比较版本号
+
+165\. Compare Version Numbers (Medium)
+
+[Leetcode](https://leetcode.com/problems/compare-version-numbers/) / [力扣](https://leetcode.cn/problems/compare-version-numbers/)
+
+```text
+Input: version1 = "1.01", version2 = "1.001"
+Output: 0
+Input: version1 = "1.0.1", version2 = "1"
+Output: 1
+```
+
+题目描述:版本号由 . 分隔的修订号组成,从左到右依次比较修订号的整数值,前导 0 忽略,缺少的修订号视为 0。
+
+用两个指针分别扫描两个字符串,每次各解析出一段修订号的整数值进行比较,不需要先把字符串分割成数组。
+
+```java
+public int compareVersion(String version1, String version2) {
+ int i = 0, j = 0;
+ while (i < version1.length() || j < version2.length()) {
+ int x = 0, y = 0;
+ while (i < version1.length() && version1.charAt(i) != '.') {
+ x = x * 10 + version1.charAt(i++) - '0';
+ }
+ while (j < version2.length() && version2.charAt(j) != '.') {
+ y = y * 10 + version2.charAt(j++) - '0';
+ }
+ if (x != y) {
+ return x > y ? 1 : -1;
+ }
+ i++;
+ j++;
+ }
+ return 0;
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md"
index 8060fbeaf..05da7be77 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\345\255\246.md"
@@ -25,6 +25,7 @@
* [2. 3 的 n 次方](#2-3-的-n-次方)
* [3. 乘积数组](#3-乘积数组)
* [4. 找出数组中的乘积最大的三个数](#4-找出数组中的乘积最大的三个数)
+ * [5. 用 Rand7() 实现 Rand10()](#5-用-rand7-实现-rand10)
@@ -533,3 +534,24 @@ public int maximumProduct(int[] nums) {
return Math.max(max1*max2*max3, max1*min1*min2);
}
```
+
+### 5. 用 Rand7() 实现 Rand10()
+
+470\. Implement Rand10() Using Rand7() (Medium)
+
+[Leetcode](https://leetcode.com/problems/implement-rand10-using-rand7/) / [力扣](https://leetcode.cn/problems/implement-rand10-using-rand7/)
+
+题目描述:已知 rand7() 等概率返回 1 \~ 7,只用它实现等概率返回 1 \~ 10 的 rand10()。
+
+把两次 rand7() 的结果看成一个两位的 7 进制数,(rand7() - 1) * 7 + rand7() 能等概率地生成 1 \~ 49。只保留 1 \~ 40,它们对 10 取余之后 0 \~ 9 各出现 4 次,仍然是等概率的;落在 41 \~ 49 时重新生成,这种方法称为拒绝采样。
+
+```java
+public int rand10() {
+ while (true) {
+ int x = (rand7() - 1) * 7 + rand7();
+ if (x <= 40) {
+ return (x - 1) % 10 + 1;
+ }
+ }
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md"
index 7edc8eaf4..7e74755c1 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\225\260\347\273\204\344\270\216\347\237\251\351\230\265.md"
@@ -13,6 +13,8 @@
* [10. 对角元素相等的矩阵](#10-对角元素相等的矩阵)
* [11. 嵌套数组](#11-嵌套数组)
* [12. 分隔数组](#12-分隔数组)
+ * [13. 合并区间](#13-合并区间)
+ * [14. 下一个排列](#14-下一个排列)
@@ -452,3 +454,83 @@ public int maxChunksToSorted(int[] arr) {
return ret;
}
```
+
+## 13. 合并区间
+
+56\. Merge Intervals (Medium)
+
+[Leetcode](https://leetcode.com/problems/merge-intervals/) / [力扣](https://leetcode.cn/problems/merge-intervals/)
+
+```text
+Input: [[1, 3], [2, 6], [8, 10], [15, 18]]
+Output: [[1, 6], [8, 10], [15, 18]]
+```
+
+按区间的左端点排序后,能合并的区间一定是相邻的。依次检查每个区间:如果它的左端点不超过结果中最后一个区间的右端点,就把两者合并;否则作为一个新区间加入结果。
+
+```java
+public int[][] merge(int[][] intervals) {
+ Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
+ List ret = new ArrayList<>();
+ for (int[] cur : intervals) {
+ if (!ret.isEmpty() && cur[0] <= ret.get(ret.size() - 1)[1]) {
+ int[] last = ret.get(ret.size() - 1);
+ last[1] = Math.max(last[1], cur[1]);
+ } else {
+ ret.add(cur);
+ }
+ }
+ return ret.toArray(new int[ret.size()][]);
+}
+```
+
+## 14. 下一个排列
+
+31\. Next Permutation (Medium)
+
+[Leetcode](https://leetcode.com/problems/next-permutation/) / [力扣](https://leetcode.cn/problems/next-permutation/)
+
+```text
+Input: [1, 2, 3] Output: [1, 3, 2]
+Input: [3, 2, 1] Output: [1, 2, 3]
+```
+
+题目描述:把数组原地重排为字典序中下一个更大的排列,如果已经是最大的排列,就重排为最小的排列。
+
+要让排列变大又变得尽量少,应该修改尽量靠右的位置:
+
+- 从右往左找到第一个满足 nums[i] < nums[i + 1] 的位置 i,i 右边是一段降序,已经是这一段能组成的最大排列;
+- 从右往左找到第一个大于 nums[i] 的元素 nums[j],交换两者,i 位置变大了一点;
+- 交换后 i 右边仍然是降序,把它反转为升序,使这一段尽量小。
+
+```text
+1 5 8 4 7 6 5 3 1
+ i j
+交换:1 5 8 5 7 6 4 3 1
+反转:1 5 8 5 1 3 4 6 7
+```
+
+```java
+public void nextPermutation(int[] nums) {
+ int i = nums.length - 2;
+ while (i >= 0 && nums[i] >= nums[i + 1]) {
+ i--;
+ }
+ if (i >= 0) {
+ int j = nums.length - 1;
+ while (nums[j] <= nums[i]) {
+ j--;
+ }
+ swap(nums, i, j);
+ }
+ for (int l = i + 1, r = nums.length - 1; l < r; l++, r--) {
+ swap(nums, l, r);
+ }
+}
+
+private void swap(int[] nums, int i, int j) {
+ int t = nums[i];
+ nums[i] = nums[j];
+ nums[j] = t;
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md" "b/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md"
index e53e627d5..e7a1c092b 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \346\240\210\345\222\214\351\230\237\345\210\227.md"
@@ -7,6 +7,8 @@
* [4. 用栈实现括号匹配](#4-用栈实现括号匹配)
* [5. 数组中元素与下一个比它大的元素之间的距离](#5-数组中元素与下一个比它大的元素之间的距离)
* [6. 循环数组中比当前元素大的下一个元素](#6-循环数组中比当前元素大的下一个元素)
+ * [7. 字符串解码](#7-字符串解码)
+ * [8. 基本计算器](#8-基本计算器)
@@ -232,3 +234,91 @@ public int[] nextGreaterElements(int[] nums) {
return next;
}
```
+
+## 7. 字符串解码
+
+394\. Decode String (Medium)
+
+[Leetcode](https://leetcode.com/problems/decode-string/) / [力扣](https://leetcode.cn/problems/decode-string/)
+
+```text
+Input: "3[a2[c]]"
+Output: "accaccacc"
+```
+
+括号可以嵌套,用栈保存进入括号之前的状态:遇到 [ 时,把重复次数和当前已经得到的字符串分别压栈,然后从头开始记录括号内的字符串;遇到 ] 时弹出重复次数 k 和之前的字符串 prev,当前字符串变为 prev 加上括号内的字符串重复 k 次。
+
+```java
+public String decodeString(String s) {
+ Deque counts = new ArrayDeque<>();
+ Deque prevs = new ArrayDeque<>();
+ StringBuilder cur = new StringBuilder();
+ int k = 0;
+ for (char c : s.toCharArray()) {
+ if (Character.isDigit(c)) {
+ k = k * 10 + c - '0';
+ } else if (c == '[') {
+ counts.push(k);
+ prevs.push(cur);
+ cur = new StringBuilder();
+ k = 0;
+ } else if (c == ']') {
+ String inner = cur.toString();
+ cur = prevs.pop();
+ for (int n = counts.pop(); n > 0; n--) {
+ cur.append(inner);
+ }
+ } else {
+ cur.append(c);
+ }
+ }
+ return cur.toString();
+}
+```
+
+## 8. 基本计算器
+
+227\. Basic Calculator II (Medium)
+
+[Leetcode](https://leetcode.com/problems/basic-calculator-ii/) / [力扣](https://leetcode.cn/problems/basic-calculator-ii/)
+
+```text
+Input: " 3+5 / 2 "
+Output: 5
+```
+
+题目描述:计算只包含非负整数、+、-、*、/ 和空格的表达式,整数除法向零截断。
+
+乘除的优先级高于加减。读到一个数字之后,先不急着计算,等读到它后面的运算符时,再根据它前面的运算符处理:+ 把数字压栈,- 把相反数压栈,* 和 / 与栈顶元素计算后再放回。最后把栈中所有元素相加。
+
+```java
+public int calculate(String s) {
+ Deque stack = new ArrayDeque<>();
+ char op = '+';
+ int num = 0;
+ for (int i = 0; i < s.length(); i++) {
+ char c = s.charAt(i);
+ if (Character.isDigit(c)) {
+ num = num * 10 + c - '0';
+ }
+ if ((!Character.isDigit(c) && c != ' ') || i == s.length() - 1) {
+ if (op == '+') {
+ stack.push(num);
+ } else if (op == '-') {
+ stack.push(-num);
+ } else if (op == '*') {
+ stack.push(stack.pop() * num);
+ } else {
+ stack.push(stack.pop() / num);
+ }
+ op = c;
+ num = 0;
+ }
+ }
+ int ret = 0;
+ for (int x : stack) {
+ ret += x;
+ }
+ return ret;
+}
+```
diff --git "a/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md" "b/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md"
index 270ec9ad7..188f91d62 100644
--- "a/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md"
+++ "b/notes/Leetcode \351\242\230\350\247\243 - \351\223\276\350\241\250.md"
@@ -11,6 +11,10 @@
* [8. 回文链表](#8-回文链表)
* [9. 分隔链表](#9-分隔链表)
* [10. 链表元素按奇偶聚集](#10-链表元素按奇偶聚集)
+ * [11. K 个一组翻转链表](#11-k-个一组翻转链表)
+ * [12. 重排链表](#12-重排链表)
+ * [13. 合并 K 个有序链表](#13-合并-k-个有序链表)
+ * [14. 链表排序](#14-链表排序)
@@ -363,3 +367,170 @@ public ListNode oddEvenList(ListNode head) {
return head;
}
```
+
+## 11. K 个一组翻转链表
+
+25\. Reverse Nodes in k-Group (Hard)
+
+[Leetcode](https://leetcode.com/problems/reverse-nodes-in-k-group/) / [力扣](https://leetcode.cn/problems/reverse-nodes-in-k-group/)
+
+```text
+Input: 1->2->3->4->5, k = 2
+Output: 2->1->4->3->5
+```
+
+题目描述:每 k 个节点一组进行翻转,最后不足 k 个的节点保持原有顺序。
+
+使用一个虚拟头节点 dummy,pre 指向下一组的前一个节点。先从 pre 向后走 k 步找到这一组的尾节点 tail,走不到说明剩余节点不足 k 个。然后把 pre.next 到 tail 这一段原地翻转,再把它接回原链表,最后 pre 移到这一组翻转后的末尾。
+
+
+
+```java
+public ListNode reverseKGroup(ListNode head, int k) {
+ ListNode dummy = new ListNode(-1);
+ dummy.next = head;
+ ListNode pre = dummy;
+ while (true) {
+ ListNode tail = pre;
+ for (int i = 0; i < k && tail != null; i++) {
+ tail = tail.next;
+ }
+ if (tail == null) {
+ break;
+ }
+ ListNode first = pre.next, next = tail.next;
+ ListNode prev = next, cur = first;
+ while (cur != next) {
+ ListNode tmp = cur.next;
+ cur.next = prev;
+ prev = cur;
+ cur = tmp;
+ }
+ pre.next = tail;
+ pre = first;
+ }
+ return dummy.next;
+}
+```
+
+## 12. 重排链表
+
+143\. Reorder List (Medium)
+
+[Leetcode](https://leetcode.com/problems/reorder-list/) / [力扣](https://leetcode.cn/problems/reorder-list/)
+
+```text
+Input: 1->2->3->4->5
+Output: 1->5->2->4->3
+```
+
+题目描述:把 L0→L1→…→Ln 重排为 L0→Ln→L1→Ln-1→…,只能修改节点的指针。
+
+可以拆成三个已经解决过的子问题:用快慢指针找到中点并断开;把后半段反转;再把两段交替连接起来。
+
+```java
+public void reorderList(ListNode head) {
+ if (head == null || head.next == null) {
+ return;
+ }
+ ListNode slow = head, fast = head;
+ while (fast.next != null && fast.next.next != null) {
+ slow = slow.next;
+ fast = fast.next.next;
+ }
+ ListNode l2 = reverse(slow.next);
+ slow.next = null;
+ ListNode l1 = head;
+ while (l2 != null) {
+ ListNode n1 = l1.next, n2 = l2.next;
+ l1.next = l2;
+ l2.next = n1;
+ l1 = n1;
+ l2 = n2;
+ }
+}
+
+private ListNode reverse(ListNode head) {
+ ListNode pre = null;
+ while (head != null) {
+ ListNode next = head.next;
+ head.next = pre;
+ pre = head;
+ head = next;
+ }
+ return pre;
+}
+```
+
+## 13. 合并 K 个有序链表
+
+23\. Merge k Sorted Lists (Hard)
+
+[Leetcode](https://leetcode.com/problems/merge-k-sorted-lists/) / [力扣](https://leetcode.cn/problems/merge-k-sorted-lists/)
+
+用小顶堆保存每个链表当前的头节点,每次取出值最小的节点接到结果链表后面,再把它的下一个节点放入堆中。设一共有 N 个节点,每个节点进出堆一次,时间复杂度为 O(NlogK)。
+
+也可以像归并排序那样两两合并,时间复杂度同样是 O(NlogK)。
+
+```java
+public ListNode mergeKLists(ListNode[] lists) {
+ PriorityQueue pq = new PriorityQueue<>((a, b) -> Integer.compare(a.val, b.val));
+ for (ListNode node : lists) {
+ if (node != null) {
+ pq.add(node);
+ }
+ }
+ ListNode dummy = new ListNode(-1), tail = dummy;
+ while (!pq.isEmpty()) {
+ ListNode node = pq.poll();
+ tail.next = node;
+ tail = node;
+ if (node.next != null) {
+ pq.add(node.next);
+ }
+ }
+ return dummy.next;
+}
+```
+
+## 14. 链表排序
+
+148\. Sort List (Medium)
+
+[Leetcode](https://leetcode.com/problems/sort-list/) / [力扣](https://leetcode.cn/problems/sort-list/)
+
+题目描述:在 O(NlogN) 时间复杂度内对链表进行排序。
+
+链表不能随机访问,适合用归并排序:用快慢指针找到中点断开,两半分别排序后,用第 3 题的方法归并。递归需要 O(logN) 的栈空间。
+
+```java
+public ListNode sortList(ListNode head) {
+ if (head == null || head.next == null) {
+ return head;
+ }
+ ListNode slow = head, fast = head.next;
+ while (fast != null && fast.next != null) {
+ slow = slow.next;
+ fast = fast.next.next;
+ }
+ ListNode right = slow.next;
+ slow.next = null;
+ return merge(sortList(head), sortList(right));
+}
+
+private ListNode merge(ListNode l1, ListNode l2) {
+ ListNode dummy = new ListNode(-1), tail = dummy;
+ while (l1 != null && l2 != null) {
+ if (l1.val <= l2.val) {
+ tail.next = l1;
+ l1 = l1.next;
+ } else {
+ tail.next = l2;
+ l2 = l2.next;
+ }
+ tail = tail.next;
+ }
+ tail.next = l1 != null ? l1 : l2;
+ return dummy.next;
+}
+```
diff --git a/notes/pics/reverse-k-group.png b/notes/pics/reverse-k-group.png
new file mode 100644
index 000000000..42eef4770
Binary files /dev/null and b/notes/pics/reverse-k-group.png differ
diff --git a/notes/pics/trapping-rain-water.png b/notes/pics/trapping-rain-water.png
new file mode 100644
index 000000000..4aa666abd
Binary files /dev/null and b/notes/pics/trapping-rain-water.png differ
diff --git a/tools/diagram/src/reverse-k-group.svg b/tools/diagram/src/reverse-k-group.svg
new file mode 100644
index 000000000..09f1d1f64
--- /dev/null
+++ b/tools/diagram/src/reverse-k-group.svg
@@ -0,0 +1,95 @@
+
diff --git a/tools/diagram/src/trapping-rain-water.svg b/tools/diagram/src/trapping-rain-water.svg
new file mode 100644
index 000000000..809b073a5
--- /dev/null
+++ b/tools/diagram/src/trapping-rain-water.svg
@@ -0,0 +1,67 @@
+