diff --git a/README.md b/README.md index 43dbdfca64534..d8fc9ae3908c8 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,6 @@ - [在排序数组中查找元素的第一个和最后一个位置](/solution/0000-0099/0034.Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array/README.md) - `二分查找` - [准时到达的列车最小时速](/solution/1800-1899/1870.Minimum%20Speed%20to%20Arrive%20on%20Time/README.md) - `二分查找` -- [找到需要补充粉笔的学生编号](/solution/1800-1899/1894.Find%20the%20Student%20that%20Will%20Replace%20the%20Chalk/README.md) - `二分查找` - [可移除字符的最大数目](/solution/1800-1899/1898.Maximum%20Number%20of%20Removable%20Characters/README.md) - `二分查找` - [排序数组](/solution/0900-0999/0912.Sort%20an%20Array/README.md) - `快速排序`、`归并排序` - [字符串相加](/solution/0400-0499/0415.Add%20Strings/README.md) - `高精度加法` diff --git a/README_EN.md b/README_EN.md index a1b12f59c58c1..72060d68c0d63 100644 --- a/README_EN.md +++ b/README_EN.md @@ -43,7 +43,6 @@ The repository is maintained by the Doocs community, and please give us a [star] - [Find First and Last Position of Element in Sorted Array](/solution/0000-0099/0034.Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array/README_EN.md) - `Binary search` - [Minimum Speed to Arrive on Time](/solution/1800-1899/1870.Minimum%20Speed%20to%20Arrive%20on%20Time/README_EN.md) - `Binary search` -- [Find the Student that Will Replace the Chalk](/solution/1800-1899/1894.Find%20the%20Student%20that%20Will%20Replace%20the%20Chalk/README_EN.md) - `Binary search` - [Maximum Number of Removable Characters](/solution/1800-1899/1898.Maximum%20Number%20of%20Removable%20Characters/README_EN.md) - `Binary search` - [Sort an Array](/solution/0900-0999/0912.Sort%20an%20Array/README_EN.md) - `Quick Sort`, `Merge Sort` - [Add Strings](/solution/0400-0499/0415.Add%20Strings/README_EN.md) - `Addition of large numbers` diff --git a/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README.md b/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README.md index ba756d39383e7..1815b5d0d27de 100644 --- a/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README.md +++ b/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README.md @@ -93,16 +93,16 @@ int search(int left, int right) { } ``` -做二分题目时,可以按照以下步骤: +做二分题目时,可以按照以下套路: -1. 写出循环条件:`while (left < right)`,注意是 `left < right`,而非 `left <= right`; -1. 循环体内,先无脑写出 `mid = (left + right) >> 1`; -1. 根据具体题目,实现 `check()` 函数(有时很简单的逻辑,可以不定义 `check`),想一下究竟要用 `right = mid`(模板 1) 还是 `left = mid`(模板 2); - - 如果 `right = mid`,那么无脑写出 else 语句 `left = mid + 1`,并且不需要更改 mid 的计算,即保持 `mid = (left + right) >> 1`; - - 如果 `left = mid`,那么无脑写出 else 语句 `right = mid - 1`,并且在 mid 计算时补充 +1,即 `mid = (left + right + 1) >> 1`。 -1. 循环结束时,left 与 right 相等。 +1. 写出循环条件 $left < right$; +1. 循环体内,不妨先写 $mid = \lfloor \frac{left + right}{2} \rfloor$; +1. 根据具体题目,实现 $check()$ 函数(有时很简单的逻辑,可以不定义 $check$),想一下究竟要用 $right = mid$(模板 $1$) 还是 $left = mid$(模板 $2$); +     - 如果 $right = mid$,那么写出 else 语句 $left = mid + 1$,并且不需要更改 mid 的计算,即保持 $mid = \lfloor \frac{left + right}{2} \rfloor$; +     - 如果 $left = mid$,那么写出 else 语句 $right = mid - 1$,并且在 $mid$ 计算时补充 +1,即 $mid = \lfloor \frac{left + right + 1}{2} \rfloor$; +1. 循环结束时,$left$ 与 $right$ 相等。 -注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 left 或者 right 是否满足题意即可。 +注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 $left$ 或者 $right$ 是否满足题意即可。 @@ -159,34 +159,6 @@ public: }; ``` -### **JavaScript** - -```js -/** - * @param {number[]} nums - * @param {number} target - * @return {number[]} - */ -var searchRange = function (nums, target) { - function search(x) { - let left = 0, - right = nums.length; - while (left < right) { - const mid = (left + right) >> 1; - if (nums[mid] >= x) { - right = mid; - } else { - left = mid + 1; - } - } - return left; - } - const l = search(target); - const r = search(target + 1); - return l == r ? [-1, -1] : [l, r - 1]; -}; -``` - ### **Go** ```go @@ -233,6 +205,33 @@ impl Solution { ```ts function searchRange(nums: number[], target: number): number[] { + const search = (x: number): number => { + let [left, right] = [0, nums.length]; + while (left < right) { + const mid = (left + right) >> 1; + if (nums[mid] >= x) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + }; + const l = search(target); + const r = search(target + 1); + return l === r ? [-1, -1] : [l, r - 1]; +} +``` + +### **JavaScript** + +```js +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var searchRange = function (nums, target) { function search(x) { let left = 0, right = nums.length; @@ -249,7 +248,7 @@ function searchRange(nums: number[], target: number): number[] { const l = search(target); const r = search(target + 1); return l == r ? [-1, -1] : [l, r - 1]; -} +}; ``` ### **...** diff --git a/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README_EN.md b/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README_EN.md index b018d9b28fe1b..0c0b1f8e18cec 100644 --- a/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README_EN.md +++ b/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/README_EN.md @@ -79,6 +79,17 @@ int search(int left, int right) { } ``` +When doing binary search problems, you can follow the following routine: + +1. Write out the loop condition $left < right$; +2. Inside the loop, you might as well write $mid = \lfloor \frac{left + right}{2} \rfloor$ first; +3. According to the specific problem, implement the $check()$ function (sometimes the logic is very simple, you can not define $check$), think about whether to use $right = mid$ (Template $1$) or $left = mid$ (Template $2$); + - If $right = mid$, then write the else statement $left = mid + 1$, and there is no need to change the calculation of $mid$, that is, keep $mid = \lfloor \frac{left + right}{2} \rfloor$; + - If $left = mid$, then write the else statement $right = mid - 1$, and add +1 when calculating $mid$, that is, $mid = \lfloor \frac{left + right + 1}{2} \rfloor$; +4. When the loop ends, $left$ equals $right$. + +Note that the advantage of these two templates is that they always keep the answer within the binary search interval, and the value corresponding to the end condition of the binary search is exactly at the position of the answer. For the case that may have no solution, just check whether the $left$ or $right$ after the binary search ends satisfies the problem. + ### **Python3** @@ -130,34 +141,6 @@ public: }; ``` -### **JavaScript** - -```js -/** - * @param {number[]} nums - * @param {number} target - * @return {number[]} - */ -var searchRange = function (nums, target) { - function search(x) { - let left = 0, - right = nums.length; - while (left < right) { - const mid = (left + right) >> 1; - if (nums[mid] >= x) { - right = mid; - } else { - left = mid + 1; - } - } - return left; - } - const l = search(target); - const r = search(target + 1); - return l == r ? [-1, -1] : [l, r - 1]; -}; -``` - ### **Go** ```go @@ -204,6 +187,33 @@ impl Solution { ```ts function searchRange(nums: number[], target: number): number[] { + const search = (x: number): number => { + let [left, right] = [0, nums.length]; + while (left < right) { + const mid = (left + right) >> 1; + if (nums[mid] >= x) { + right = mid; + } else { + left = mid + 1; + } + } + return left; + }; + const l = search(target); + const r = search(target + 1); + return l === r ? [-1, -1] : [l, r - 1]; +} +``` + +### **JavaScript** + +```js +/** + * @param {number[]} nums + * @param {number} target + * @return {number[]} + */ +var searchRange = function (nums, target) { function search(x) { let left = 0, right = nums.length; @@ -220,7 +230,7 @@ function searchRange(nums: number[], target: number): number[] { const l = search(target); const r = search(target + 1); return l == r ? [-1, -1] : [l, r - 1]; -} +}; ``` ### **...** diff --git a/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/Solution.ts b/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/Solution.ts index 8441ad17e1bbe..001c5ba4dba1e 100644 --- a/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/Solution.ts +++ b/solution/0000-0099/0034.Find First and Last Position of Element in Sorted Array/Solution.ts @@ -1,7 +1,6 @@ function searchRange(nums: number[], target: number): number[] { - function search(x) { - let left = 0, - right = nums.length; + const search = (x: number): number => { + let [left, right] = [0, nums.length]; while (left < right) { const mid = (left + right) >> 1; if (nums[mid] >= x) { @@ -11,8 +10,8 @@ function searchRange(nums: number[], target: number): number[] { } } return left; - } + }; const l = search(target); const r = search(target + 1); - return l == r ? [-1, -1] : [l, r - 1]; + return l === r ? [-1, -1] : [l, r - 1]; } diff --git a/solution/0000-0099/0043.Multiply Strings/README.md b/solution/0000-0099/0043.Multiply Strings/README.md index f9ff69fa14a39..07a2d4849d154 100644 --- a/solution/0000-0099/0043.Multiply Strings/README.md +++ b/solution/0000-0099/0043.Multiply Strings/README.md @@ -149,35 +149,29 @@ public: ```go func multiply(num1 string, num2 string) string { + if num1 == "0" || num2 == "0" { + return "0" + } m, n := len(num1), len(num2) - res := make([]int, m+n) - mul := func(b, i int) { - for j, t := m-1, 0; j >= 0 || t > 0; i, j = i+1, j-1 { - if j >= 0 { - a := int(num1[j] - '0') - t += a * b - } - res[i] += t % 10 - if res[i] >= 10 { - res[i] %= 10 - res[i+1]++ - } - t /= 10 + arr := make([]int, m+n) + for i := m - 1; i >= 0; i-- { + a := int(num1[i] - '0') + for j := n - 1; j >= 0; j-- { + b := int(num2[j] - '0') + arr[i+j+1] += a * b } } - for i := 0; i < n; i++ { - b := num2[n-1-i] - '0' - mul(int(b), i) - } - var ans []byte - for _, v := range res { - ans = append(ans, byte(v+'0')) + for i := len(arr) - 1; i > 0; i-- { + arr[i-1] += arr[i] / 10 + arr[i] %= 10 } - for len(ans) > 1 && ans[len(ans)-1] == '0' { - ans = ans[:len(ans)-1] + i := 0 + if arr[0] == 0 { + i = 1 } - for i, j := 0, len(ans)-1; i < j; i, j = i+1, j-1 { - ans[i], ans[j] = ans[j], ans[i] + ans := []byte{} + for ; i < len(arr); i++ { + ans = append(ans, byte('0'+arr[i])) } return string(ans) } @@ -185,61 +179,30 @@ func multiply(num1 string, num2 string) string { ### **TypeScript** -```ts -function multiply(num1: string, num2: string): string { - if ([num1, num2].includes('0')) return '0'; - const n1 = num1.length, - n2 = num2.length; - let ans = ''; - for (let i = 0; i < n1; i++) { - let cur1 = parseInt(num1.charAt(n1 - i - 1), 10); - let sum = ''; - for (let j = 0; j < n2; j++) { - let cur2 = parseInt(num2.charAt(n2 - j - 1), 10); - sum = addString(sum, cur1 * cur2 + '0'.repeat(j)); - } - ans = addString(ans, sum + '0'.repeat(i)); - } - return ans; -} - -function addString(s1: string, s2: string): string { - const n1 = s1.length, - n2 = s2.length; - let ans = []; - let sum = 0; - for (let i = 0; i < n1 || i < n2 || sum > 0; i++) { - let num1 = i < n1 ? parseInt(s1.charAt(n1 - i - 1), 10) : 0; - let num2 = i < n2 ? parseInt(s2.charAt(n2 - i - 1), 10) : 0; - sum += num1 + num2; - ans.unshift(sum % 10); - sum = Math.floor(sum / 10); - } - return ans.join(''); -} -``` - ```ts function multiply(num1: string, num2: string): string { if (num1 === '0' || num2 === '0') { return '0'; } - - const n = num1.length; - const m = num2.length; - const res = []; - for (let i = 0; i < n; i++) { - const a = Number(num1[n - i - 1]); - let sum = 0; - for (let j = 0; j < m || sum !== 0; j++) { - const b = Number(num2[m - j - 1] ?? 0); - sum += a * b + (res[i + j] ?? 0); - res[i + j] = sum % 10; - sum = Math.floor(sum / 10); + const m: number = num1.length; + const n: number = num2.length; + const arr: number[] = Array(m + n).fill(0); + for (let i: number = m - 1; i >= 0; i--) { + const a: number = +num1[i]; + for (let j: number = n - 1; j >= 0; j--) { + const b: number = +num2[j]; + arr[i + j + 1] += a * b; } } - - return res.reverse().join(''); + for (let i: number = arr.length - 1; i > 0; i--) { + arr[i - 1] += Math.floor(arr[i] / 10); + arr[i] %= 10; + } + let i: number = 0; + while (i < arr.length && arr[i] === 0) { + i++; + } + return arr.slice(i).join(''); } ``` @@ -277,6 +240,47 @@ impl Solution { } ``` +### **C#** + +```cs +public class Solution { + public string Multiply(string num1, string num2) { + if (num1 == "0" || num2 == "0") { + return "0"; + } + + int m = num1.Length; + int n = num2.Length; + int[] arr = new int[m + n]; + + for (int i = m - 1; i >= 0; i--) { + int a = num1[i] - '0'; + for (int j = n - 1; j >= 0; j--) { + int b = num2[j] - '0'; + arr[i + j + 1] += a * b; + } + } + + for (int i = arr.Length - 1; i > 0; i--) { + arr[i - 1] += arr[i] / 10; + arr[i] %= 10; + } + + int index = 0; + while (index < arr.Length && arr[index] == 0) { + index++; + } + + StringBuilder ans = new StringBuilder(); + for (; index < arr.Length; index++) { + ans.Append(arr[index]); + } + + return ans.ToString(); + } +} +``` + ### **...** ``` diff --git a/solution/0000-0099/0043.Multiply Strings/README_EN.md b/solution/0000-0099/0043.Multiply Strings/README_EN.md index 258b5e79ff34d..f89d7c03db083 100644 --- a/solution/0000-0099/0043.Multiply Strings/README_EN.md +++ b/solution/0000-0099/0043.Multiply Strings/README_EN.md @@ -164,61 +164,30 @@ func multiply(num1 string, num2 string) string { ### **TypeScript** -```ts -function multiply(num1: string, num2: string): string { - if ([num1, num2].includes('0')) return '0'; - const n1 = num1.length, - n2 = num2.length; - let ans = ''; - for (let i = 0; i < n1; i++) { - let cur1 = parseInt(num1.charAt(n1 - i - 1), 10); - let sum = ''; - for (let j = 0; j < n2; j++) { - let cur2 = parseInt(num2.charAt(n2 - j - 1), 10); - sum = addString(sum, cur1 * cur2 + '0'.repeat(j)); - } - ans = addString(ans, sum + '0'.repeat(i)); - } - return ans; -} - -function addString(s1: string, s2: string): string { - const n1 = s1.length, - n2 = s2.length; - let ans = []; - let sum = 0; - for (let i = 0; i < n1 || i < n2 || sum > 0; i++) { - let num1 = i < n1 ? parseInt(s1.charAt(n1 - i - 1), 10) : 0; - let num2 = i < n2 ? parseInt(s2.charAt(n2 - i - 1), 10) : 0; - sum += num1 + num2; - ans.unshift(sum % 10); - sum = Math.floor(sum / 10); - } - return ans.join(''); -} -``` - ```ts function multiply(num1: string, num2: string): string { if (num1 === '0' || num2 === '0') { return '0'; } - - const n = num1.length; - const m = num2.length; - const res = []; - for (let i = 0; i < n; i++) { - const a = Number(num1[n - i - 1]); - let sum = 0; - for (let j = 0; j < m || sum !== 0; j++) { - const b = Number(num2[m - j - 1] ?? 0); - sum += a * b + (res[i + j] ?? 0); - res[i + j] = sum % 10; - sum = Math.floor(sum / 10); + const m: number = num1.length; + const n: number = num2.length; + const arr: number[] = Array(m + n).fill(0); + for (let i: number = m - 1; i >= 0; i--) { + const a: number = +num1[i]; + for (let j: number = n - 1; j >= 0; j--) { + const b: number = +num2[j]; + arr[i + j + 1] += a * b; } } - - return res.reverse().join(''); + for (let i: number = arr.length - 1; i > 0; i--) { + arr[i - 1] += Math.floor(arr[i] / 10); + arr[i] %= 10; + } + let i: number = 0; + while (i < arr.length && arr[i] === 0) { + i++; + } + return arr.slice(i).join(''); } ``` @@ -256,6 +225,47 @@ impl Solution { } ``` +### **C#** + +```cs +public class Solution { + public string Multiply(string num1, string num2) { + if (num1 == "0" || num2 == "0") { + return "0"; + } + + int m = num1.Length; + int n = num2.Length; + int[] arr = new int[m + n]; + + for (int i = m - 1; i >= 0; i--) { + int a = num1[i] - '0'; + for (int j = n - 1; j >= 0; j--) { + int b = num2[j] - '0'; + arr[i + j + 1] += a * b; + } + } + + for (int i = arr.Length - 1; i > 0; i--) { + arr[i - 1] += arr[i] / 10; + arr[i] %= 10; + } + + int index = 0; + while (index < arr.Length && arr[index] == 0) { + index++; + } + + StringBuilder ans = new StringBuilder(); + for (; index < arr.Length; index++) { + ans.Append(arr[index]); + } + + return ans.ToString(); + } +} +``` + ### **...** ``` diff --git a/solution/0000-0099/0043.Multiply Strings/Solution.cs b/solution/0000-0099/0043.Multiply Strings/Solution.cs index f73a98c15225b..a27c739d22cf5 100644 --- a/solution/0000-0099/0043.Multiply Strings/Solution.cs +++ b/solution/0000-0099/0043.Multiply Strings/Solution.cs @@ -1,36 +1,36 @@ -using System.Text; - -public class Solution { - public string Multiply(string num1, string num2) { - var digits = new int[num1.Length + num2.Length]; - for (var i = 0; i < num1.Length; ++i) - { - for (var j = 0; j < num2.Length; ++j) - { - var digit1 = num1[num1.Length - i - 1] - '0'; - var digit2 = num2[num2.Length - j - 1] - '0'; - var product = digit1 * digit2; - digits[i + j] += product; - } - } - - var carry = 0; - for (var i = 0; i < digits.Length; ++i) - { - digits[i] += carry; - carry = digits[i] / 10; - digits[i] %= 10; - } - - var sb = new StringBuilder(); - for (var i = digits.Length - 1; i >= 0; --i) - { - if (digits[i] > 0 || sb.Length > 0) - { - sb.Append((char)(digits[i] + '0')); - } - } - if (sb.Length == 0) sb.Append('0'); - return sb.ToString(); - } +public class Solution { + public string Multiply(string num1, string num2) { + if (num1 == "0" || num2 == "0") { + return "0"; + } + + int m = num1.Length; + int n = num2.Length; + int[] arr = new int[m + n]; + + for (int i = m - 1; i >= 0; i--) { + int a = num1[i] - '0'; + for (int j = n - 1; j >= 0; j--) { + int b = num2[j] - '0'; + arr[i + j + 1] += a * b; + } + } + + for (int i = arr.Length - 1; i > 0; i--) { + arr[i - 1] += arr[i] / 10; + arr[i] %= 10; + } + + int index = 0; + while (index < arr.Length && arr[index] == 0) { + index++; + } + + StringBuilder ans = new StringBuilder(); + for (; index < arr.Length; index++) { + ans.Append(arr[index]); + } + + return ans.ToString(); + } } \ No newline at end of file diff --git a/solution/0000-0099/0043.Multiply Strings/Solution.ts b/solution/0000-0099/0043.Multiply Strings/Solution.ts index 668d7fd5e70cd..c90dd9ad3ec2f 100644 --- a/solution/0000-0099/0043.Multiply Strings/Solution.ts +++ b/solution/0000-0099/0043.Multiply Strings/Solution.ts @@ -1,31 +1,24 @@ function multiply(num1: string, num2: string): string { - if ([num1, num2].includes('0')) return '0'; - const n1 = num1.length, - n2 = num2.length; - let ans = ''; - for (let i = 0; i < n1; i++) { - let cur1 = parseInt(num1.charAt(n1 - i - 1), 10); - let sum = ''; - for (let j = 0; j < n2; j++) { - let cur2 = parseInt(num2.charAt(n2 - j - 1), 10); - sum = addString(sum, cur1 * cur2 + '0'.repeat(j)); + if (num1 === '0' || num2 === '0') { + return '0'; + } + const m: number = num1.length; + const n: number = num2.length; + const arr: number[] = Array(m + n).fill(0); + for (let i: number = m - 1; i >= 0; i--) { + const a: number = +num1[i]; + for (let j: number = n - 1; j >= 0; j--) { + const b: number = +num2[j]; + arr[i + j + 1] += a * b; } - ans = addString(ans, sum + '0'.repeat(i)); } - return ans; -} - -function addString(s1: string, s2: string): string { - const n1 = s1.length, - n2 = s2.length; - let ans = []; - let sum = 0; - for (let i = 0; i < n1 || i < n2 || sum > 0; i++) { - let num1 = i < n1 ? parseInt(s1.charAt(n1 - i - 1), 10) : 0; - let num2 = i < n2 ? parseInt(s2.charAt(n2 - i - 1), 10) : 0; - sum += num1 + num2; - ans.unshift(sum % 10); - sum = Math.floor(sum / 10); + for (let i: number = arr.length - 1; i > 0; i--) { + arr[i - 1] += Math.floor(arr[i] / 10); + arr[i] %= 10; + } + let i: number = 0; + while (i < arr.length && arr[i] === 0) { + i++; } - return ans.join(''); + return arr.slice(i).join(''); } diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/README.md b/solution/0300-0399/0303.Range Sum Query - Immutable/README.md index f7e1b3fe77262..68f3da4569c3a 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/README.md +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/README.md @@ -54,9 +54,9 @@ numArray.sumRange(0, 5); // return -3 ((-2) + 0 + 3 + (-5) + 2 + (-1)) **方法一:前缀和** -前缀和计算公式:`s[i + 1] = s[i] + nums[i]`。 +我们创建一个长度为 $n + 1$ 的前缀和数组 $s$,其中 $s[i]$ 表示前 $i$ 个元素的前缀和,即 $s[i] = \sum_{j=0}^{i-1} nums[j]$,那么索引 $[left, right]$ 之间的元素的和就可以表示为 $s[right + 1] - s[left]$。 -初始化的时间复杂度是 $O(n)$,每次查询的时间复杂度是 $O(1)$。其中 $n$ 是数组的长度。 +初始化前缀和数组 $s$ 的时间复杂度为 $O(n)$,查询的时间复杂度为 $O(1)$。空间复杂度 $O(n)$。 @@ -111,17 +111,20 @@ class NumArray { ```cpp class NumArray { public: - vector s; - NumArray(vector& nums) { int n = nums.size(); s.resize(n + 1); - for (int i = 0; i < n; ++i) s[i + 1] = s[i] + nums[i]; + for (int i = 0; i < n; ++i) { + s[i + 1] = s[i] + nums[i]; + } } int sumRange(int left, int right) { return s[right + 1] - s[left]; } + +private: + vector s; }; /** @@ -166,7 +169,7 @@ func (this *NumArray) SumRange(left int, right int) int { */ var NumArray = function (nums) { const n = nums.length; - this.s = new Array(n + 1).fill(0); + this.s = Array(n + 1).fill(0); for (let i = 0; i < n; ++i) { this.s[i + 1] = this.s[i] + nums[i]; } @@ -196,7 +199,7 @@ class NumArray { constructor(nums: number[]) { const n = nums.length; - this.s = new Array(n + 1).fill(0); + this.s = Array(n + 1).fill(0); for (let i = 0; i < n; ++i) { this.s[i + 1] = this.s[i] + nums[i]; } @@ -218,34 +221,7 @@ class NumArray { ```rust struct NumArray { - nums: Vec, -} - -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ -impl NumArray { - fn new(nums: Vec) -> Self { - Self { - nums, - } - } - - fn sum_range(&self, left: i32, right: i32) -> i32 { - let (left, right) = (left as usize, right as usize); - self.nums[left..=right].iter().sum::() - } -}/** - * Your NumArray object will be instantiated and called as such: - * let obj = NumArray::new(nums); - * let ret_1: i32 = obj.sum_range(left, right); - */ -``` - -```rust -struct NumArray { - sums: Vec, + s: Vec, } /** @@ -255,15 +231,15 @@ struct NumArray { impl NumArray { fn new(mut nums: Vec) -> Self { let n = nums.len(); - let mut sums = vec![0; n + 1]; + let mut s = vec![0; n + 1]; for i in 0..n { - sums[i + 1] = sums[i] + nums[i]; + s[i + 1] = s[i] + nums[i]; } - Self { sums } + Self { s } } fn sum_range(&self, left: i32, right: i32) -> i32 { - self.sums[(right + 1) as usize] - self.sums[left as usize] + self.s[(right + 1) as usize] - self.s[left as usize] } }/** * Your NumArray object will be instantiated and called as such: @@ -276,25 +252,26 @@ impl NumArray { ```c typedef struct { - int* sums; + int* s; } NumArray; -NumArray* numArrayCreate(int* nums, int numsSize) { - int* sums = malloc(sizeof(int) * (numsSize + 1)); - memset(sums, 0, numsSize + 1); - for (int i = 0; i < numsSize; i++) { - sums[i + 1] = sums[i] + nums[i]; +NumArray* numArrayCreate(int* nums, int n) { + int* s = malloc(sizeof(int) * (n + 1)); + s[0] = 0; + for (int i = 0; i < n; i++) { + s[i + 1] = s[i] + nums[i]; } - NumArray* res = malloc(sizeof(NumArray)); - res->sums = sums; - return res; + NumArray* obj = malloc(sizeof(NumArray)); + obj->s = s; + return obj; } int numArraySumRange(NumArray* obj, int left, int right) { - return obj->sums[right + 1] - obj->sums[left]; + return obj->s[right + 1] - obj->s[left]; } void numArrayFree(NumArray* obj) { + free(obj->s); free(obj); } @@ -315,20 +292,27 @@ class NumArray { * @param Integer[] $nums */ function __construct($nums) { - $this->sum = [0]; - for ($i = 0; $i < count($nums); $i++) { - array_push($this->sum, $this->sum[$i] + $nums[$i]); + $this->s = [0]; + foreach ($nums as $x) { + $this->s[] = $this->s[count($this->s) - 1] + $x; } } + /** * @param Integer $left * @param Integer $right * @return Integer */ function sumRange($left, $right) { - return $this->sum[$right + 1] - $this->sum[$left]; + return $this->s[$right + 1] - $this->s[$left]; } } + +/** + * Your NumArray object will be instantiated and called as such: + * $obj = NumArray($nums); + * $ret_1 = $obj->sumRange($left, $right); + */ ``` ### **...** diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/README_EN.md b/solution/0300-0399/0303.Range Sum Query - Immutable/README_EN.md index 3ae04765ad644..8f772fa24ec29 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/README_EN.md +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/README_EN.md @@ -46,6 +46,12 @@ numArray.sumRange(0, 5); // return (-2) + 0 + 3 + (-5) + 2 + (-1) = -3 ## Solutions +**Solution 1: Prefix Sum** + +We create a prefix sum array $s$ of length $n + 1$, where $s[i]$ represents the prefix sum of the first $i$ elements, that is, $s[i] = \sum_{j=0}^{i-1} nums[j]$. Therefore, the sum of the elements between the indices $[left, right]$ can be expressed as $s[right + 1] - s[left]$. + +The time complexity for initializing the prefix sum array $s$ is $O(n)$, and the time complexity for querying is $O(1)$. The space complexity is $O(n)$. + ### **Python3** @@ -95,17 +101,20 @@ class NumArray { ```cpp class NumArray { public: - vector s; - NumArray(vector& nums) { int n = nums.size(); s.resize(n + 1); - for (int i = 0; i < n; ++i) s[i + 1] = s[i] + nums[i]; + for (int i = 0; i < n; ++i) { + s[i + 1] = s[i] + nums[i]; + } } int sumRange(int left, int right) { return s[right + 1] - s[left]; } + +private: + vector s; }; /** @@ -150,7 +159,7 @@ func (this *NumArray) SumRange(left int, right int) int { */ var NumArray = function (nums) { const n = nums.length; - this.s = new Array(n + 1).fill(0); + this.s = Array(n + 1).fill(0); for (let i = 0; i < n; ++i) { this.s[i + 1] = this.s[i] + nums[i]; } @@ -180,7 +189,7 @@ class NumArray { constructor(nums: number[]) { const n = nums.length; - this.s = new Array(n + 1).fill(0); + this.s = Array(n + 1).fill(0); for (let i = 0; i < n; ++i) { this.s[i + 1] = this.s[i] + nums[i]; } @@ -202,34 +211,7 @@ class NumArray { ```rust struct NumArray { - nums: Vec, -} - -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ -impl NumArray { - fn new(nums: Vec) -> Self { - Self { - nums, - } - } - - fn sum_range(&self, left: i32, right: i32) -> i32 { - let (left, right) = (left as usize, right as usize); - self.nums[left..=right].iter().sum::() - } -}/** - * Your NumArray object will be instantiated and called as such: - * let obj = NumArray::new(nums); - * let ret_1: i32 = obj.sum_range(left, right); - */ -``` - -```rust -struct NumArray { - sums: Vec, + s: Vec, } /** @@ -239,15 +221,15 @@ struct NumArray { impl NumArray { fn new(mut nums: Vec) -> Self { let n = nums.len(); - let mut sums = vec![0; n + 1]; + let mut s = vec![0; n + 1]; for i in 0..n { - sums[i + 1] = sums[i] + nums[i]; + s[i + 1] = s[i] + nums[i]; } - Self { sums } + Self { s } } fn sum_range(&self, left: i32, right: i32) -> i32 { - self.sums[(right + 1) as usize] - self.sums[left as usize] + self.s[(right + 1) as usize] - self.s[left as usize] } }/** * Your NumArray object will be instantiated and called as such: @@ -260,25 +242,26 @@ impl NumArray { ```c typedef struct { - int* sums; + int* s; } NumArray; -NumArray* numArrayCreate(int* nums, int numsSize) { - int* sums = malloc(sizeof(int) * (numsSize + 1)); - memset(sums, 0, numsSize + 1); - for (int i = 0; i < numsSize; i++) { - sums[i + 1] = sums[i] + nums[i]; +NumArray* numArrayCreate(int* nums, int n) { + int* s = malloc(sizeof(int) * (n + 1)); + s[0] = 0; + for (int i = 0; i < n; i++) { + s[i + 1] = s[i] + nums[i]; } - NumArray* res = malloc(sizeof(NumArray)); - res->sums = sums; - return res; + NumArray* obj = malloc(sizeof(NumArray)); + obj->s = s; + return obj; } int numArraySumRange(NumArray* obj, int left, int right) { - return obj->sums[right + 1] - obj->sums[left]; + return obj->s[right + 1] - obj->s[left]; } void numArrayFree(NumArray* obj) { + free(obj->s); free(obj); } @@ -299,20 +282,27 @@ class NumArray { * @param Integer[] $nums */ function __construct($nums) { - $this->sum = [0]; - for ($i = 0; $i < count($nums); $i++) { - array_push($this->sum, $this->sum[$i] + $nums[$i]); + $this->s = [0]; + foreach ($nums as $x) { + $this->s[] = $this->s[count($this->s) - 1] + $x; } } + /** * @param Integer $left * @param Integer $right * @return Integer */ function sumRange($left, $right) { - return $this->sum[$right + 1] - $this->sum[$left]; + return $this->s[$right + 1] - $this->s[$left]; } } + +/** + * Your NumArray object will be instantiated and called as such: + * $obj = NumArray($nums); + * $ret_1 = $obj->sumRange($left, $right); + */ ``` ### **...** diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.c b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.c index 56223f93d7b5c..885e2f173cd07 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.c +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.c @@ -1,30 +1,31 @@ -typedef struct { - int* sums; -} NumArray; - -NumArray* numArrayCreate(int* nums, int numsSize) { - int* sums = malloc(sizeof(int) * (numsSize + 1)); - memset(sums, 0, numsSize + 1); - for (int i = 0; i < numsSize; i++) { - sums[i + 1] = sums[i] + nums[i]; - } - NumArray* res = malloc(sizeof(NumArray)); - res->sums = sums; - return res; -} - -int numArraySumRange(NumArray* obj, int left, int right) { - return obj->sums[right + 1] - obj->sums[left]; -} - -void numArrayFree(NumArray* obj) { - free(obj); -} - -/** - * Your NumArray struct will be instantiated and called as such: - * NumArray* obj = numArrayCreate(nums, numsSize); - * int param_1 = numArraySumRange(obj, left, right); - - * numArrayFree(obj); -*/ +typedef struct { + int* s; +} NumArray; + +NumArray* numArrayCreate(int* nums, int n) { + int* s = malloc(sizeof(int) * (n + 1)); + s[0] = 0; + for (int i = 0; i < n; i++) { + s[i + 1] = s[i] + nums[i]; + } + NumArray* obj = malloc(sizeof(NumArray)); + obj->s = s; + return obj; +} + +int numArraySumRange(NumArray* obj, int left, int right) { + return obj->s[right + 1] - obj->s[left]; +} + +void numArrayFree(NumArray* obj) { + free(obj->s); + free(obj); +} + +/** + * Your NumArray struct will be instantiated and called as such: + * NumArray* obj = numArrayCreate(nums, numsSize); + * int param_1 = numArraySumRange(obj, left, right); + + * numArrayFree(obj); +*/ \ No newline at end of file diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.cpp b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.cpp index 3c8e4ec54a3cb..6d43748b70011 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.cpp +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.cpp @@ -1,20 +1,23 @@ -class NumArray { -public: - vector s; - - NumArray(vector& nums) { - int n = nums.size(); - s.resize(n + 1); - for (int i = 0; i < n; ++i) s[i + 1] = s[i] + nums[i]; - } - - int sumRange(int left, int right) { - return s[right + 1] - s[left]; - } -}; - -/** - * Your NumArray object will be instantiated and called as such: - * NumArray* obj = new NumArray(nums); - * int param_1 = obj->sumRange(left,right); +class NumArray { +public: + NumArray(vector& nums) { + int n = nums.size(); + s.resize(n + 1); + for (int i = 0; i < n; ++i) { + s[i + 1] = s[i] + nums[i]; + } + } + + int sumRange(int left, int right) { + return s[right + 1] - s[left]; + } + +private: + vector s; +}; + +/** + * Your NumArray object will be instantiated and called as such: + * NumArray* obj = new NumArray(nums); + * int param_1 = obj->sumRange(left,right); */ \ No newline at end of file diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.js b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.js index ec3c3e932309a..e9b4734f5a6b8 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.js +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.js @@ -3,7 +3,7 @@ */ var NumArray = function (nums) { const n = nums.length; - this.s = new Array(n + 1).fill(0); + this.s = Array(n + 1).fill(0); for (let i = 0; i < n; ++i) { this.s[i + 1] = this.s[i] + nums[i]; } diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.php b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.php index 742640f6db636..ca25c0e8f5028 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.php +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.php @@ -3,17 +3,24 @@ class NumArray { * @param Integer[] $nums */ function __construct($nums) { - $this->sum = [0]; - for ($i = 0; $i < count($nums); $i++) { - array_push($this->sum, $this->sum[$i] + $nums[$i]); + $this->s = [0]; + foreach ($nums as $x) { + $this->s[] = $this->s[count($this->s) - 1] + $x; } } + /** * @param Integer $left * @param Integer $right * @return Integer */ function sumRange($left, $right) { - return $this->sum[$right + 1] - $this->sum[$left]; + return $this->s[$right + 1] - $this->s[$left]; } -} \ No newline at end of file +} + +/** + * Your NumArray object will be instantiated and called as such: + * $obj = NumArray($nums); + * $ret_1 = $obj->sumRange($left, $right); + */ \ No newline at end of file diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.rs b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.rs index 5d2a6cccb368e..aedebce9f9640 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.rs +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.rs @@ -1,5 +1,5 @@ struct NumArray { - sums: Vec, + s: Vec, } /** @@ -9,15 +9,15 @@ struct NumArray { impl NumArray { fn new(mut nums: Vec) -> Self { let n = nums.len(); - let mut sums = vec![0; n + 1]; + let mut s = vec![0; n + 1]; for i in 0..n { - sums[i + 1] = sums[i] + nums[i]; + s[i + 1] = s[i] + nums[i]; } - Self { sums } + Self { s } } fn sum_range(&self, left: i32, right: i32) -> i32 { - self.sums[(right + 1) as usize] - self.sums[left as usize] + self.s[(right + 1) as usize] - self.s[left as usize] } }/** * Your NumArray object will be instantiated and called as such: diff --git a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.ts b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.ts index 9ac24a436ee48..2de8967656444 100644 --- a/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.ts +++ b/solution/0300-0399/0303.Range Sum Query - Immutable/Solution.ts @@ -3,7 +3,7 @@ class NumArray { constructor(nums: number[]) { const n = nums.length; - this.s = new Array(n + 1).fill(0); + this.s = Array(n + 1).fill(0); for (let i = 0; i < n; ++i) { this.s[i + 1] = this.s[i] + nums[i]; } diff --git a/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README.md b/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README.md index c140bb0b88dfa..25fc40cb89adf 100644 --- a/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README.md +++ b/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README.md @@ -73,7 +73,7 @@ $$ 我们在初始化方法中预处理出前缀和数组 $s$,在查询方法中直接返回上述公式的结果即可。 -初始化的时间复杂度为 $O(m\times n)$,查询的时间复杂度为 $O(1)$。 +初始化的时间复杂度为 $O(m \times n)$,查询的时间复杂度为 $O(1)$。空间复杂度为 $O(m \times n)$。 diff --git a/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README_EN.md b/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README_EN.md index bef073a8ed9ef..e02447a18ab1b 100644 --- a/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README_EN.md +++ b/solution/0300-0399/0304.Range Sum Query 2D - Immutable/README_EN.md @@ -52,23 +52,23 @@ numMatrix.sumRegion(1, 2, 2, 4); // return 12 (i.e sum of the blue rectangle) ## Solutions -We use $s[i + 1][j + 1]$ to represent the sum of all elements in the upper-left part up to the $i$-th row and $j$-th column, where the indices $i$ and $j$ both start from $0$. +**Solution 1: Two-dimensional Prefix Sum** -We can derive the following prefix sum formula: +We use $s[i + 1][j + 1]$ to represent the sum of all elements in the upper left part of the $i$th row and $j$th column, where indices $i$ and $j$ both start from $0$. We can get the following prefix sum formula: $$ s[i + 1][j + 1] = s[i + 1][j] + s[i][j + 1] - s[i][j] + nums[i][j] $$ -The sum of the elements in the rectangle with $(x_1, y_1)$ and $(x_2, y_2)$ as its upper-left and bottom-right corners respectively, is: +Then, the sum of the elements of the rectangle with $(x_1, y_1)$ and $(x_2, y_2)$ as the upper left corner and lower right corner respectively is: $$ s[x_2 + 1][y_2 + 1] - s[x_2 + 1][y_1] - s[x_1][y_2 + 1] + s[x_1][y_1] $$ -We preprocess the prefix sum array $s$ in the initialization method, and directly return the result of the above formula in the query method. +In the initialization method, we preprocess the prefix sum array $s$, and in the query method, we directly return the result of the above formula. -The time complexity for initialization is $O(m \times n)$, and the time complexity for query is $O(1)$. +The time complexity for initializing is $O(m \times n)$, and the time complexity for querying is $O(1)$. The space complexity is $O(m \times n)$. @@ -159,6 +159,7 @@ public: ### **Rust** ```rust + /** * Your NumMatrix object will be instantiated and called as such: * let obj = NumMatrix::new(matrix); diff --git a/solution/0300-0399/0305.Number of Islands II/README.md b/solution/0300-0399/0305.Number of Islands II/README.md index c6f60607951f5..eed6773192a6c 100644 --- a/solution/0300-0399/0305.Number of Islands II/README.md +++ b/solution/0300-0399/0305.Number of Islands II/README.md @@ -55,72 +55,13 @@ -并查集。 +**方法一:并查集** -并查集模板: +我们用一个二维数组 $grid$ 来表示一个地图,其中 $0$ 和 $1$ 分别表示水和陆地。初始时 $grid$ 中的所有单元格都是水单元格(即所有单元格都是 $0$),用一个变量 $cnt$ 来记录岛屿的数量。而岛屿之间的连通关系可以用一个并查集 $uf$ 来维护。 -模板 1——朴素并查集: +接下来,我们遍历数组 $positions$ 中的每个位置 $(i, j)$,如果 $grid[i][j]$ 为 $1$,说明该位置已经是陆地,我们直接将 $cnt$ 添加到答案中;否则,我们将 $grid[i][j]$ 的值改为 $1$,并且将 $cnt$ 的值增加 $1$。然后,我们遍历该位置的上下左右四个方向,如果某个方向的位置为 $1$,并且该位置与 $(i, j)$ 不属于同一个连通分量,那么我们就将该位置与 $(i, j)$ 进行合并,同时将 $cnt$ 的值减少 $1$。遍历完该位置的上下左右四个方向之后,我们将 $cnt$ 添加到答案中。 -```python -# 初始化,p存储每个点的父节点 -p = list(range(n)) - - -# 返回x的祖宗节点 -def find(x): - if p[x] != x: - # 路径压缩 - p[x] = find(p[x]) - return p[x] - - -# 合并a和b所在的两个集合 -p[find(a)] = find(b) -``` - -模板 2——维护 size 的并查集: - -```python -# 初始化,p存储每个点的父节点,size只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量 -p = list(range(n)) -size = [1] * n - - -# 返回x的祖宗节点 -def find(x): - if p[x] != x: - # 路径压缩 - p[x] = find(p[x]) - return p[x] - - -# 合并a和b所在的两个集合 -if find(a) != find(b): - size[find(b)] += size[find(a)] - p[find(a)] = find(b) -``` - -模板 3——维护到祖宗节点距离的并查集: - -```python -# 初始化,p存储每个点的父节点,d[x]存储x到p[x]的距离 -p = list(range(n)) -d = [0] * n - - -# 返回x的祖宗节点 -def find(x): - if p[x] != x: - t = find(p[x]) - d[x] += d[p[x]] - p[x] = t - return p[x] - - -# 合并a和b所在的两个集合 -p[find(a)] = find(b) -d[find(a)] = distance -``` +时间复杂度 $O(k \times \alpha(m \times n))$ 或 $O(k \times \log(m \times n))$,其中 $k$ 是 $positions$ 的长度,而 $\alpha$ 是阿克曼函数的反函数,本题中 $\alpha(m \times n)$ 可以认为是一个很小的常数。 @@ -129,32 +70,45 @@ d[find(a)] = distance ```python +class UnionFind: + def __init__(self, n: int): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x: int): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a: int, b: int) -> bool: + pa, pb = self.find(a - 1), self.find(b - 1) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + class Solution: def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]: - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] - + uf = UnionFind(m * n) grid = [[0] * n for _ in range(m)] - cnt = 0 - p = list(range(m * n)) ans = [] + dirs = (-1, 0, 1, 0, -1) + cnt = 0 for i, j in positions: - if grid[i][j] == 1: + if grid[i][j]: ans.append(cnt) continue grid[i][j] = 1 cnt += 1 - for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]: + for a, b in pairwise(dirs): x, y = i + a, j + b - if ( - 0 <= x < m - and 0 <= y < n - and grid[x][y] == 1 - and find(i * n + j) != find(x * n + y) - ): - p[find(i * n + j)] = find(x * n + y) + if 0 <= x < m and 0 <= y < n and grid[x][y] and uf.union(i * n + j, x * n + y): cnt -= 1 ans.append(cnt) return ans @@ -165,21 +119,51 @@ class Solution: ```java -class Solution { - private int[] p; - - public List numIslands2(int m, int n, int[][] positions) { - p = new int[m * n]; - for (int i = 0; i < p.length; ++i) { +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { p[i] = i; + size[i] = 1; + } + } + + public int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; } + return true; + } +} + +class Solution { + public List numIslands2(int m, int n, int[][] positions) { int[][] grid = new int[m][n]; + UnionFind uf = new UnionFind(m * n); + int[] dirs = {-1, 0, 1, 0, -1}; int cnt = 0; List ans = new ArrayList<>(); - int[] dirs = {-1, 0, 1, 0, -1}; - for (int[] pos : positions) { - int i = pos[0]; - int j = pos[1]; + for (var p : positions) { + int i = p[0], j = p[1]; if (grid[i][j] == 1) { ans.add(cnt); continue; @@ -187,11 +171,9 @@ class Solution { grid[i][j] = 1; ++cnt; for (int k = 0; k < 4; ++k) { - int x = i + dirs[k]; - int y = j + dirs[k + 1]; + int x = i + dirs[k], y = j + dirs[k + 1]; if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 - && find(x * n + y) != find(i * n + j)) { - p[find(x * n + y)] = find(i * n + j); + && uf.union(i * n + j, x * n + y)) { --cnt; } } @@ -199,33 +181,58 @@ class Solution { } return ans; } +} +``` + +### **C++** + +```cpp +class UnionFind { +public: + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } + + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } - private int find(int x) { + int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } -} -``` -### **C++** +private: + vector p, size; +}; -```cpp class Solution { public: - vector p; - vector numIslands2(int m, int n, vector>& positions) { - p.resize(m * n); - for (int i = 0; i < p.size(); ++i) p[i] = i; - vector> grid(m, vector(n)); - vector ans; + int grid[m][n]; + memset(grid, 0, sizeof(grid)); + UnionFind uf(m * n); + int dirs[5] = {-1, 0, 1, 0, -1}; int cnt = 0; - vector dirs = {-1, 0, 1, 0, -1}; - for (auto& pos : positions) { - int i = pos[0], j = pos[1]; - if (grid[i][j] == 1) { + vector ans; + for (auto& p : positions) { + int i = p[0], j = p[1]; + if (grid[i][j]) { ans.push_back(cnt); continue; } @@ -233,8 +240,7 @@ public: ++cnt; for (int k = 0; k < 4; ++k) { int x = i + dirs[k], y = j + dirs[k + 1]; - if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && find(x * n + y) != find(i * n + j)) { - p[find(x * n + y)] = find(i * n + j); + if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] && uf.unite(i * n + j, x * n + y)) { --cnt; } } @@ -242,38 +248,58 @@ public: } return ans; } - - int find(int x) { - if (p[x] != x) p[x] = find(p[x]); - return p[x]; - } }; ``` ### **Go** ```go -func numIslands2(m int, n int, positions [][]int) []int { - p := make([]int, m*n) - for i := 0; i < len(p); i++ { +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) + for i := range p { p[i] = i + size[i] = 1 + } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func numIslands2(m int, n int, positions [][]int) (ans []int) { + uf := newUnionFind(m * n) grid := make([][]int, m) - for i := 0; i < m; i++ { + for i := range grid { grid[i] = make([]int, n) } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] - } - var ans []int + dirs := [5]int{-1, 0, 1, 0, -1} cnt := 0 - dirs := []int{-1, 0, 1, 0, -1} - for _, pos := range positions { - i, j := pos[0], pos[1] + for _, p := range positions { + i, j := p[0], p[1] if grid[i][j] == 1 { ans = append(ans, cnt) continue @@ -282,14 +308,77 @@ func numIslands2(m int, n int, positions [][]int) []int { cnt++ for k := 0; k < 4; k++ { x, y := i+dirs[k], j+dirs[k+1] - if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && find(x*n+y) != find(i*n+j) { - p[find(x*n+y)] = find(i*n + j) + if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && uf.union(i*n+j, x*n+y) { cnt-- } } ans = append(ans, cnt) } - return ans + return +} +``` + +## **TypeScript** + +```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array(n) + .fill(0) + .map((_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function numIslands2(m: number, n: number, positions: number[][]): number[] { + const grid: number[][] = Array.from({ length: m }, () => Array(n).fill(0)); + const uf = new UnionFind(m * n); + const ans: number[] = []; + const dirs: number[] = [-1, 0, 1, 0, -1]; + let cnt = 0; + for (const [i, j] of positions) { + if (grid[i][j]) { + ans.push(cnt); + continue; + } + grid[i][j] = 1; + ++cnt; + for (let k = 0; k < 4; ++k) { + const [x, y] = [i + dirs[k], j + dirs[k + 1]]; + if (x < 0 || x >= m || y < 0 || y >= n || !grid[x][y]) { + continue; + } + if (uf.union(i * n + j, x * n + y)) { + --cnt; + } + } + ans.push(cnt); + } + return ans; } ``` diff --git a/solution/0300-0399/0305.Number of Islands II/README_EN.md b/solution/0300-0399/0305.Number of Islands II/README_EN.md index cf7cf5201686a..083a38e15839d 100644 --- a/solution/0300-0399/0305.Number of Islands II/README_EN.md +++ b/solution/0300-0399/0305.Number of Islands II/README_EN.md @@ -49,39 +49,58 @@ Initially, the 2d grid is filled with water. ## Solutions -Union find. +**Solution 1: Union-Find** + +We use a two-dimensional array $grid$ to represent a map, where $0$ and $1$ represent water and land respectively. Initially, all cells in $grid$ are water cells (i.e., all cells are $0$), and we use a variable $cnt$ to record the number of islands. The connectivity between islands can be maintained by a union-find set $uf$. + +Next, we traverse each position $(i, j)$ in the array $positions$. If $grid[i][j]$ is $1$, it means that this position is already land, and we directly add $cnt$ to the answer; otherwise, we change the value of $grid[i][j]$ to $1$, and increase the value of $cnt$ by $1$. Then, we traverse the four directions of up, down, left, and right of this position. If a certain direction is $1$, and this position does not belong to the same connected component as $(i, j)$, then we merge this position with $(i, j)$, and decrease the value of $cnt$ by $1$. After traversing the four directions of up, down, left, and right of this position, we add $cnt$ to the answer. + +The time complexity is $O(k \times \alpha(m \times n))$ or $O(k \times \log(m \times n))$, where $k$ is the length of $positions$, and $\alpha$ is the inverse function of the Ackermann function. In this problem, $\alpha(m \times n)$ can be considered as a very small constant. ### **Python3** ```python +class UnionFind: + def __init__(self, n: int): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x: int): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a: int, b: int) -> bool: + pa, pb = self.find(a - 1), self.find(b - 1) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + class Solution: def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]: - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] - + uf = UnionFind(m * n) grid = [[0] * n for _ in range(m)] - cnt = 0 - p = list(range(m * n)) ans = [] + dirs = (-1, 0, 1, 0, -1) + cnt = 0 for i, j in positions: - if grid[i][j] == 1: + if grid[i][j]: ans.append(cnt) continue grid[i][j] = 1 cnt += 1 - for a, b in [[0, -1], [0, 1], [1, 0], [-1, 0]]: + for a, b in pairwise(dirs): x, y = i + a, j + b - if ( - 0 <= x < m - and 0 <= y < n - and grid[x][y] == 1 - and find(i * n + j) != find(x * n + y) - ): - p[find(i * n + j)] = find(x * n + y) + if 0 <= x < m and 0 <= y < n and grid[x][y] and uf.union(i * n + j, x * n + y): cnt -= 1 ans.append(cnt) return ans @@ -90,21 +109,51 @@ class Solution: ### **Java** ```java -class Solution { - private int[] p; - - public List numIslands2(int m, int n, int[][] positions) { - p = new int[m * n]; - for (int i = 0; i < p.length; ++i) { +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { p[i] = i; + size[i] = 1; } + } + + public int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { + public List numIslands2(int m, int n, int[][] positions) { int[][] grid = new int[m][n]; + UnionFind uf = new UnionFind(m * n); + int[] dirs = {-1, 0, 1, 0, -1}; int cnt = 0; List ans = new ArrayList<>(); - int[] dirs = {-1, 0, 1, 0, -1}; - for (int[] pos : positions) { - int i = pos[0]; - int j = pos[1]; + for (var p : positions) { + int i = p[0], j = p[1]; if (grid[i][j] == 1) { ans.add(cnt); continue; @@ -112,11 +161,9 @@ class Solution { grid[i][j] = 1; ++cnt; for (int k = 0; k < 4; ++k) { - int x = i + dirs[k]; - int y = j + dirs[k + 1]; + int x = i + dirs[k], y = j + dirs[k + 1]; if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 - && find(x * n + y) != find(i * n + j)) { - p[find(x * n + y)] = find(i * n + j); + && uf.union(i * n + j, x * n + y)) { --cnt; } } @@ -124,33 +171,58 @@ class Solution { } return ans; } +} +``` + +### **C++** + +```cpp +class UnionFind { +public: + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } - private int find(int x) { + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } + + int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } -} -``` -### **C++** +private: + vector p, size; +}; -```cpp class Solution { public: - vector p; - vector numIslands2(int m, int n, vector>& positions) { - p.resize(m * n); - for (int i = 0; i < p.size(); ++i) p[i] = i; - vector> grid(m, vector(n)); - vector ans; + int grid[m][n]; + memset(grid, 0, sizeof(grid)); + UnionFind uf(m * n); + int dirs[5] = {-1, 0, 1, 0, -1}; int cnt = 0; - vector dirs = {-1, 0, 1, 0, -1}; - for (auto& pos : positions) { - int i = pos[0], j = pos[1]; - if (grid[i][j] == 1) { + vector ans; + for (auto& p : positions) { + int i = p[0], j = p[1]; + if (grid[i][j]) { ans.push_back(cnt); continue; } @@ -158,8 +230,7 @@ public: ++cnt; for (int k = 0; k < 4; ++k) { int x = i + dirs[k], y = j + dirs[k + 1]; - if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && find(x * n + y) != find(i * n + j)) { - p[find(x * n + y)] = find(i * n + j); + if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] && uf.unite(i * n + j, x * n + y)) { --cnt; } } @@ -167,38 +238,58 @@ public: } return ans; } - - int find(int x) { - if (p[x] != x) p[x] = find(p[x]); - return p[x]; - } }; ``` ### **Go** ```go -func numIslands2(m int, n int, positions [][]int) []int { - p := make([]int, m*n) - for i := 0; i < len(p); i++ { +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) + for i := range p { p[i] = i + size[i] = 1 + } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func numIslands2(m int, n int, positions [][]int) (ans []int) { + uf := newUnionFind(m * n) grid := make([][]int, m) - for i := 0; i < m; i++ { + for i := range grid { grid[i] = make([]int, n) } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] - } - var ans []int + dirs := [5]int{-1, 0, 1, 0, -1} cnt := 0 - dirs := []int{-1, 0, 1, 0, -1} - for _, pos := range positions { - i, j := pos[0], pos[1] + for _, p := range positions { + i, j := p[0], p[1] if grid[i][j] == 1 { ans = append(ans, cnt) continue @@ -207,14 +298,77 @@ func numIslands2(m int, n int, positions [][]int) []int { cnt++ for k := 0; k < 4; k++ { x, y := i+dirs[k], j+dirs[k+1] - if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && find(x*n+y) != find(i*n+j) { - p[find(x*n+y)] = find(i*n + j) + if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && uf.union(i*n+j, x*n+y) { cnt-- } } ans = append(ans, cnt) } - return ans + return +} +``` + +## **TypeScript** + +```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array(n) + .fill(0) + .map((_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function numIslands2(m: number, n: number, positions: number[][]): number[] { + const grid: number[][] = Array.from({ length: m }, () => Array(n).fill(0)); + const uf = new UnionFind(m * n); + const ans: number[] = []; + const dirs: number[] = [-1, 0, 1, 0, -1]; + let cnt = 0; + for (const [i, j] of positions) { + if (grid[i][j]) { + ans.push(cnt); + continue; + } + grid[i][j] = 1; + ++cnt; + for (let k = 0; k < 4; ++k) { + const [x, y] = [i + dirs[k], j + dirs[k + 1]]; + if (x < 0 || x >= m || y < 0 || y >= n || !grid[x][y]) { + continue; + } + if (uf.union(i * n + j, x * n + y)) { + --cnt; + } + } + ans.push(cnt); + } + return ans; } ``` diff --git a/solution/0300-0399/0305.Number of Islands II/Solution.cpp b/solution/0300-0399/0305.Number of Islands II/Solution.cpp index dffdd0297c6d0..2175b71fe3448 100644 --- a/solution/0300-0399/0305.Number of Islands II/Solution.cpp +++ b/solution/0300-0399/0305.Number of Islands II/Solution.cpp @@ -1,36 +1,62 @@ -class Solution { -public: - vector p; - - vector numIslands2(int m, int n, vector>& positions) { - p.resize(m * n); - for (int i = 0; i < p.size(); ++i) p[i] = i; - vector> grid(m, vector(n)); - vector ans; - int cnt = 0; - vector dirs = {-1, 0, 1, 0, -1}; - for (auto& pos : positions) { - int i = pos[0], j = pos[1]; - if (grid[i][j] == 1) { - ans.push_back(cnt); - continue; - } - grid[i][j] = 1; - ++cnt; - for (int k = 0; k < 4; ++k) { - int x = i + dirs[k], y = j + dirs[k + 1]; - if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && find(x * n + y) != find(i * n + j)) { - p[find(x * n + y)] = find(i * n + j); - --cnt; - } - } - ans.push_back(cnt); - } - return ans; - } - - int find(int x) { - if (p[x] != x) p[x] = find(p[x]); - return p[x]; - } +class UnionFind { +public: + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); + iota(p.begin(), p.end(), 0); + } + + bool unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + vector numIslands2(int m, int n, vector>& positions) { + int grid[m][n]; + memset(grid, 0, sizeof(grid)); + UnionFind uf(m * n); + int dirs[5] = {-1, 0, 1, 0, -1}; + int cnt = 0; + vector ans; + for (auto& p : positions) { + int i = p[0], j = p[1]; + if (grid[i][j]) { + ans.push_back(cnt); + continue; + } + grid[i][j] = 1; + ++cnt; + for (int k = 0; k < 4; ++k) { + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] && uf.union(i * n + j, x * n + y)) { + --cnt; + } + } + ans.push_back(cnt); + } + return ans; + } }; \ No newline at end of file diff --git a/solution/0300-0399/0305.Number of Islands II/Solution.go b/solution/0300-0399/0305.Number of Islands II/Solution.go index 8d5415237f054..b0b2d47bd7b16 100644 --- a/solution/0300-0399/0305.Number of Islands II/Solution.go +++ b/solution/0300-0399/0305.Number of Islands II/Solution.go @@ -1,24 +1,49 @@ -func numIslands2(m int, n int, positions [][]int) []int { - p := make([]int, m*n) - for i := 0; i < len(p); i++ { +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { + p := make([]int, n) + size := make([]int, n) + for i := range p { p[i] = i + size[i] = 1 } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func numIslands2(m int, n int, positions [][]int) (ans []int) { + uf := newUnionFind(m * n) grid := make([][]int, m) - for i := 0; i < m; i++ { + for i := range grid { grid[i] = make([]int, n) } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] - } - var ans []int + dirs := [5]int{-1, 0, 1, 0, -1} cnt := 0 - dirs := []int{-1, 0, 1, 0, -1} - for _, pos := range positions { - i, j := pos[0], pos[1] + for _, p := range positions { + i, j := p[0], p[1] if grid[i][j] == 1 { ans = append(ans, cnt) continue @@ -27,12 +52,11 @@ func numIslands2(m int, n int, positions [][]int) []int { cnt++ for k := 0; k < 4; k++ { x, y := i+dirs[k], j+dirs[k+1] - if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && find(x*n+y) != find(i*n+j) { - p[find(x*n+y)] = find(i*n + j) + if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 && uf.union(i*n+j, x*n+y) { cnt-- } } ans = append(ans, cnt) } - return ans + return } \ No newline at end of file diff --git a/solution/0300-0399/0305.Number of Islands II/Solution.java b/solution/0300-0399/0305.Number of Islands II/Solution.java index 8ff3fdacc3d63..e5808e8b70af7 100644 --- a/solution/0300-0399/0305.Number of Islands II/Solution.java +++ b/solution/0300-0399/0305.Number of Islands II/Solution.java @@ -1,42 +1,63 @@ -class Solution { - private int[] p; - - public List numIslands2(int m, int n, int[][] positions) { - p = new int[m * n]; - for (int i = 0; i < p.length; ++i) { - p[i] = i; - } - int[][] grid = new int[m][n]; - int cnt = 0; - List ans = new ArrayList<>(); - int[] dirs = {-1, 0, 1, 0, -1}; - for (int[] pos : positions) { - int i = pos[0]; - int j = pos[1]; - if (grid[i][j] == 1) { - ans.add(cnt); - continue; - } - grid[i][j] = 1; - ++cnt; - for (int k = 0; k < 4; ++k) { - int x = i + dirs[k]; - int y = j + dirs[k + 1]; - if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 - && find(x * n + y) != find(i * n + j)) { - p[find(x * n + y)] = find(i * n + j); - --cnt; - } - } - ans.add(cnt); - } - return ans; - } - - private int find(int x) { - if (p[x] != x) { - p[x] = find(p[x]); - } - return p[x]; - } +class UnionFind { + private final int[] p; + private final int[] size; + + public UnionFind(int n) { + p = new int[n]; + size = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + size[i] = 1; + } + } + + public int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + + public boolean union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa == pb) { + return false; + } + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + return true; + } +} + +class Solution { + public List numIslands2(int m, int n, int[][] positions) { + int[][] grid = new int[m][n]; + UnionFind uf = new UnionFind(m * n); + int[] dirs = {-1, 0, 1, 0, -1}; + int cnt = 0; + List ans = new ArrayList<>(); + for (var p : positions) { + int i = p[0], j = p[1]; + if (grid[i][j] == 1) { + ans.add(cnt); + continue; + } + grid[i][j] = 1; + ++cnt; + for (int k = 0; k < 4; ++k) { + int x = i + dirs[k], y = j + dirs[k + 1]; + if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1 + && uf.union(i * n + j, x * n + y)) { + --cnt; + } + } + ans.add(cnt); + } + return ans; + } } \ No newline at end of file diff --git a/solution/0300-0399/0305.Number of Islands II/Solution.py b/solution/0300-0399/0305.Number of Islands II/Solution.py index 1e77b98ff21ff..738f6d2ccc4cc 100644 --- a/solution/0300-0399/0305.Number of Islands II/Solution.py +++ b/solution/0300-0399/0305.Number of Islands II/Solution.py @@ -1,27 +1,47 @@ -class Solution: - def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]: - p = list(range(m * n)) - grid = [[0] * n for _ in range(m)] - - def check(i, j): - return 0 <= i < m and 0 <= j < n and grid[i][j] == 1 - - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] - - res = [] - cur = 0 - for i, j in positions: - if grid[i][j] == 1: - res.append(cur) - continue - grid[i][j] = 1 - cur += 1 - for x, y in [(-1, 0), (1, 0), (0, -1), (0, 1)]: - if check(i + x, j + y) and find(i * n + j) != find((i + x) * n + j + y): - p[find(i * n + j)] = find((i + x) * n + j + y) - cur -= 1 - res.append(cur) - return res +class UnionFind: + def __init__(self, n: int): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x: int): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a: int, b: int) -> bool: + pa, pb = self.find(a - 1), self.find(b - 1) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + +class Solution: + def numIslands2(self, m: int, n: int, positions: List[List[int]]) -> List[int]: + uf = UnionFind(m * n) + grid = [[0] * n for _ in range(m)] + ans = [] + dirs = (-1, 0, 1, 0, -1) + cnt = 0 + for i, j in positions: + if grid[i][j]: + ans.append(cnt) + continue + grid[i][j] = 1 + cnt += 1 + for a, b in pairwise(dirs): + x, y = i + a, j + b + if ( + 0 <= x < m + and 0 <= y < n + and grid[x][y] + and uf.union(i * n + j, x * n + y) + ): + cnt -= 1 + ans.append(cnt) + return ans diff --git a/solution/0300-0399/0305.Number of Islands II/Solution.ts b/solution/0300-0399/0305.Number of Islands II/Solution.ts new file mode 100644 index 0000000000000..c5de3dbb2d705 --- /dev/null +++ b/solution/0300-0399/0305.Number of Islands II/Solution.ts @@ -0,0 +1,59 @@ +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array(n) + .fill(0) + .map((_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function numIslands2(m: number, n: number, positions: number[][]): number[] { + const grid: number[][] = Array.from({ length: m }, () => Array(n).fill(0)); + const uf = new UnionFind(m * n); + const ans: number[] = []; + const dirs: number[] = [-1, 0, 1, 0, -1]; + let cnt = 0; + for (const [i, j] of positions) { + if (grid[i][j]) { + ans.push(cnt); + continue; + } + grid[i][j] = 1; + ++cnt; + for (let k = 0; k < 4; ++k) { + const [x, y] = [i + dirs[k], j + dirs[k + 1]]; + if (x < 0 || x >= m || y < 0 || y >= n || !grid[x][y]) { + continue; + } + if (uf.union(i * n + j, x * n + y)) { + --cnt; + } + } + ans.push(cnt); + } + return ans; +} diff --git a/solution/0300-0399/0307.Range Sum Query - Mutable/README.md b/solution/0300-0399/0307.Range Sum Query - Mutable/README.md index 38a862a6a0ae8..f71f115889f65 100644 --- a/solution/0300-0399/0307.Range Sum Query - Mutable/README.md +++ b/solution/0300-0399/0307.Range Sum Query - Mutable/README.md @@ -84,6 +84,8 @@ numArray.sumRange(0, 2); // 返回 1 + 2 + 5 = 8 - 线段树的每个叶子节点代表一个长度为 $1$ 的元区间 $[x, x]$; - 对于每个内部节点 $[l, r]$,它的左儿子是 $[l, mid]$,右儿子是 $[mid + 1, r]$, 其中 $mid = \lfloor \frac{l + r}{2} \rfloor$(即向下取整)。 +对于本题,构造函数的时间复杂度为 $O(n \log n)$,其他操作的时间复杂度为 $O(\log n)$。空间复杂度为 $O(n)$。 + ### **Python3** diff --git a/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README.md b/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README.md index ebf9002a191c8..f425d8093facf 100644 --- a/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README.md +++ b/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README.md @@ -71,7 +71,7 @@ 时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为数组 $prices$ 的长度。 -我们注意到,状态 $f[i][]$ 的转移只与 $f[i - 1][]$ 和 $f[i - 2][0]$ 有关,因此我们可以用三个变量 $f, f_0, f_1$ 代替数组 $f$,将空间复杂度优化到 $O(1)$。 +我们注意到,状态 $f[i][]$ 的转移只与 $f[i - 1][]$ 和 $f[i - 2][0]$ 有关,因此我们可以用三个变量 $f$, $f_0$, $f_1$ 代替数组 $f$,将空间复杂度优化到 $O(1)$。 diff --git a/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README_EN.md b/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README_EN.md index 40260a15abbca..9adafbe037917 100644 --- a/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README_EN.md +++ b/solution/0300-0399/0309.Best Time to Buy and Sell Stock with Cooldown/README_EN.md @@ -40,6 +40,34 @@ ## Solutions +**Solution 1: Memoization Search** + +We design a function $dfs(i, j)$, which represents the maximum profit that can be obtained starting from the $i$th day with state $j$. The values of $j$ are $0$ and $1$, respectively representing currently not holding a stock and holding a stock. The answer is $dfs(0, 0)$. + +The execution logic of the function $dfs(i, j)$ is as follows: + +If $i \geq n$, it means that there are no more stocks to trade, so return $0$; + +Otherwise, we can choose not to trade, then $dfs(i, j) = dfs(i + 1, j)$. We can also trade stocks. If $j > 0$, it means that we currently hold a stock and can sell it, then $dfs(i, j) = prices[i] + dfs(i + 2, 0)$. If $j = 0$, it means that we currently do not hold a stock and can buy, then $dfs(i, j) = -prices[i] + dfs(i + 1, 1)$. Take the maximum value as the return value of the function $dfs(i, j)$. + +The answer is $dfs(0, 0)$. + +To avoid repeated calculations, we use the method of memoization search, and use an array $f$ to record the return value of $dfs(i, j)$. If $f[i][j]$ is not $-1$, it means that it has been calculated, and we can directly return $f[i][j]$. + +The time complexity is $O(n)$, and the space complexity is $O(n)$, where $n$ is the length of the array $prices$. + +**Solution 2: Dynamic Programming** + +We can also use dynamic programming to solve this problem. + +We define $f[i][j]$ to represent the maximum profit that can be obtained on the $i$th day with state $j$. The values of $j$ are $0$ and $1$, respectively representing currently not holding a stock and holding a stock. Initially, $f[0][0] = 0$, $f[0][1] = -prices[0]$. + +When $i \geq 1$, if we currently do not hold a stock, then $f[i][0]$ can be obtained by transitioning from $f[i - 1][0]$ and $f[i - 1][1] + prices[i]$, i.e., $f[i][0] = \max(f[i - 1][0], f[i - 1][1] + prices[i])$. If we currently hold a stock, then $f[i][1]$ can be obtained by transitioning from $f[i - 1][1]$ and $f[i - 2][0] - prices[i]$, i.e., $f[i][1] = \max(f[i - 1][1], f[i - 2][0] - prices[i])$. The final answer is $f[n - 1][0]$. + +The time complexity is $O(n)$, and the space complexity is $O(n)$, where $n$ is the length of the array $prices$. + +We notice that the transition of state $f[i][]$ is only related to $f[i - 1][]$ and $f[i - 2][0]$, so we can use three variables $f$, $f_0$, $f_1$ to replace the array $f$, optimizing the space complexity to $O(1)$. + ### **Python3** diff --git a/solution/0300-0399/0311.Sparse Matrix Multiplication/README.md b/solution/0300-0399/0311.Sparse Matrix Multiplication/README.md index f0f831c0b0b98..47905fcdb2bca 100644 --- a/solution/0300-0399/0311.Sparse Matrix Multiplication/README.md +++ b/solution/0300-0399/0311.Sparse Matrix Multiplication/README.md @@ -46,7 +46,7 @@ 我们可以直接按照矩阵乘法的定义,计算出结果矩阵中的每一个元素。 -时间复杂度 $O(m \times n \times k)$,空间复杂度 $O(m \times n)$。 +时间复杂度 $O(m \times n \times k)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别是矩阵 $mat1$ 的行数和矩阵 $mat2$ 的列数,而 $k$ 是矩阵 $mat1$ 的列数或矩阵 $mat2$ 的行数。 **方法二:预处理** @@ -54,7 +54,7 @@ 接下来,我们遍历每一行 $i$,遍历 $g1[i]$ 中的每一个元素 $(k, x)$,遍历 $g2[k]$ 中的每一个元素 $(j, y)$,那么最终 $mat1[i][k] \times mat2[k][j]$ 就会对应到结果矩阵中的 $ans[i][j]$,我们将所有的结果累加即可。 -时间复杂度 $O(m \times n \times k)$,空间复杂度 $O(m \times n)$。 +时间复杂度 $O(m \times n \times k)$,空间复杂度 $O(m \times n)$。其中 $m$ 和 $n$ 分别是矩阵 $mat1$ 的行数和矩阵 $mat2$ 的列数,而 $k$ 是矩阵 $mat1$ 的列数或矩阵 $mat2$ 的行数。 diff --git a/solution/0300-0399/0311.Sparse Matrix Multiplication/README_EN.md b/solution/0300-0399/0311.Sparse Matrix Multiplication/README_EN.md index 0f5f680971d31..7f40dd6e12c1f 100644 --- a/solution/0300-0399/0311.Sparse Matrix Multiplication/README_EN.md +++ b/solution/0300-0399/0311.Sparse Matrix Multiplication/README_EN.md @@ -34,6 +34,20 @@ ## Solutions +**Solution 1: Direct Multiplication** + +We can directly calculate each element in the result matrix according to the definition of matrix multiplication. + +The time complexity is $O(m \times n \times k)$, and the space complexity is $O(m \times n)$. Where $m$ and $n$ are the number of rows of matrix $mat1$ and the number of columns of matrix $mat2$ respectively, and $k$ is the number of columns of matrix $mat1$ or the number of rows of matrix $mat2$. + +**Solution 2: Preprocessing** + +We can preprocess the sparse representation of the two matrices, i.e., $g1[i]$ represents the column index and value of all non-zero elements in the $i$th row of matrix $mat1$, and $g2[i]$ represents the column index and value of all non-zero elements in the $i$th row of matrix $mat2$. + +Next, we traverse each row $i$, traverse each element $(k, x)$ in $g1[i]$, traverse each element $(j, y)$ in $g2[k]$, then $mat1[i][k] \times mat2[k][j]$ will correspond to $ans[i][j]$ in the result matrix, and we can accumulate all the results. + +The time complexity is $O(m \times n \times k)$, and the space complexity is $O(m \times n)$. Where $m$ and $n$ are the number of rows of matrix $mat1$ and the number of columns of matrix $mat2$ respectively, and $k$ is the number of columns of matrix $mat1$ or the number of rows of matrix $mat2$. + ### **Python3** diff --git a/solution/0300-0399/0313.Super Ugly Number/README_EN.md b/solution/0300-0399/0313.Super Ugly Number/README_EN.md index 8d2ce695512e9..07bcee900aa53 100644 --- a/solution/0300-0399/0313.Super Ugly Number/README_EN.md +++ b/solution/0300-0399/0313.Super Ugly Number/README_EN.md @@ -40,7 +40,15 @@ ## Solutions -Priority Queue. +**Solution 1: Priority Queue (Min Heap)** + +We use a priority queue (min heap) to maintain all possible super ugly numbers, initially putting $1$ into the queue. + +Each time we take the smallest super ugly number $x$ from the queue, multiply $x$ by each number in the array `primes`, and put the product into the queue. Repeat the above operation $n$ times to get the $n$th super ugly number. + +Since the problem guarantees that the $n$th super ugly number is within the range of a 32-bit signed integer, before we put the product into the queue, we can first check whether the product exceeds $2^{31} - 1$. If it does, there is no need to put the product into the queue. In addition, the Euler sieve can be used for optimization. + +The time complexity is $O(n \times m \times \log (n \times m))$, and the space complexity is $O(n \times m)$. Where $m$ and $n$ are the length of the array `primes` and the given integer $n$ respectively. diff --git a/solution/0300-0399/0314.Binary Tree Vertical Order Traversal/README_EN.md b/solution/0300-0399/0314.Binary Tree Vertical Order Traversal/README_EN.md index 338b260771fe4..f9031bd0eaa82 100644 --- a/solution/0300-0399/0314.Binary Tree Vertical Order Traversal/README_EN.md +++ b/solution/0300-0399/0314.Binary Tree Vertical Order Traversal/README_EN.md @@ -40,6 +40,18 @@ ## Solutions +**Solution 1: DFS** + +DFS traverses the binary tree, recording the value, depth, and horizontal offset of each node. Then sort all nodes by horizontal offset from small to large, then by depth from small to large, and finally group by horizontal offset. + +The time complexity is $O(n\log \log n)$, and the space complexity is $O(n)$. Where $n$ is the number of nodes in the binary tree. + +**Solution 2: BFS** + +A better approach to this problem should be BFS, traversing from top to bottom level by level. + +The time complexity is $O(n\log n)$, and the space complexity is $O(n)$. Where $n$ is the number of nodes in the binary tree. + ### **Python3** diff --git a/solution/0300-0399/0316.Remove Duplicate Letters/README.md b/solution/0300-0399/0316.Remove Duplicate Letters/README.md index ffad2a86d882c..ec21fa3e43375 100644 --- a/solution/0300-0399/0316.Remove Duplicate Letters/README.md +++ b/solution/0300-0399/0316.Remove Duplicate Letters/README.md @@ -48,7 +48,7 @@ 最后将栈中元素拼接成字符串作为结果返回。 -时间复杂度 $O(n)$。其中 $n$ 为字符串 $s$ 的长度。 +时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 为字符串 $s$ 的长度。 diff --git a/solution/0300-0399/0316.Remove Duplicate Letters/README_EN.md b/solution/0300-0399/0316.Remove Duplicate Letters/README_EN.md index 837e57b2b345a..6e8ad2739be1c 100644 --- a/solution/0300-0399/0316.Remove Duplicate Letters/README_EN.md +++ b/solution/0300-0399/0316.Remove Duplicate Letters/README_EN.md @@ -34,7 +34,15 @@ ## Solutions -**Stack** +**Solution 1: Stack** + +We use an array `last` to record the last occurrence of each character, a stack to save the result string, and an array `vis` or an integer variable `mask` to record whether the current character is in the stack. + +Traverse the string $s$, for each character $c$, if $c$ is not in the stack, we need to check whether the top element of the stack is greater than $c$. If it is greater than $c$ and the top element of the stack will appear later, we pop the top element of the stack and push $c$ into the stack. + +Finally, concatenate the elements in the stack into a string and return it as the result. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Where $n$ is the length of the string $s$. diff --git a/solution/0400-0499/0415.Add Strings/README.md b/solution/0400-0499/0415.Add Strings/README.md index adcc2b3924596..6fb125ed76e43 100644 --- a/solution/0400-0499/0415.Add Strings/README.md +++ b/solution/0400-0499/0415.Add Strings/README.md @@ -55,7 +55,7 @@ 最后将答案字符串反转并返回即可。 -时间复杂度 $O(max(m, n))$,其中 $m$ 和 $n$ 分别是两个字符串的长度。忽略答案字符串的空间消耗,空间复杂度 $O(1)$。 +时间复杂度 $O(\max(m, n))$,其中 $m$ 和 $n$ 分别是两个字符串的长度。忽略答案字符串的空间消耗,空间复杂度 $O(1)$。 以下代码还实现了字符串相减,参考 `subStrings(num1, num2)` 函数。 @@ -256,8 +256,8 @@ var addStrings = function (num1, num2) { let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { - c += i < 0 ? 0 : parseInt(num1.charAt(i), 10); - c += j < 0 ? 0 : parseInt(num2.charAt(j), 10); + c += i < 0 ? 0 : +num1[i]; + c += j < 0 ? 0 : +num2[j]; ans.push(c % 10); c = Math.floor(c / 10); } @@ -282,20 +282,17 @@ var subStrings = function (num1, num2) { let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0; --i, --j) { - c = parseInt(num1.charAt(i), 10) - c; + c = +num1[i] - c; if (j >= 0) { - c -= parseInt(num2.charAt(j), 10); + c -= +num2[j]; } ans.push((c + 10) % 10); c = c < 0 ? 1 : 0; } - while (ans.length > 1 && ans[ans.length - 1] == '0') { + while (ans.length > 1 && ans.at(-1) === 0) { ans.pop(); } - if (neg) { - ans.push('-'); - } - return ans.reverse().join(''); + return (neg ? '-' : '') + ans.reverse().join(''); }; ``` @@ -303,18 +300,42 @@ var subStrings = function (num1, num2) { ```ts function addStrings(num1: string, num2: string): string { - const res = []; let i = num1.length - 1; let j = num2.length - 1; - let isOver = false; - while (i >= 0 || j >= 0 || isOver) { - const x = Number(num1[i--]) || 0; - const y = Number(num2[j--]) || 0; - const sum = x + y + (isOver ? 1 : 0); - isOver = sum >= 10; - res.push(sum % 10); + const ans: number[] = []; + for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { + c += i < 0 ? 0 : +num1[i]; + c += j < 0 ? 0 : +num2[j]; + ans.push(c % 10); + c = Math.floor(c / 10); + } + return ans.reverse().join(''); +} + +function subStrings(num1: string, num2: string): string { + const m = num1.length; + const n = num2.length; + const neg = m < n || (m == n && num1 < num2); + if (neg) { + const t = num1; + num1 = num2; + num2 = t; + } + let i = num1.length - 1; + let j = num2.length - 1; + const ans: number[] = []; + for (let c = 0; i >= 0; --i, --j) { + c = +num1[i] - c; + if (j >= 0) { + c -= +num2[j]; + } + ans.push((c + 10) % 10); + c = c < 0 ? 1 : 0; + } + while (ans.length > 1 && ans.at(-1) === 0) { + ans.pop(); } - return res.reverse().join(''); + return (neg ? '-' : '') + ans.reverse().join(''); } ``` diff --git a/solution/0400-0499/0415.Add Strings/README_EN.md b/solution/0400-0499/0415.Add Strings/README_EN.md index dfada01a0d23b..3c04568b9e91b 100644 --- a/solution/0400-0499/0415.Add Strings/README_EN.md +++ b/solution/0400-0499/0415.Add Strings/README_EN.md @@ -41,6 +41,16 @@ ## Solutions +**Solution 1: Two Pointers** + +We use two pointers $i$ and $j$ to point to the end of the two strings respectively, and start adding bit by bit from the end. Each time we take out the corresponding digits $a$ and $b$, calculate their sum $a + b + c$, where $c$ represents the carry from the last addition. Finally, we append the units digit of $a + b + c$ to the end of the answer string, and then take the tens digit of $a + b + c$ as the value of the carry $c$, and loop this process until the pointers of both strings have pointed to the beginning of the string and the value of the carry $c$ is $0$. + +Finally, reverse the answer string and return it. + +The time complexity is $O(\max(m, n))$, where $m$ and $n$ are the lengths of the two strings respectively. Ignoring the space consumption of the answer string, the space complexity is $O(1)$. + +The following code also implements string subtraction, refer to the `subStrings(num1, num2)` function. + ### **Python3** @@ -234,8 +244,8 @@ var addStrings = function (num1, num2) { let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { - c += i < 0 ? 0 : parseInt(num1.charAt(i), 10); - c += j < 0 ? 0 : parseInt(num2.charAt(j), 10); + c += i < 0 ? 0 : +num1[i]; + c += j < 0 ? 0 : +num2[j]; ans.push(c % 10); c = Math.floor(c / 10); } @@ -260,20 +270,17 @@ var subStrings = function (num1, num2) { let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0; --i, --j) { - c = parseInt(num1.charAt(i), 10) - c; + c = +num1[i] - c; if (j >= 0) { - c -= parseInt(num2.charAt(j), 10); + c -= +num2[j]; } ans.push((c + 10) % 10); c = c < 0 ? 1 : 0; } - while (ans.length > 1 && ans[ans.length - 1] == '0') { + while (ans.length > 1 && ans.at(-1) === 0) { ans.pop(); } - if (neg) { - ans.push('-'); - } - return ans.reverse().join(''); + return (neg ? '-' : '') + ans.reverse().join(''); }; ``` @@ -281,18 +288,42 @@ var subStrings = function (num1, num2) { ```ts function addStrings(num1: string, num2: string): string { - const res = []; let i = num1.length - 1; let j = num2.length - 1; - let isOver = false; - while (i >= 0 || j >= 0 || isOver) { - const x = Number(num1[i--]) || 0; - const y = Number(num2[j--]) || 0; - const sum = x + y + (isOver ? 1 : 0); - isOver = sum >= 10; - res.push(sum % 10); + const ans: number[] = []; + for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { + c += i < 0 ? 0 : +num1[i]; + c += j < 0 ? 0 : +num2[j]; + ans.push(c % 10); + c = Math.floor(c / 10); + } + return ans.reverse().join(''); +} + +function subStrings(num1: string, num2: string): string { + const m = num1.length; + const n = num2.length; + const neg = m < n || (m == n && num1 < num2); + if (neg) { + const t = num1; + num1 = num2; + num2 = t; + } + let i = num1.length - 1; + let j = num2.length - 1; + const ans: number[] = []; + for (let c = 0; i >= 0; --i, --j) { + c = +num1[i] - c; + if (j >= 0) { + c -= +num2[j]; + } + ans.push((c + 10) % 10); + c = c < 0 ? 1 : 0; + } + while (ans.length > 1 && ans.at(-1) === 0) { + ans.pop(); } - return res.reverse().join(''); + return (neg ? '-' : '') + ans.reverse().join(''); } ``` diff --git a/solution/0400-0499/0415.Add Strings/Solution.js b/solution/0400-0499/0415.Add Strings/Solution.js index 6a74f80c2589a..e8e44bb1b48b1 100644 --- a/solution/0400-0499/0415.Add Strings/Solution.js +++ b/solution/0400-0499/0415.Add Strings/Solution.js @@ -8,8 +8,8 @@ var addStrings = function (num1, num2) { let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { - c += i < 0 ? 0 : parseInt(num1.charAt(i), 10); - c += j < 0 ? 0 : parseInt(num2.charAt(j), 10); + c += i < 0 ? 0 : +num1[i]; + c += j < 0 ? 0 : +num2[j]; ans.push(c % 10); c = Math.floor(c / 10); } @@ -34,18 +34,15 @@ var subStrings = function (num1, num2) { let j = num2.length - 1; const ans = []; for (let c = 0; i >= 0; --i, --j) { - c = parseInt(num1.charAt(i), 10) - c; + c = +num1[i] - c; if (j >= 0) { - c -= parseInt(num2.charAt(j), 10); + c -= +num2[j]; } ans.push((c + 10) % 10); c = c < 0 ? 1 : 0; } - while (ans.length > 1 && ans[ans.length - 1] == '0') { + while (ans.length > 1 && ans.at(-1) === 0) { ans.pop(); } - if (neg) { - ans.push('-'); - } - return ans.reverse().join(''); + return (neg ? '-' : '') + ans.reverse().join(''); }; diff --git a/solution/0400-0499/0415.Add Strings/Solution.ts b/solution/0400-0499/0415.Add Strings/Solution.ts index b715c7328047d..875b94a001c59 100644 --- a/solution/0400-0499/0415.Add Strings/Solution.ts +++ b/solution/0400-0499/0415.Add Strings/Solution.ts @@ -1,14 +1,38 @@ function addStrings(num1: string, num2: string): string { - const res = []; let i = num1.length - 1; let j = num2.length - 1; - let isOver = false; - while (i >= 0 || j >= 0 || isOver) { - const x = Number(num1[i--]) || 0; - const y = Number(num2[j--]) || 0; - const sum = x + y + (isOver ? 1 : 0); - isOver = sum >= 10; - res.push(sum % 10); - } - return res.reverse().join(''); + const ans: number[] = []; + for (let c = 0; i >= 0 || j >= 0 || c; --i, --j) { + c += i < 0 ? 0 : +num1[i]; + c += j < 0 ? 0 : +num2[j]; + ans.push(c % 10); + c = Math.floor(c / 10); + } + return ans.reverse().join(''); +} + +function subStrings(num1: string, num2: string): string { + const m = num1.length; + const n = num2.length; + const neg = m < n || (m == n && num1 < num2); + if (neg) { + const t = num1; + num1 = num2; + num2 = t; + } + let i = num1.length - 1; + let j = num2.length - 1; + const ans: number[] = []; + for (let c = 0; i >= 0; --i, --j) { + c = +num1[i] - c; + if (j >= 0) { + c -= +num2[j]; + } + ans.push((c + 10) % 10); + c = c < 0 ? 1 : 0; + } + while (ans.length > 1 && ans.at(-1) === 0) { + ans.pop(); + } + return (neg ? '-' : '') + ans.reverse().join(''); } diff --git a/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README.md b/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README.md index 2591c48e172a2..efe07d5b8af3e 100644 --- a/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README.md +++ b/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README.md @@ -219,9 +219,9 @@ function minimumMountainRemovals(nums: number[]): number { } ``` -### **TypeScript** +### **Rust** -```ts +```rust impl Solution { pub fn minimum_mountain_removals(nums: Vec) -> i32 { let n = nums.len(); diff --git a/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README_EN.md b/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README_EN.md index 0c483fbc51c62..cac1ba743c46a 100644 --- a/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README_EN.md +++ b/solution/1600-1699/1671.Minimum Number of Removals to Make Mountain Array/README_EN.md @@ -209,9 +209,9 @@ function minimumMountainRemovals(nums: number[]): number { } ``` -### **TypeScript** +### **Rust** -```ts +```rust impl Solution { pub fn minimum_mountain_removals(nums: Vec) -> i32 { let n = nums.len(); diff --git a/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README.md b/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README.md index 5d82cfe9e7291..2d921928eb591 100644 --- a/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README.md +++ b/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README.md @@ -112,16 +112,16 @@ int search(int left, int right) { } ``` -做二分题目时,可以按照以下步骤: +做二分题目时,可以按照以下套路: -1. 写出循环条件:`while (left < right)`,注意是 `left < right`,而非 `left <= right`; -1. 循环体内,先无脑写出 `mid = (left + right) >> 1`; -1. 根据具体题目,实现 `check()` 函数(有时很简单的逻辑,可以不定义 `check`),想一下究竟要用 `right = mid`(模板 1) 还是 `left = mid`(模板 2); - - 如果 `right = mid`,那么无脑写出 else 语句 `left = mid + 1`,并且不需要更改 mid 的计算,即保持 `mid = (left + right) >> 1`; - - 如果 `left = mid`,那么无脑写出 else 语句 `right = mid - 1`,并且在 mid 计算时补充 +1,即 `mid = (left + right + 1) >> 1`。 -1. 循环结束时,left 与 right 相等。 +1. 写出循环条件 $left < right$; +1. 循环体内,不妨先写 $mid = \lfloor \frac{left + right}{2} \rfloor$; +1. 根据具体题目,实现 $check()$ 函数(有时很简单的逻辑,可以不定义 $check$),想一下究竟要用 $right = mid$(模板 $1$) 还是 $left = mid$(模板 $2$); +     - 如果 $right = mid$,那么写出 else 语句 $left = mid + 1$,并且不需要更改 mid 的计算,即保持 $mid = \lfloor \frac{left + right}{2} \rfloor$; +     - 如果 $left = mid$,那么写出 else 语句 $right = mid - 1$,并且在 $mid$ 计算时补充 +1,即 $mid = \lfloor \frac{left + right + 1}{2} \rfloor$; +1. 循环结束时,$left$ 与 $right$ 相等。 -注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 left 或者 right 是否满足题意即可。 +注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 $left$ 或者 $right$ 是否满足题意即可。 diff --git a/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README_EN.md b/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README_EN.md index 588262b9abd13..bd845221efa41 100644 --- a/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README_EN.md +++ b/solution/1800-1899/1870.Minimum Speed to Arrive on Time/README_EN.md @@ -102,6 +102,17 @@ int search(int left, int right) { } ``` +When doing binary search problems, you can follow the following routine: + +1. Write out the loop condition $left < right$; +2. Inside the loop, you might as well write $mid = \lfloor \frac{left + right}{2} \rfloor$ first; +3. According to the specific problem, implement the $check()$ function (sometimes the logic is very simple, you can not define $check$), think about whether to use $right = mid$ (Template $1$) or $left = mid$ (Template $2$); + - If $right = mid$, then write the else statement $left = mid + 1$, and there is no need to change the calculation of $mid$, that is, keep $mid = \lfloor \frac{left + right}{2} \rfloor$; + - If $left = mid$, then write the else statement $right = mid - 1$, and add +1 when calculating $mid$, that is, $mid = \lfloor \frac{left + right + 1}{2} \rfloor$; +4. When the loop ends, $left$ equals $right$. + +Note that the advantage of these two templates is that they always keep the answer within the binary search interval, and the value corresponding to the end condition of the binary search is exactly at the position of the answer. For the case that may have no solution, just check whether the $left$ or $right$ after the binary search ends satisfies the problem. + ### **Python3** diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README.md b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README.md index 8d150771ebcc6..fc9519935e519 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README.md +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README.md @@ -61,58 +61,13 @@ -**方法一:前缀和 + 二分查找** +**方法一:求和取余 + 模拟** -以下是二分查找的两个通用模板: +由于学生的回答是一轮一轮循环进行的,因此我们可以将所有学生需要消耗的粉笔数加起来,得到一个总数 $s$。然后我们对 $k$ 取 $s$ 的余数,即可知道最后一轮结束后剩余的粉笔数。 -模板 1: +接下来,我们只需要模拟最后一轮即可。初始时,剩余的粉笔数为 $k$,编号为 $0$ 的学生开始回答问题。当剩余的粉笔数小于当前学生需要的粉笔数时,当前学生需要补充粉笔,我们直接返回当前学生的编号 $i$ 即可。否则,我们将剩余的粉笔数减去当前学生需要的粉笔数,并将当前学生的编号 $i$ 加一,进行下一次模拟。 -```java -boolean check(int x) { -} - -int search(int left, int right) { - while (left < right) { - int mid = (left + right) >> 1; - if (check(mid)) { - right = mid; - } else { - left = mid + 1; - } - } - return left; -} -``` - -模板 2: - -```java -boolean check(int x) { -} - -int search(int left, int right) { - while (left < right) { - int mid = (left + right + 1) >> 1; - if (check(mid)) { - left = mid; - } else { - right = mid - 1; - } - } - return left; -} -``` - -做二分题目时,可以按照以下步骤: - -1. 写出循环条件:`while (left < right)`,注意是 `left < right`,而非 `left <= right`; -1. 循环体内,先无脑写出 `mid = (left + right) >> 1`; -1. 根据具体题目,实现 `check()` 函数(有时很简单的逻辑,可以不定义 `check`),想一下究竟要用 `right = mid`(模板 1) 还是 `left = mid`(模板 2); - - 如果 `right = mid`,那么无脑写出 else 语句 `left = mid + 1`,并且不需要更改 mid 的计算,即保持 `mid = (left + right) >> 1`; - - 如果 `left = mid`,那么无脑写出 else 语句 `right = mid - 1`,并且在 mid 计算时补充 +1,即 `mid = (left + right + 1) >> 1`。 -1. 循环结束时,left 与 right 相等。 - -注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 left 或者 right 是否满足题意即可。 +时间复杂度 $O(n)$,其中 $n$ 是学生的数量。空间复杂度 $O(1)$。 @@ -123,9 +78,12 @@ int search(int left, int right) { ```python class Solution: def chalkReplacer(self, chalk: List[int], k: int) -> int: - s = list(accumulate(chalk)) - k %= s[-1] - return bisect_right(s, k) + s = sum(chalk) + k %= s + for i, x in enumerate(chalk): + if k < x: + return i + k -= x ``` ### **Java** @@ -135,22 +93,17 @@ class Solution: ```java class Solution { public int chalkReplacer(int[] chalk, int k) { - int n = chalk.length; - long[] preSum = new long[n + 1]; - for (int i = 0; i < n; ++i) { - preSum[i + 1] = preSum[i] + chalk[i]; + long s = 0; + for (int x : chalk) { + s += x; } - k %= preSum[n]; - int left = 0, right = n - 1; - while (left < right) { - int mid = (left + right) >> 1; - if (preSum[mid + 1] > k) { - right = mid; - } else { - left = mid + 1; + k %= s; + for (int i = 0;; ++i) { + if (k < chalk[i]) { + return i; } + k -= chalk[i]; } - return left; } } ``` @@ -161,11 +114,14 @@ class Solution { class Solution { public: int chalkReplacer(vector& chalk, int k) { - int n = chalk.size(); - vector s(n, chalk[0]); - for (int i = 1; i < n; ++i) s[i] = s[i - 1] + chalk[i]; - k %= s[n - 1]; - return upper_bound(s.begin(), s.end(), k) - s.begin(); + long long s = accumulate(chalk.begin(), chalk.end(), 0LL); + k %= s; + for (int i = 0;; ++i) { + if (k < chalk[i]) { + return i; + } + k -= chalk[i]; + } } }; ``` @@ -174,13 +130,17 @@ public: ```go func chalkReplacer(chalk []int, k int) int { - n := len(chalk) - s := make([]int, n+1) - for i := 0; i < n; i++ { - s[i+1] = s[i] + chalk[i] + s := 0 + for _, x := range chalk { + s += x + } + k %= s + for i := 0; ; i++ { + if k < chalk[i] { + return i + } + k -= chalk[i] } - k %= s[n] - return sort.Search(n, func(i int) bool { return s[i+1] > k }) } ``` @@ -189,19 +149,36 @@ func chalkReplacer(chalk []int, k int) int { ```rust impl Solution { pub fn chalk_replacer(chalk: Vec, k: i32) -> i32 { - let pre_sum: Vec = chalk - .into_iter() - .map(|x| x as i64) - .scan(0, |state, x| { - *state += x; - Some(*state) - }) - .collect(); - - pre_sum.binary_search(&((k as i64) % pre_sum.last().unwrap())).map_or_else( - |e| e, - |v| v + 1 - ) as i32 + let mut s: i64 = chalk + .iter() + .map(|&x| x as i64) + .sum(); + let mut k = (k as i64) % s; + for (i, &x) in chalk.iter().enumerate() { + if k < (x as i64) { + return i as i32; + } + k -= x as i64; + } + 0 + } +} +``` + +### **TypeScript** + +```ts +function chalkReplacer(chalk: number[], k: number): number { + let s = 0; + for (const x of chalk) { + s += x; + } + k %= s; + for (let i = 0; ; ++i) { + if (k < chalk[i]) { + return i; + } + k -= chalk[i]; } } ``` diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README_EN.md b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README_EN.md index d427ed28f408d..63d62b17f6ffa 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README_EN.md +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/README_EN.md @@ -55,7 +55,13 @@ Student number 1 does not have enough chalk, so they will have to replace it. ## Solutions -PreSum and Binary search. +**Solution 1: Sum and Modulo + Simulation** + +Since the students' answers are conducted in rounds, we can add up the chalk needed by all students to get a total $s$. Then we take the remainder of $k$ by $s$, which can tell us the remaining number of chalks after the last round. + +Next, we just need to simulate the last round. Initially, the remaining number of chalks is $k$, and the student with the number $0$ starts to answer the question. When the remaining number of chalks is less than the current student needs, the current student needs to replenish the chalk, and we directly return the current student's number $i$. Otherwise, we subtract the chalk needed by the current student from the remaining chalk, and add one to the current student's number $i$ for the next simulation. + +The time complexity is $O(n)$, where $n$ is the number of students. The space complexity is $O(1)$. @@ -64,9 +70,12 @@ PreSum and Binary search. ```python class Solution: def chalkReplacer(self, chalk: List[int], k: int) -> int: - s = list(accumulate(chalk)) - k %= s[-1] - return bisect_right(s, k) + s = sum(chalk) + k %= s + for i, x in enumerate(chalk): + if k < x: + return i + k -= x ``` ### **Java** @@ -74,22 +83,17 @@ class Solution: ```java class Solution { public int chalkReplacer(int[] chalk, int k) { - int n = chalk.length; - long[] preSum = new long[n + 1]; - for (int i = 0; i < n; ++i) { - preSum[i + 1] = preSum[i] + chalk[i]; + long s = 0; + for (int x : chalk) { + s += x; } - k %= preSum[n]; - int left = 0, right = n - 1; - while (left < right) { - int mid = (left + right) >> 1; - if (preSum[mid + 1] > k) { - right = mid; - } else { - left = mid + 1; + k %= s; + for (int i = 0;; ++i) { + if (k < chalk[i]) { + return i; } + k -= chalk[i]; } - return left; } } ``` @@ -100,11 +104,14 @@ class Solution { class Solution { public: int chalkReplacer(vector& chalk, int k) { - int n = chalk.size(); - vector s(n, chalk[0]); - for (int i = 1; i < n; ++i) s[i] = s[i - 1] + chalk[i]; - k %= s[n - 1]; - return upper_bound(s.begin(), s.end(), k) - s.begin(); + long long s = accumulate(chalk.begin(), chalk.end(), 0LL); + k %= s; + for (int i = 0;; ++i) { + if (k < chalk[i]) { + return i; + } + k -= chalk[i]; + } } }; ``` @@ -113,13 +120,17 @@ public: ```go func chalkReplacer(chalk []int, k int) int { - n := len(chalk) - s := make([]int, n+1) - for i := 0; i < n; i++ { - s[i+1] = s[i] + chalk[i] + s := 0 + for _, x := range chalk { + s += x + } + k %= s + for i := 0; ; i++ { + if k < chalk[i] { + return i + } + k -= chalk[i] } - k %= s[n] - return sort.Search(n, func(i int) bool { return s[i+1] > k }) } ``` @@ -128,19 +139,36 @@ func chalkReplacer(chalk []int, k int) int { ```rust impl Solution { pub fn chalk_replacer(chalk: Vec, k: i32) -> i32 { - let pre_sum: Vec = chalk - .into_iter() - .map(|x| x as i64) - .scan(0, |state, x| { - *state += x; - Some(*state) - }) - .collect(); - - pre_sum.binary_search(&((k as i64) % pre_sum.last().unwrap())).map_or_else( - |e| e, - |v| v + 1 - ) as i32 + let mut s: i64 = chalk + .iter() + .map(|&x| x as i64) + .sum(); + let mut k = (k as i64) % s; + for (i, &x) in chalk.iter().enumerate() { + if k < (x as i64) { + return i as i32; + } + k -= x as i64; + } + 0 + } +} +``` + +### **TypeScript** + +```ts +function chalkReplacer(chalk: number[], k: number): number { + let s = 0; + for (const x of chalk) { + s += x; + } + k %= s; + for (let i = 0; ; ++i) { + if (k < chalk[i]) { + return i; + } + k -= chalk[i]; } } ``` diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.cpp b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.cpp index 1c49693d1879d..3448711b14fb8 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.cpp +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.cpp @@ -1,10 +1,13 @@ class Solution { public: int chalkReplacer(vector& chalk, int k) { - int n = chalk.size(); - vector s(n, chalk[0]); - for (int i = 1; i < n; ++i) s[i] = s[i - 1] + chalk[i]; - k %= s[n - 1]; - return upper_bound(s.begin(), s.end(), k) - s.begin(); + long long s = accumulate(chalk.begin(), chalk.end(), 0LL); + k %= s; + for (int i = 0;; ++i) { + if (k < chalk[i]) { + return i; + } + k -= chalk[i]; + } } }; \ No newline at end of file diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.go b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.go index ce7a0de2114bc..14e167fa004ef 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.go +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.go @@ -1,9 +1,13 @@ func chalkReplacer(chalk []int, k int) int { - n := len(chalk) - s := make([]int, n+1) - for i := 0; i < n; i++ { - s[i+1] = s[i] + chalk[i] + s := 0 + for _, x := range chalk { + s += x + } + k %= s + for i := 0; ; i++ { + if k < chalk[i] { + return i + } + k -= chalk[i] } - k %= s[n] - return sort.Search(n, func(i int) bool { return s[i+1] > k }) } \ No newline at end of file diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.java b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.java index efdf40d6d8016..6fec2a84f3602 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.java +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.java @@ -1,21 +1,15 @@ class Solution { public int chalkReplacer(int[] chalk, int k) { - int n = chalk.length; - long[] s = new long[n + 1]; - s[0] = chalk[0]; - for (int i = 1; i < n; ++i) { - s[i] = s[i - 1] + chalk[i]; + long s = 0; + for (int x : chalk) { + s += x; } - k %= s[n - 1]; - int left = 0, right = n - 1; - while (left < right) { - int mid = (left + right) >> 1; - if (s[mid] > k) { - right = mid; - } else { - left = mid + 1; + k %= s; + for (int i = 0;; ++i) { + if (k < chalk[i]) { + return i; } + k -= chalk[i]; } - return left; } } \ No newline at end of file diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.py b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.py index 44cd704347bd7..e68cc368b5845 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.py +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.py @@ -1,5 +1,8 @@ class Solution: def chalkReplacer(self, chalk: List[int], k: int) -> int: - s = list(accumulate(chalk)) - k %= s[-1] - return bisect_right(s, k) + s = sum(chalk) + k %= s + for i, x in enumerate(chalk): + if k < x: + return i + k -= x diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.rs b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.rs index da08095089d35..37cf29fe22368 100644 --- a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.rs +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.rs @@ -1,17 +1,16 @@ impl Solution { pub fn chalk_replacer(chalk: Vec, k: i32) -> i32 { - let pre_sum: Vec = chalk - .into_iter() - .map(|x| x as i64) - .scan(0, |state, x| { - *state += x; - Some(*state) - }) - .collect(); - - pre_sum.binary_search(&((k as i64) % pre_sum.last().unwrap())).map_or_else( - |e| e, - |v| v + 1 - ) as i32 + let mut s: i64 = chalk + .iter() + .map(|&x| x as i64) + .sum(); + let mut k = (k as i64) % s; + for (i, &x) in chalk.iter().enumerate() { + if k < (x as i64) { + return i as i32; + } + k -= x as i64; + } + 0 } } diff --git a/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.ts b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.ts new file mode 100644 index 0000000000000..4c4e55915d353 --- /dev/null +++ b/solution/1800-1899/1894.Find the Student that Will Replace the Chalk/Solution.ts @@ -0,0 +1,13 @@ +function chalkReplacer(chalk: number[], k: number): number { + let s = 0; + for (const x of chalk) { + s += x; + } + k %= s; + for (let i = 0; ; ++i) { + if (k < chalk[i]) { + return i; + } + k -= chalk[i]; + } +} diff --git a/solution/1800-1899/1898.Maximum Number of Removable Characters/README.md b/solution/1800-1899/1898.Maximum Number of Removable Characters/README.md index 15f1559099ee0..bf054f19c81b9 100644 --- a/solution/1800-1899/1898.Maximum Number of Removable Characters/README.md +++ b/solution/1800-1899/1898.Maximum Number of Removable Characters/README.md @@ -105,16 +105,16 @@ int search(int left, int right) { } ``` -做二分题目时,可以按照以下步骤: +做二分题目时,可以按照以下套路: -1. 写出循环条件:`while (left < right)`,注意是 `left < right`,而非 `left <= right`; -1. 循环体内,先无脑写出 `mid = (left + right) >> 1`; -1. 根据具体题目,实现 `check()` 函数(有时很简单的逻辑,可以不定义 `check`),想一下究竟要用 `right = mid`(模板 1) 还是 `left = mid`(模板 2); - - 如果 `right = mid`,那么无脑写出 else 语句 `left = mid + 1`,并且不需要更改 mid 的计算,即保持 `mid = (left + right) >> 1`; - - 如果 `left = mid`,那么无脑写出 else 语句 `right = mid - 1`,并且在 mid 计算时补充 +1,即 `mid = (left + right + 1) >> 1`。 -1. 循环结束时,left 与 right 相等。 +1. 写出循环条件 $left < right$; +1. 循环体内,不妨先写 $mid = \lfloor \frac{left + right}{2} \rfloor$; +1. 根据具体题目,实现 $check()$ 函数(有时很简单的逻辑,可以不定义 $check$),想一下究竟要用 $right = mid$(模板 $1$) 还是 $left = mid$(模板 $2$); +     - 如果 $right = mid$,那么写出 else 语句 $left = mid + 1$,并且不需要更改 mid 的计算,即保持 $mid = \lfloor \frac{left + right}{2} \rfloor$; +     - 如果 $left = mid$,那么写出 else 语句 $right = mid - 1$,并且在 $mid$ 计算时补充 +1,即 $mid = \lfloor \frac{left + right + 1}{2} \rfloor$; +1. 循环结束时,$left$ 与 $right$ 相等。 -注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 left 或者 right 是否满足题意即可。 +注意,这两个模板的优点是始终保持答案位于二分区间内,二分结束条件对应的值恰好在答案所处的位置。 对于可能无解的情况,只要判断二分结束后的 $left$ 或者 $right$ 是否满足题意即可。