From ef5e1e1c39d1e34a88fdb7c7fdc26a965df5ace6 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Tue, 28 Nov 2023 20:21:24 +0800 Subject: [PATCH 1/2] feat: add solutions to lc problems --- .../0000-0099/0002.Add Two Numbers/README.md | 2 +- .../0002.Add Two Numbers/README_EN.md | 2 +- .../README_EN.md | 19 +- .../README_EN.md | 17 +- .../0006.Zigzag Conversion/README_EN.md | 6 +- .../0007.Reverse Integer/README_EN.md | 20 +- .../0008.String to Integer (atoi)/README.md | 12 +- .../README_EN.md | 12 + .../0009.Palindrome Number/README_EN.md | 24 ++ .../README_EN.md | 27 ++ .../README_EN.md | 10 + .../0012.Integer to Roman/README_EN.md | 4 +- .../0013.Roman to Integer/README_EN.md | 6 +- .../0014.Longest Common Prefix/README_EN.md | 6 +- .../0000-0099/0016.3Sum Closest/README_EN.md | 6 + .../README.md | 354 +++++++++++------ .../README_EN.md | 358 ++++++++++++------ .../Solution.cpp | 38 +- .../Solution.js | 4 +- .../Solution.py | 20 +- .../Solution.rs | 42 +- .../Solution.ts | 37 +- solution/0000-0099/0018.4Sum/README_EN.md | 12 +- .../README.md | 4 +- .../README_EN.md | 8 + .../0020.Valid Parentheses/README_EN.md | 12 + .../0021.Merge Two Sorted Lists/README_EN.md | 19 + .../0022.Generate Parentheses/README_EN.md | 13 +- .../0023.Merge k Sorted Lists/README_EN.md | 6 + .../0024.Swap Nodes in Pairs/README_EN.md | 14 +- .../README_EN.md | 4 +- .../README.md | 4 +- .../README_EN.md | 21 + .../README.md | 6 +- .../README_EN.md | 16 +- .../0029.Divide Two Integers/README_EN.md | 8 +- .../README_EN.md | 8 +- .../README.md | 20 +- .../README_EN.md | 17 +- .../README.md | 2 +- .../README_EN.md | 8 +- .../0035.Search Insert Position/README.md | 4 +- .../0035.Search Insert Position/README_EN.md | 6 +- 43 files changed, 851 insertions(+), 387 deletions(-) diff --git a/solution/0000-0099/0002.Add Two Numbers/README.md b/solution/0000-0099/0002.Add Two Numbers/README.md index 88366a856db39..4c93234ccee35 100644 --- a/solution/0000-0099/0002.Add Two Numbers/README.md +++ b/solution/0000-0099/0002.Add Two Numbers/README.md @@ -58,7 +58,7 @@ 最后我们返回答案链表的头节点即可。 -时间复杂度 $O(max(m, n))$,其中 $m$ 和 $n$ 分别为两个链表的长度。我们需要遍历两个链表的全部位置,而处理每个位置只需要 $O(1)$ 的时间。忽略答案的空间消耗,空间复杂度 $O(1)$。 +时间复杂度 $O(\max(m, n))$,其中 $m$ 和 $n$ 分别为两个链表的长度。我们需要遍历两个链表的全部位置,而处理每个位置只需要 $O(1)$ 的时间。忽略答案的空间消耗,空间复杂度 $O(1)$。 diff --git a/solution/0000-0099/0002.Add Two Numbers/README_EN.md b/solution/0000-0099/0002.Add Two Numbers/README_EN.md index fd42b5ee6c37c..835f7c5b844a5 100644 --- a/solution/0000-0099/0002.Add Two Numbers/README_EN.md +++ b/solution/0000-0099/0002.Add Two Numbers/README_EN.md @@ -50,7 +50,7 @@ Each time we traverse, we take out the current bit of the corresponding linked l Finally, we return the head node of the answer linked list. -The time complexity is $O(max (m, n))$, where $m$ and $n$ are the lengths of the two linked lists. We need to traverse the entire position of the two linked lists, and each position only needs $O(1)$ time. Ignoring the space consumption of the answer, the space complexity is $O(1)$. +The time complexity is $O(\max (m, n))$, where $m$ and $n$ are the lengths of the two linked lists. We need to traverse the entire position of the two linked lists, and each position only needs $O(1)$ time. Ignoring the space consumption of the answer, the space complexity is $O(1)$. diff --git a/solution/0000-0099/0004.Median of Two Sorted Arrays/README_EN.md b/solution/0000-0099/0004.Median of Two Sorted Arrays/README_EN.md index 4bfa2b15390a5..10b0f148b2e6f 100644 --- a/solution/0000-0099/0004.Median of Two Sorted Arrays/README_EN.md +++ b/solution/0000-0099/0004.Median of Two Sorted Arrays/README_EN.md @@ -39,7 +39,24 @@ ## Solutions -Binary search. +**Solution 1: Divide and Conquer** + +The problem requires the time complexity of the algorithm to be $O(\log (m + n))$, so we cannot directly traverse the two arrays, but need to use the binary search method. + +If $m + n$ is odd, then the median is the $\left\lfloor\frac{m + n + 1}{2}\right\rfloor$-th number; if $m + n$ is even, then the median is the average of the $\left\lfloor\frac{m + n + 1}{2}\right\rfloor$-th and the $\left\lfloor\frac{m + n + 2}{2}\right\rfloor$-th numbers. In fact, we can unify it as the average of the $\left\lfloor\frac{m + n + 1}{2}\right\rfloor$-th and the $\left\lfloor\frac{m + n + 2}{2}\right\rfloor$-th numbers. + +Therefore, we can design a function $f(i, j, k)$, which represents the $k$-th smallest number in the interval $[i, m)$ of array $nums1$ and the interval $[j, n)$ of array $nums2$. The median is the average of $f(0, 0, \left\lfloor\frac{m + n + 1}{2}\right\rfloor)$ and $f(0, 0, \left\lfloor\frac{m + n + 2}{2}\right\rfloor)$. + +The implementation idea of the function $f(i, j, k)$ is as follows: + +- If $i \geq m$, it means that the interval $[i, m)$ of array $nums1$ is empty, so directly return $nums2[j + k - 1]$; +- If $j \geq n$, it means that the interval $[j, n)$ of array $nums2$ is empty, so directly return $nums1[i + k - 1]$; +- If $k = 1$, it means to find the first number, so just return the minimum of $nums1[i]$ and $nums2[j]$; +- Otherwise, we find the $\left\lfloor\frac{k}{2}\right\rfloor$-th number in the two arrays, denoted as $x$ and $y$. (Note, if a certain array does not have the $\left\lfloor\frac{k}{2}\right\rfloor$-th number, then we regard the $\left\lfloor\frac{k}{2}\right\rfloor$-th number as $+\infty$.) Compare the size of $x$ and $y$: + - If $x \leq y$, it means that the $\left\lfloor\frac{k}{2}\right\rfloor$-th number of array $nums1$ cannot be the $k$-th smallest number, so we can exclude the interval $[i, i + \left\lfloor\frac{k}{2}\right\rfloor)$ of array $nums1$, and recursively call $f(i + \left\lfloor\frac{k}{2}\right\rfloor, j, k - \left\lfloor\frac{k}{2}\right\rfloor)$. + - If $x > y$, it means that the $\left\lfloor\frac{k}{2}\right\rfloor$-th number of array $nums2$ cannot be the $k$-th smallest number, so we can exclude the interval $[j, j + \left\lfloor\frac{k}{2}\right\rfloor)$ of array $nums2$, and recursively call $f(i, j + \left\lfloor\frac{k}{2}\right\rfloor, k - \left\lfloor\frac{k}{2}\right\rfloor)$. + +The time complexity is $O(\log(m + n))$, and the space complexity is $O(\log(m + n))$. Here, $m$ and $n$ are the lengths of arrays $nums1$ and $nums2$ respectively. diff --git a/solution/0000-0099/0005.Longest Palindromic Substring/README_EN.md b/solution/0000-0099/0005.Longest Palindromic Substring/README_EN.md index 924ffdfb82e22..3cc788cd6b2c6 100644 --- a/solution/0000-0099/0005.Longest Palindromic Substring/README_EN.md +++ b/solution/0000-0099/0005.Longest Palindromic Substring/README_EN.md @@ -34,18 +34,21 @@ **Solution 1: Dynamic Programming** -Set $dp[i][j]$ to indicate whether the string $s[i..j]$ is a palindrome. +We define $f[i][j]$ to represent whether the string $s[i..j]$ is a palindrome, initially $f[i][j] = true$. -- When $j - i \lt 2$, that is, the string length is `2`, as long as $s[i] == s[j]$, then $dp[i][j]$ is `true`. -- When $j - i \ge 2$, there is $dp[i][j] = dp[i + 1][j - 1] \cap s[i] == s[j]$. +Next, we define variables $k$ and $mx$, where $k$ represents the starting position of the longest palindrome, and $mx$ represents the length of the longest palindrome. Initially, $k = 0$, $mx = 1$. -The time complexity is $O(n^2)$ and the space complexity is $O(n^2)$. Where $n$ is the length of the string $s$. +Considering $f[i][j]$, if $s[i] = s[j]$, then $f[i][j] = f[i + 1][j - 1]$; otherwise, $f[i][j] = false$. If $f[i][j] = true$ and $mx < j - i + 1$, then we update $k = i$, $mx = j - i + 1$. -**Solution 2: Enumerate the Palindrome Center** +Since $f[i][j]$ depends on $f[i + 1][j - 1]$, we need to ensure that $i + 1$ is before $j - 1$, so we need to enumerate $i$ from large to small, and enumerate $j$ from small to large. -We can enumerate the palindrome center, spread to both sides, and find the longest palindrome. +The time complexity is $O(n^2)$, and the space complexity is $O(n^2)$. Here, $n$ is the length of the string $s$. -The time complexity is $O(n^2)$ and the space complexity is $O(1)$. Where $n$ is the length of the string $s$. +**Solution 2: Enumerate Palindrome Midpoint** + +We can enumerate the midpoint of the palindrome, spread to both sides, and find the longest palindrome. + +The time complexity is $O(n^2)$, and the space complexity is $O(1)$. Here, $n$ is the length of the string $s$. diff --git a/solution/0000-0099/0006.Zigzag Conversion/README_EN.md b/solution/0000-0099/0006.Zigzag Conversion/README_EN.md index 87364a4700d3c..a41757336f86b 100644 --- a/solution/0000-0099/0006.Zigzag Conversion/README_EN.md +++ b/solution/0000-0099/0006.Zigzag Conversion/README_EN.md @@ -60,11 +60,11 @@ P I **Solution 1: Simulation** -We use a 2D array $g$ to simulate the process of the $Z$-shaped arrangement, where $g[i][j]$ represents the character in the $i$th row and the $j$th column. Initially, $i=0$, and we also define a direction variable $k$, initially $k=-1$, which means up. +We use a two-dimensional array $g$ to simulate the process of the $Z$-shape arrangement, where $g[i][j]$ represents the character at the $i$-th row and the $j$-th column. Initially, $i=0$, and we define a direction variable $k$, initially $k=-1$, indicating moving upwards. -We traverse the string $s$ from left to right. Each time we traverse a character $c$, we append it to $g[i]$. If at this time $i=0$ or $i=numRows-1$, it means that the current character is at the turning point of the $Z$-shaped arrangement. We reverse the value of $k$, that is, $k=-k$. Next, we update the value of $i$ to $i+k$, that is, move up or down one row. Continue to traverse the next character until the string $s$ is traversed. We return the string that all rows in $g$ are concatenated. +We traverse the string $s$ from left to right. Each time we traverse to a character $c$, we append it to $g[i]$. If $i=0$ or $i=numRows-1$ at this time, it means that the current character is at the turning point of the $Z$-shape arrangement, and we reverse the value of $k$, i.e., $k=-k$. Next, we update the value of $i$ to $i+k$, i.e., move up or down one row. Continue to traverse the next character until we have traversed the string $s$, and we return the string concatenated by all rows in $g$. -Time complexity $O(n)$, space complexity $O(n)$, where $n$ is the length of the string $s$. +The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the string $s$. diff --git a/solution/0000-0099/0007.Reverse Integer/README_EN.md b/solution/0000-0099/0007.Reverse Integer/README_EN.md index aa8413064f71a..b2f0e013b9a0c 100644 --- a/solution/0000-0099/0007.Reverse Integer/README_EN.md +++ b/solution/0000-0099/0007.Reverse Integer/README_EN.md @@ -39,27 +39,27 @@ ## Solutions -**Solution 1: Mathematical** +**Solution 1: Mathematics** -Let $mi$ and $mx$ be $-2^{31}$ and $2^{31} - 1$ respectively. The reversed result $ans$ needs to satisfy $mi \le ans \le mx$. +Let's denote $mi$ and $mx$ as $-2^{31}$ and $2^{31} - 1$ respectively, then the reverse result of $x$, $ans$, needs to satisfy $mi \le ans \le mx$. -We can get the last digit $y$ of $x$ by repeatedly taking the remainder of $x$ by $10$, and then add $y$ to the end of $ans$. Before adding $y$, we need to determine whether $ans$ will overflow. That is, whether $ans \times 10 + y$ is within the range $[mi, mx]$. +We can continuously take the remainder of $x$ to get the last digit $y$ of $x$, and add $y$ to the end of $ans$. Before adding $y$, we need to check if $ans$ overflows. That is, check whether $ans \times 10 + y$ is within the range $[mi, mx]$. -If $x \gt 0$, then $ans \times 10 + y \leq mx$ should be satisfied. That is, $ans \times 10 + y \leq \left \lfloor \frac{mx}{10} \right \rfloor \times 10 + 7$. Rearrange to get $(ans - \left \lfloor \frac{mx}{10} \right \rfloor) \times 10 \leq 7 - y$. +If $x \gt 0$, it needs to satisfy $ans \times 10 + y \leq mx$, that is, $ans \times 10 + y \leq \left \lfloor \frac{mx}{10} \right \rfloor \times 10 + 7$. Rearranging gives $(ans - \left \lfloor \frac{mx}{10} \right \rfloor) \times 10 \leq 7 - y$. -The following conditions need to be satisfied for the above inequality to hold: +Next, we discuss the conditions for the inequality to hold: - When $ans \lt \left \lfloor \frac{mx}{10} \right \rfloor$, the inequality obviously holds; -- When $ans = \left \lfloor \frac{mx}{10} \right \rfloor$, the necessary and sufficient conditions for the above inequality to hold are $y \leq 7$. If $ans = \left \lfloor \frac{mx}{10} \right \rfloor$ and can still continue to add digits, it means that the current digit is the most significant digit. Therefore, $y$ must be less than or equal to $2$, so the inequality must hold; +- When $ans = \left \lfloor \frac{mx}{10} \right \rfloor$, the necessary and sufficient condition for the inequality to hold is $y \leq 7$. If $ans = \left \lfloor \frac{mx}{10} \right \rfloor$ and we can still add numbers, it means that the number is at the highest digit, that is, $y$ must not exceed $2$, therefore, the inequality must hold; - When $ans \gt \left \lfloor \frac{mx}{10} \right \rfloor$, the inequality obviously does not hold. -Therefore, when $x \gt 0$, the necessary and sufficient conditions for the inequality to hold are $ans \leq \left \lfloor \frac{mx}{10} \right \rfloor$. +In summary, when $x \gt 0$, the necessary and sufficient condition for the inequality to hold is $ans \leq \left \lfloor \frac{mx}{10} \right \rfloor$. -Similarly, when $x \lt 0$, the necessary and sufficient conditions for the inequality to hold are $ans \geq \left \lfloor \frac{mi}{10} \right \rfloor$. +Similarly, when $x \lt 0$, the necessary and sufficient condition for the inequality to hold is $ans \geq \left \lfloor \frac{mi}{10} \right \rfloor$. -Therefore, we can determine whether $ans$ will overflow by determining whether $ans$ is within the range $[\left \lfloor \frac{mi}{10} \right \rfloor, \left \lfloor \frac{mx}{10} \right \rfloor]$. If so, return $0$. Otherwise, add $y$ to the end of $ans$, and then remove the last digit of $x$, that is, $x \gets \left \lfloor \frac{x}{10} \right \rfloor$. +Therefore, we can check whether $ans$ overflows by checking whether $ans$ is within the range $[\left \lfloor \frac{mi}{10} \right \rfloor, \left \lfloor \frac{mx}{10} \right \rfloor]$. If it overflows, return $0$. Otherwise, add $y$ to the end of $ans$, and then remove the last digit of $x$, that is, $x \gets \left \lfloor \frac{x}{10} \right \rfloor$. -Time complexity $O(\log |x|)$, where $|x|$ is the absolute value of $x$. Space complexity $O(1)$. +The time complexity is $O(\log |x|)$, where $|x|$ is the absolute value of $x$. The space complexity is $O(1)$. diff --git a/solution/0000-0099/0008.String to Integer (atoi)/README.md b/solution/0000-0099/0008.String to Integer (atoi)/README.md index 16206d80fa198..02bc35be907d9 100644 --- a/solution/0000-0099/0008.String to Integer (atoi)/README.md +++ b/solution/0000-0099/0008.String to Integer (atoi)/README.md @@ -88,7 +88,17 @@ -遍历字符串,注意做溢出处理。 +**方法一:遍历字符串** + +我们首先判断字符串是否为空,如果是,直接返回 $0$。 + +否则,我们需要遍历字符串,跳过前导空格,判断第一个非空格字符是否为正负号。 + +接着遍历后面的字符,如果是数字,我们判断添加该数字是否会导致整数溢出,如果会,我们根据正负号返回结果。否则我们将数字添加到结果中。继续遍历后面的字符,直到遇到非数字字符或者遍历结束。 + +遍历结束后,我们根据正负号返回结果。 + +时间复杂度 $O(n)$,其中 $n$ 为字符串的长度。我们只需要依次处理所有字符。空间复杂度 $O(1)$。 同[面试题 67. 把字符串转换成整数](/lcof/面试题67.%20把字符串转换成整数/README.md)。 diff --git a/solution/0000-0099/0008.String to Integer (atoi)/README_EN.md b/solution/0000-0099/0008.String to Integer (atoi)/README_EN.md index 65d893586cbbd..bac37815dfe01 100644 --- a/solution/0000-0099/0008.String to Integer (atoi)/README_EN.md +++ b/solution/0000-0099/0008.String to Integer (atoi)/README_EN.md @@ -83,6 +83,18 @@ Since 4193 is in the range [-231, 231 - 1], the final resu ## Solutions +**Solution 1: Traverse the String** + +First, we determine whether the string is empty. If it is, we directly return $0$. + +Otherwise, we need to traverse the string, skip the leading spaces, and determine whether the first non-space character is a positive or negative sign. + +Then we traverse the following characters. If it is a digit, we judge whether adding this digit will cause integer overflow. If it does, we return the result according to the positive or negative sign. Otherwise, we add the digit to the result. We continue to traverse the following characters until we encounter a non-digit character or the traversal ends. + +After the traversal ends, we return the result according to the positive or negative sign. + +The time complexity is $O(n)$, where $n$ is the length of the string. We only need to process all characters in turn. The space complexity is $O(1)$. + ### **Python3** diff --git a/solution/0000-0099/0009.Palindrome Number/README_EN.md b/solution/0000-0099/0009.Palindrome Number/README_EN.md index 65a99aa461cc8..9bb58afeb7918 100644 --- a/solution/0000-0099/0009.Palindrome Number/README_EN.md +++ b/solution/0000-0099/0009.Palindrome Number/README_EN.md @@ -43,6 +43,30 @@ ## Solutions +**Solution 1: Reverse Half of the Number** + +First, we determine special cases: + +- If $x < 0$, then $x$ is not a palindrome, directly return `false`; +- If $x > 0$ and the last digit of $x$ is $0$, then $x$ is not a palindrome, directly return `false`; +- If the last digit of $x$ is not $0$, then $x$ might be a palindrome, continue the following steps. + +We reverse the second half of $x$ and compare it with the first half. If they are equal, then $x$ is a palindrome, otherwise, $x$ is not a palindrome. + +For example, for $x = 1221$, we can reverse the second half from "21" to "12" and compare it with the first half "12". Since they are equal, we know that $x$ is a palindrome. + +Let's see how to reverse the second half. + +For the number $1221$, if we perform $1221 \bmod 10$, we will get the last digit $1$. To get the second last digit, we can first remove the last digit from $1221$ by dividing by $10$, $1221 / 10 = 122$, then get the remainder of the previous result divided by $10$, $122 \bmod 10 = 2$, to get the second last digit. + +If we continue this process, we will get more reversed digits. + +By continuously multiplying the last digit to the variable $y$, we can get the number in reverse order. + +In the code implementation, we can repeatedly "take out" the last digit of $x$ and "add" it to the end of $y$, loop until $y \ge x$. If at this time $x = y$, or $x = y / 10$, then $x$ is a palindrome. + +The time complexity is $O(\log_{10}(n))$, where $n$ is $x$. For each iteration, we will divide the input by $10$, so the time complexity is $O(\log_{10}(n))$. The space complexity is $O(1)$. + ### **Python3** diff --git a/solution/0000-0099/0010.Regular Expression Matching/README_EN.md b/solution/0000-0099/0010.Regular Expression Matching/README_EN.md index c007c08cbff55..ddc4cc808ba14 100644 --- a/solution/0000-0099/0010.Regular Expression Matching/README_EN.md +++ b/solution/0000-0099/0010.Regular Expression Matching/README_EN.md @@ -51,6 +51,33 @@ ## Solutions +**Solution 1: Memoization Search** + +We design a function $dfs(i, j)$, which indicates whether the $i$-th character of $s$ matches the $j$-th character of $p$. The answer is $dfs(0, 0)$. + +The calculation process of the function $dfs(i, j)$ is as follows: + +- If $j$ has reached the end of $p$, then if $i$ has also reached the end of $s$, the match is successful, otherwise, the match fails. +- If the next character of $j$ is `'*'`, we can choose to match $0$ $s[i]$ characters, which is $dfs(i, j + 2)$. If $i \lt m$ and $s[i]$ matches $p[j]$, we can choose to match $1$ $s[i]$ character, which is $dfs(i + 1, j)$. +- If the next character of $j$ is not `'*'`, then if $i \lt m$ and $s[i]$ matches $p[j]$, it is $dfs(i + 1, j + 1)$. Otherwise, the match fails. + +During the process, we can use memoization search to avoid repeated calculations. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. Here, $m$ and $n$ are the lengths of $s$ and $p$ respectively. + +**Solution 2: Dynamic Programming** + +We can convert the memoization search in Solution 1 into dynamic programming. + +Define $f[i][j]$ to represent whether the first $i$ characters of string $s$ match the first $j$ characters of string $p$. The answer is $f[m][n]$. Initialize $f[0][0] = true$, indicating that the empty string and the empty regular expression match. + +Similar to Solution 1, we can discuss different cases. + +- If $p[j - 1]$ is `'*'`, we can choose to match $0$ $s[i - 1]$ characters, which is $f[i][j] = f[i][j - 2]$. If $s[i - 1]$ matches $p[j - 2]$, we can choose to match $1$ $s[i - 1]$ character, which is $f[i][j] = f[i][j] \lor f[i - 1][j]$. +- If $p[j - 1]$ is not `'*'`, then if $s[i - 1]$ matches $p[j - 1]$, it is $f[i][j] = f[i - 1][j - 1]$. Otherwise, the match fails. + +The time complexity is $O(m \times n)$, and the space complexity is $O(m \times n)$. Here, $m$ and $n$ are the lengths of $s$ and $p$ respectively. + ### **Python3** diff --git a/solution/0000-0099/0011.Container With Most Water/README_EN.md b/solution/0000-0099/0011.Container With Most Water/README_EN.md index 2f778ed5afbea..da79683de3164 100644 --- a/solution/0000-0099/0011.Container With Most Water/README_EN.md +++ b/solution/0000-0099/0011.Container With Most Water/README_EN.md @@ -39,6 +39,16 @@ ## Solutions +**Solution 1: Two Pointers** + +Initially, we consider the capacity of the water that the two farthest pillars can hold. The width of the water is the distance between the two pillars, and the height of the water depends on the shorter one between the two pillars. + +The current pillars are the pillars on the farthest sides, so the width of the water is the largest. For other combinations, the width of the water is smaller. Suppose the height of the left pillar is less than or equal to the height of the right pillar, then the height of the water is the height of the left pillar. If we move the right pillar, the width of the water will decrease, but the height of the water will not increase, so the capacity of the water will definitely decrease. Therefore, we move the left pillar and update the maximum capacity. + +Repeat this process until the two pillars meet. + +The time complexity is $O(n)$, where $n$ is the length of the array `height`. The space complexity is $O(1)$. + ### **Python3** diff --git a/solution/0000-0099/0012.Integer to Roman/README_EN.md b/solution/0000-0099/0012.Integer to Roman/README_EN.md index 65b28ff579e7e..596f3c5329149 100644 --- a/solution/0000-0099/0012.Integer to Roman/README_EN.md +++ b/solution/0000-0099/0012.Integer to Roman/README_EN.md @@ -64,9 +64,9 @@ M 1000 **Solution 1: Greedy** -We can list all possible symbols $cs$ and corresponding values $vs$ first, then enumerate the value $vs[i]$ from large to small, and use the symbol $cs[i]$ as much as possible each time until the number $num$ becomes $0$. +We can first list all possible symbols $cs$ and their corresponding values $vs$, then enumerate each value $vs[i]$ from large to small. Each time, we use as many symbols $cs[i]$ corresponding to this value as possible, until the number $num$ becomes $0$. -The time complexity is $O(m)$ and the space complexity is $O(m)$, where $m$ is the number of symbols. +The time complexity is $O(m)$, and the space complexity is $O(m)$. Here, $m$ is the number of symbols. diff --git a/solution/0000-0099/0013.Roman to Integer/README_EN.md b/solution/0000-0099/0013.Roman to Integer/README_EN.md index 243e3deda144f..f5f07c0a3d69a 100644 --- a/solution/0000-0099/0013.Roman to Integer/README_EN.md +++ b/solution/0000-0099/0013.Roman to Integer/README_EN.md @@ -64,11 +64,11 @@ M 1000 ## Solutions -**Solution 1: Hash table + simulation** +**Solution 1: Hash Table + Simulation** -We first use a hash table $d$ to record the numerical value corresponding to each character, and then traverse the string $s$ from left to right. If the numerical value corresponding to the current character is less than the numerical value corresponding to the right character, subtract the numerical value corresponding to the current character, otherwise add the numerical value corresponding to the current character. +First, we use a hash table $d$ to record the numerical value corresponding to each character. Then, we traverse the string $s$ from left to right. If the numerical value corresponding to the current character is less than the numerical value corresponding to the character on the right, we subtract the numerical value corresponding to the current character. Otherwise, we add the numerical value corresponding to the current character. -The time complexity is $O(n)$ and the space complexity is $O(m)$. Where $n$ and $m$ are the lengths of the string $s$ and the size of the character set, respectively. +The time complexity is $O(n)$, and the space complexity is $O(m)$. Here, $n$ and $m$ are the length of the string $s$ and the size of the character set, respectively. diff --git a/solution/0000-0099/0014.Longest Common Prefix/README_EN.md b/solution/0000-0099/0014.Longest Common Prefix/README_EN.md index d8f81dce4fd45..5590f153d672e 100644 --- a/solution/0000-0099/0014.Longest Common Prefix/README_EN.md +++ b/solution/0000-0099/0014.Longest Common Prefix/README_EN.md @@ -37,11 +37,11 @@ **Solution 1: Character Comparison** -We take the first string $strs[0]$ as the benchmark, and compare the $i$th character of the string after it with the $i$th character of $strs[0]$. If it is the same, continue to compare the next character, otherwise return the first $i$ characters of $strs[0]$. +We use the first string $strs[0]$ as a benchmark, and compare whether the $i$-th character of the subsequent strings is the same as the $i$-th character of $strs[0]$. If they are the same, we continue to compare the next character. Otherwise, we return the first $i$ characters of $strs[0]$. -After the traversal is over, it means that the first $i$ characters of all strings are the same, and $strs[0]$ is returned. +If the traversal ends, it means that the first $i$ characters of all strings are the same, and we return $strs[0]$. -Time complexity $(n \times m)$, where $n$ and $m$ are the length of the string array and the minimum length of the string respectively. The space complexity is $O(1)$. +The time complexity is $O(n \times m)$, where $n$ and $m$ are the length of the string array and the minimum length of the strings, respectively. The space complexity is $O(1)$. diff --git a/solution/0000-0099/0016.3Sum Closest/README_EN.md b/solution/0000-0099/0016.3Sum Closest/README_EN.md index e16474ce284aa..55514a8825cbb 100644 --- a/solution/0000-0099/0016.3Sum Closest/README_EN.md +++ b/solution/0000-0099/0016.3Sum Closest/README_EN.md @@ -38,6 +38,12 @@ ## Solutions +**Solution 1: Sorting + Two Pointers** + +We sort the array first, then traverse the array. For each element $nums[i]$, we use pointers $j$ and $k$ to point to $i+1$ and $n-1$ respectively, calculate the sum of the three numbers. If the sum of the three numbers equals $target$, we directly return $target$. Otherwise, we update the answer based on the difference from $target$. If the sum of the three numbers is greater than $target$, we move $k$ one place to the left, otherwise, we move $j$ one place to the right. + +The time complexity is $O(n^2)$, and the space complexity is $O(\log n)$. Here, $n$ is the length of the array. + ### **Python3** diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/README.md b/solution/0000-0099/0017.Letter Combinations of a Phone Number/README.md index 7f35c1ce3ffe7..6cfc247979a3f 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/README.md +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/README.md @@ -52,7 +52,13 @@ 我们先用一个数组或者哈希表存储每个数字对应的字母,然后遍历每个数字,将其对应的字母与之前的结果进行组合,得到新的结果。 -时间复杂度 $O(4^n)$。其中 $n$ 是输入数字的长度。 +时间复杂度 $O(4^n)$。空间复杂度 $O(4^n)$。其中 $n$ 是输入数字的长度。 + +**方法二:DFS** + +我们可以使用深度优先搜索的方法,枚举所有可能的字母组合。假设当前已经产生了一部分字母组合,但是还有一些数字没有被穷举到,此时我们取出下一个数字所对应的字母,然后依次枚举这个数字所对应的每一个字母,将它们添加到前面已经产生的字母组合后面,形成所有可能的组合。 + +时间复杂度 $O(4^n)$。空间复杂度 $O(n)$。其中 $n$ 是输入数字的长度。 @@ -65,14 +71,35 @@ class Solution: def letterCombinations(self, digits: str) -> List[str]: if not digits: return [] - d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'] - ans = [''] + d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] + ans = [""] for i in digits: s = d[int(i) - 2] ans = [a + b for a in ans for b in s] return ans ``` +```python +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + def dfs(i: int): + if i >= len(digits): + ans.append("".join(t)) + return + for c in d[int(digits[i]) - 2]: + t.append(c) + dfs(i + 1) + t.pop() + + if not digits: + return [] + d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] + ans = [] + t = [] + dfs(0) + return ans +``` + ### **Java** @@ -101,16 +128,49 @@ class Solution { } ``` +```java +class Solution { + private final String[] d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + private String digits; + private List ans = new ArrayList<>(); + private StringBuilder t = new StringBuilder(); + + public List letterCombinations(String digits) { + if (digits.length() == 0) { + return ans; + } + this.digits = digits; + dfs(0); + return ans; + } + + private void dfs(int i) { + if (i >= digits.length()) { + ans.add(t.toString()); + return; + } + String s = d[digits.charAt(i) - '2']; + for (char c : s.toCharArray()) { + t.append(c); + dfs(i + 1); + t.deleteCharAt(t.length() - 1); + } + } +} +``` + ### **C++** ```cpp class Solution { public: vector letterCombinations(string digits) { - if (digits.empty()) return {}; + if (digits.empty()) { + return {}; + } vector d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; vector ans = {""}; - for (auto i : digits) { + for (auto& i : digits) { string s = d[i - '2']; vector t; for (auto& a : ans) { @@ -125,6 +185,33 @@ public: }; ``` +```cpp +class Solution { +public: + vector letterCombinations(string digits) { + if (digits.empty()) { + return {}; + } + vector d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + vector ans; + string t; + function dfs = [&](int i) { + if (i >= digits.size()) { + ans.push_back(t); + return; + } + for (auto& c : d[digits[i] - '2']) { + t.push_back(c); + dfs(i + 1); + t.pop_back(); + } + }; + dfs(0); + return ans; + } +}; +``` + ### **Go** ```go @@ -149,6 +236,30 @@ func letterCombinations(digits string) []string { } ``` +```go +func letterCombinations(digits string) (ans []string) { + d := []string{"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"} + t := []rune{} + var dfs func(int) + dfs = func(i int) { + if i >= len(digits) { + ans = append(ans, string(t)) + return + } + for _, c := range d[digits[i]-'2'] { + t = append(t, c) + dfs(i + 1) + t = t[:len(t)-1] + } + } + if len(digits) == 0 { + return + } + dfs(0) + return +} +``` + ### **JavaScript** ```js @@ -160,7 +271,7 @@ var letterCombinations = function (digits) { if (digits.length == 0) { return []; } - let ans = ['']; + const ans = ['']; const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; for (const i of digits) { const s = d[parseInt(i) - 2]; @@ -170,8 +281,37 @@ var letterCombinations = function (digits) { t.push(a + b); } } - ans = t; + ans.splice(0, ans.length, ...t); + } + return ans; +}; +``` + +```js +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function (digits) { + if (digits.length == 0) { + return []; } + const ans = []; + const t = []; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + const dfs = i => { + if (i >= digits.length) { + ans.push(t.join('')); + return; + } + const s = d[parseInt(digits[i]) - 2]; + for (const c of s) { + t.push(c); + dfs(i + 1); + t.pop(); + } + }; + dfs(0); return ans; }; ``` @@ -202,151 +342,135 @@ public class Solution { } ``` +```cs +public class Solution { + private readonly string[] d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + private string digits; + private List ans = new List(); + private System.Text.StringBuilder t = new System.Text.StringBuilder(); + + public IList LetterCombinations(string digits) { + if (digits.Length == 0) { + return ans; + } + this.digits = digits; + Dfs(0); + return ans; + } + + private void Dfs(int i) { + if (i >= digits.Length) { + ans.Add(t.ToString()); + return; + } + string s = d[digits[i] - '2']; + foreach (char c in s) { + t.Append(c); + Dfs(i + 1); + t.Remove(t.Length - 1, 1); + } + } +} +``` + ### **TypeScript** ```ts -const map = { - '2': ['a', 'b', 'c'], - '3': ['d', 'e', 'f'], - '4': ['g', 'h', 'i'], - '5': ['j', 'k', 'l'], - '6': ['m', 'n', 'o'], - '7': ['p', 'q', 'r', 's'], - '8': ['t', 'u', 'v'], - '9': ['w', 'x', 'y', 'z'], -}; - function letterCombinations(digits: string): string[] { - const n = digits.length; - if (n === 0) { + if (digits.length == 0) { return []; } - const res = []; - const dfs = (i: number, str: string) => { - if (i === n) { - res.push(str); - return; - } - for (const c of map[digits[i]]) { - dfs(i + 1, str + c); + const ans: string[] = ['']; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + for (const i of digits) { + const s = d[parseInt(i) - 2]; + const t: string[] = []; + for (const a of ans) { + for (const b of s) { + t.push(a + b); + } } - }; - dfs(0, ''); - return res; + ans.splice(0, ans.length, ...t); + } + return ans; } ``` ```ts -const map = { - '2': ['a', 'b', 'c'], - '3': ['d', 'e', 'f'], - '4': ['g', 'h', 'i'], - '5': ['j', 'k', 'l'], - '6': ['m', 'n', 'o'], - '7': ['p', 'q', 'r', 's'], - '8': ['t', 'u', 'v'], - '9': ['w', 'x', 'y', 'z'], -}; - function letterCombinations(digits: string): string[] { - const n = digits.length; - if (n === 0) { + if (digits.length == 0) { return []; } - const dfs = (i: number, ss: string[]) => { - if (i === n) { - return ss; + const ans: string[] = []; + const t: string[] = []; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + const dfs = (i: number) => { + if (i >= digits.length) { + ans.push(t.join('')); + return; } - const t = []; - for (const c of map[digits[i]]) { - for (const s of ss) { - t.push(s + c); - } + const s = d[parseInt(digits[i]) - 2]; + for (const c of s) { + t.push(c); + dfs(i + 1); + t.pop(); } - return dfs(i + 1, t); }; - return dfs(1, map[digits[0]]); + dfs(0); + return ans; } ``` ### **Rust** ```rust -use std::collections::HashMap; - impl Solution { - fn dfs( - i: usize, - s: &mut String, - cs: &Vec, - map: &HashMap, - res: &mut Vec - ) { - if i == cs.len() { - res.push(s.clone()); - return; - } - for c in map.get(&cs[i]).unwrap().chars() { - s.push(c); - Self::dfs(i + 1, s, cs, map, res); - s.pop(); - } - } - pub fn letter_combinations(digits: String) -> Vec { - let mut res = vec![]; + let mut ans: Vec = Vec::new(); if digits.is_empty() { - return res; + return ans; } - - let mut map = HashMap::new(); - map.insert('2', String::from("abc")); - map.insert('3', String::from("def")); - map.insert('4', String::from("ghi")); - map.insert('5', String::from("jkl")); - map.insert('6', String::from("mno")); - map.insert('7', String::from("pqrs")); - map.insert('8', String::from("tuv")); - map.insert('9', String::from("wxyz")); - - Self::dfs(0, &mut String::new(), &digits.chars().collect(), &map, &mut res); - res + ans.push("".to_string()); + let d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]; + for i in digits.chars() { + let s = &d[((i as u8) - b'2') as usize]; + let mut t: Vec = Vec::new(); + for a in &ans { + for b in s.chars() { + t.push(format!("{}{}", a, b)); + } + } + ans = t; + } + ans } } ``` ```rust impl Solution { - fn dfs(i: usize, digits: &[u8], map: &Vec>, s: &mut String, res: &mut Vec) { - if i == digits.len() { - res.push(s.clone()); - return; - } - for c in map[(digits[i] - b'2') as usize].iter() { - s.push(*c); - Self::dfs(i + 1, digits, map, s, res); - s.pop(); + pub fn letter_combinations(digits: String) -> Vec { + let d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]; + let mut ans = Vec::new(); + let mut t = String::new(); + if digits.is_empty() { + return ans; } + Solution::dfs(&digits, &d, &mut t, &mut ans, 0); + ans } - pub fn letter_combinations(digits: String) -> Vec { - if digits.is_empty() { - return Vec::new(); + fn dfs(digits: &String, d: &[&str; 8], t: &mut String, ans: &mut Vec, i: usize) { + if i >= digits.len() { + ans.push(t.clone()); + return; + } + let s = d[((digits.chars().nth(i).unwrap() as u8) - b'2') as usize]; + for c in s.chars() { + t.push(c); + Solution::dfs(digits, d, t, ans, i + 1); + t.pop(); } - let digits = digits.as_bytes(); - let map = vec![ - vec!['a', 'b', 'c'], - vec!['d', 'e', 'f'], - vec!['g', 'h', 'i'], - vec!['j', 'k', 'l'], - vec!['m', 'n', 'o'], - vec!['p', 'q', 'r', 's'], - vec!['t', 'u', 'v'], - vec!['w', 'x', 'y', 'z'] - ]; - let mut res = Vec::new(); - Self::dfs(0, digits, &map, &mut String::new(), &mut res); - res } } ``` diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/README_EN.md b/solution/0000-0099/0017.Letter Combinations of a Phone Number/README_EN.md index 5d9f013595969..f9e0e6d06f71f 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/README_EN.md +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/README_EN.md @@ -40,6 +40,18 @@ ## Solutions +**Solution 1: Traversal** + +First, we use an array or hash table to store the letters corresponding to each digit. Then we traverse each digit, combine its corresponding letters with the previous results to get the new results. + +The time complexity is $O(4^n)$, and the space complexity is $O(4^n)$. Here, $n$ is the length of the input digits. + +**Solution 2: DFS** + +We can use the method of depth-first search to enumerate all possible letter combinations. Suppose that a part of the letter combination has been generated, but some digits have not been exhausted. At this time, we take out the letters corresponding to the next digit, and then enumerate each letter corresponding to this digit one by one, add them to the letter combination that has been generated before, to form all possible combinations. + +The time complexity is $O(4^n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the input digits. + ### **Python3** @@ -49,14 +61,35 @@ class Solution: def letterCombinations(self, digits: str) -> List[str]: if not digits: return [] - d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'] - ans = [''] + d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] + ans = [""] for i in digits: s = d[int(i) - 2] ans = [a + b for a in ans for b in s] return ans ``` +```python +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + def dfs(i: int): + if i >= len(digits): + ans.append("".join(t)) + return + for c in d[int(digits[i]) - 2]: + t.append(c) + dfs(i + 1) + t.pop() + + if not digits: + return [] + d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] + ans = [] + t = [] + dfs(0) + return ans +``` + ### **Java** ```java @@ -83,16 +116,49 @@ class Solution { } ``` +```java +class Solution { + private final String[] d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + private String digits; + private List ans = new ArrayList<>(); + private StringBuilder t = new StringBuilder(); + + public List letterCombinations(String digits) { + if (digits.length() == 0) { + return ans; + } + this.digits = digits; + dfs(0); + return ans; + } + + private void dfs(int i) { + if (i >= digits.length()) { + ans.add(t.toString()); + return; + } + String s = d[digits.charAt(i) - '2']; + for (char c : s.toCharArray()) { + t.append(c); + dfs(i + 1); + t.deleteCharAt(t.length() - 1); + } + } +} +``` + ### **C++** ```cpp class Solution { public: vector letterCombinations(string digits) { - if (digits.empty()) return {}; + if (digits.empty()) { + return {}; + } vector d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; vector ans = {""}; - for (auto i : digits) { + for (auto& i : digits) { string s = d[i - '2']; vector t; for (auto& a : ans) { @@ -107,6 +173,33 @@ public: }; ``` +```cpp +class Solution { +public: + vector letterCombinations(string digits) { + if (digits.empty()) { + return {}; + } + vector d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + vector ans; + string t; + function dfs = [&](int i) { + if (i >= digits.size()) { + ans.push_back(t); + return; + } + for (auto& c : d[digits[i] - '2']) { + t.push_back(c); + dfs(i + 1); + t.pop_back(); + } + }; + dfs(0); + return ans; + } +}; +``` + ### **Go** ```go @@ -131,6 +224,30 @@ func letterCombinations(digits string) []string { } ``` +```go +func letterCombinations(digits string) (ans []string) { + d := []string{"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"} + t := []rune{} + var dfs func(int) + dfs = func(i int) { + if i >= len(digits) { + ans = append(ans, string(t)) + return + } + for _, c := range d[digits[i]-'2'] { + t = append(t, c) + dfs(i + 1) + t = t[:len(t)-1] + } + } + if len(digits) == 0 { + return + } + dfs(0) + return +} +``` + ### **JavaScript** ```js @@ -142,7 +259,7 @@ var letterCombinations = function (digits) { if (digits.length == 0) { return []; } - let ans = ['']; + const ans = ['']; const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; for (const i of digits) { const s = d[parseInt(i) - 2]; @@ -152,12 +269,41 @@ var letterCombinations = function (digits) { t.push(a + b); } } - ans = t; + ans.splice(0, ans.length, ...t); } return ans; }; ``` +```js +/** + * @param {string} digits + * @return {string[]} + */ +var letterCombinations = function (digits) { + if (digits.length == 0) { + return []; + } + const ans = []; + const t = []; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + const dfs = i => { + if (i >= digits.length) { + ans.push(t.join('')); + return; + } + const s = d[parseInt(digits[i]) - 2]; + for (const c of s) { + t.push(c); + dfs(i + 1); + t.pop(); + } + }; + dfs(0); + return ans; +}; +``` + ### **C#** ```cs @@ -184,151 +330,135 @@ public class Solution { } ``` +```cs +public class Solution { + private readonly string[] d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + private string digits; + private List ans = new List(); + private System.Text.StringBuilder t = new System.Text.StringBuilder(); + + public IList LetterCombinations(string digits) { + if (digits.Length == 0) { + return ans; + } + this.digits = digits; + Dfs(0); + return ans; + } + + private void Dfs(int i) { + if (i >= digits.Length) { + ans.Add(t.ToString()); + return; + } + string s = d[digits[i] - '2']; + foreach (char c in s) { + t.Append(c); + Dfs(i + 1); + t.Remove(t.Length - 1, 1); + } + } +} +``` + ### **TypeScript** ```ts -const map = { - '2': ['a', 'b', 'c'], - '3': ['d', 'e', 'f'], - '4': ['g', 'h', 'i'], - '5': ['j', 'k', 'l'], - '6': ['m', 'n', 'o'], - '7': ['p', 'q', 'r', 's'], - '8': ['t', 'u', 'v'], - '9': ['w', 'x', 'y', 'z'], -}; - function letterCombinations(digits: string): string[] { - const n = digits.length; - if (n === 0) { + if (digits.length == 0) { return []; } - const res = []; - const dfs = (i: number, str: string) => { - if (i === n) { - res.push(str); - return; - } - for (const c of map[digits[i]]) { - dfs(i + 1, str + c); + const ans: string[] = ['']; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + for (const i of digits) { + const s = d[parseInt(i) - 2]; + const t: string[] = []; + for (const a of ans) { + for (const b of s) { + t.push(a + b); + } } - }; - dfs(0, ''); - return res; + ans.splice(0, ans.length, ...t); + } + return ans; } ``` ```ts -const map = { - '2': ['a', 'b', 'c'], - '3': ['d', 'e', 'f'], - '4': ['g', 'h', 'i'], - '5': ['j', 'k', 'l'], - '6': ['m', 'n', 'o'], - '7': ['p', 'q', 'r', 's'], - '8': ['t', 'u', 'v'], - '9': ['w', 'x', 'y', 'z'], -}; - function letterCombinations(digits: string): string[] { - const n = digits.length; - if (n === 0) { + if (digits.length == 0) { return []; } - const dfs = (i: number, ss: string[]) => { - if (i === n) { - return ss; + const ans: string[] = []; + const t: string[] = []; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + const dfs = (i: number) => { + if (i >= digits.length) { + ans.push(t.join('')); + return; } - const t = []; - for (const c of map[digits[i]]) { - for (const s of ss) { - t.push(s + c); - } + const s = d[parseInt(digits[i]) - 2]; + for (const c of s) { + t.push(c); + dfs(i + 1); + t.pop(); } - return dfs(i + 1, t); }; - return dfs(1, map[digits[0]]); + dfs(0); + return ans; } ``` ### **Rust** ```rust -use std::collections::HashMap; - impl Solution { - fn dfs( - i: usize, - s: &mut String, - cs: &Vec, - map: &HashMap, - res: &mut Vec - ) { - if i == cs.len() { - res.push(s.clone()); - return; - } - for c in map.get(&cs[i]).unwrap().chars() { - s.push(c); - Self::dfs(i + 1, s, cs, map, res); - s.pop(); - } - } - pub fn letter_combinations(digits: String) -> Vec { - let mut res = vec![]; + let mut ans: Vec = Vec::new(); if digits.is_empty() { - return res; + return ans; } - - let mut map = HashMap::new(); - map.insert('2', String::from("abc")); - map.insert('3', String::from("def")); - map.insert('4', String::from("ghi")); - map.insert('5', String::from("jkl")); - map.insert('6', String::from("mno")); - map.insert('7', String::from("pqrs")); - map.insert('8', String::from("tuv")); - map.insert('9', String::from("wxyz")); - - Self::dfs(0, &mut String::new(), &digits.chars().collect(), &map, &mut res); - res + ans.push("".to_string()); + let d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]; + for i in digits.chars() { + let s = &d[((i as u8) - b'2') as usize]; + let mut t: Vec = Vec::new(); + for a in &ans { + for b in s.chars() { + t.push(format!("{}{}", a, b)); + } + } + ans = t; + } + ans } } ``` ```rust impl Solution { - fn dfs(i: usize, digits: &[u8], map: &Vec>, s: &mut String, res: &mut Vec) { - if i == digits.len() { - res.push(s.clone()); - return; - } - for c in map[(digits[i] - b'2') as usize].iter() { - s.push(*c); - Self::dfs(i + 1, digits, map, s, res); - s.pop(); + pub fn letter_combinations(digits: String) -> Vec { + let d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]; + let mut ans = Vec::new(); + let mut t = String::new(); + if digits.is_empty() { + return ans; } + Solution::dfs(&digits, &d, &mut t, &mut ans, 0); + ans } - pub fn letter_combinations(digits: String) -> Vec { - if digits.is_empty() { - return Vec::new(); + fn dfs(digits: &String, d: &[&str; 8], t: &mut String, ans: &mut Vec, i: usize) { + if i >= digits.len() { + ans.push(t.clone()); + return; + } + let s = d[((digits.chars().nth(i).unwrap() as u8) - b'2') as usize]; + for c in s.chars() { + t.push(c); + Solution::dfs(digits, d, t, ans, i + 1); + t.pop(); } - let digits = digits.as_bytes(); - let map = vec![ - vec!['a', 'b', 'c'], - vec!['d', 'e', 'f'], - vec!['g', 'h', 'i'], - vec!['j', 'k', 'l'], - vec!['m', 'n', 'o'], - vec!['p', 'q', 'r', 's'], - vec!['t', 'u', 'v'], - vec!['w', 'x', 'y', 'z'] - ]; - let mut res = Vec::new(); - Self::dfs(0, digits, &map, &mut String::new(), &mut res); - res } } ``` diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.cpp b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.cpp index b44423eb2c497..5be1938df3fb3 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.cpp +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.cpp @@ -1,19 +1,21 @@ -class Solution { -public: - vector letterCombinations(string digits) { - if (digits.empty()) return {}; - vector d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; - vector ans = {""}; - for (auto i : digits) { - string s = d[i - '2']; - vector t; - for (auto& a : ans) { - for (auto& b : s) { - t.push_back(a + b); - } - } - ans = move(t); - } - return ans; - } +class Solution { +public: + vector letterCombinations(string digits) { + if (digits.empty()) { + return {}; + } + vector d = {"abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; + vector ans = {""}; + for (auto& i : digits) { + string s = d[i - '2']; + vector t; + for (auto& a : ans) { + for (auto& b : s) { + t.push_back(a + b); + } + } + ans = move(t); + } + return ans; + } }; \ No newline at end of file diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.js b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.js index 445729676ac42..6b35a1b0bbf0b 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.js +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.js @@ -6,7 +6,7 @@ var letterCombinations = function (digits) { if (digits.length == 0) { return []; } - let ans = ['']; + const ans = ['']; const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; for (const i of digits) { const s = d[parseInt(i) - 2]; @@ -16,7 +16,7 @@ var letterCombinations = function (digits) { t.push(a + b); } } - ans = t; + ans.splice(0, ans.length, ...t); } return ans; }; diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.py b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.py index d5b1a5a34425a..d76248076ff54 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.py +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.py @@ -1,10 +1,10 @@ -class Solution: - def letterCombinations(self, digits: str) -> List[str]: - if not digits: - return [] - d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz'] - ans = [''] - for i in digits: - s = d[int(i) - 2] - ans = [a + b for a in ans for b in s] - return ans +class Solution: + def letterCombinations(self, digits: str) -> List[str]: + if not digits: + return [] + d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"] + ans = [""] + for i in digits: + s = d[int(i) - 2] + ans = [a + b for a in ans for b in s] + return ans diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.rs b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.rs index 1c8221c075606..d99172d9feb59 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.rs +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.rs @@ -1,33 +1,21 @@ impl Solution { - fn dfs(i: usize, digits: &[u8], map: &Vec>, s: &mut String, res: &mut Vec) { - if i == digits.len() { - res.push(s.clone()); - return; - } - for c in map[(digits[i] - b'2') as usize].iter() { - s.push(*c); - Self::dfs(i + 1, digits, map, s, res); - s.pop(); - } - } - pub fn letter_combinations(digits: String) -> Vec { + let mut ans: Vec = Vec::new(); if digits.is_empty() { - return Vec::new(); + return ans; + } + ans.push("".to_string()); + let d = ["abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"]; + for i in digits.chars() { + let s = &d[((i as u8) - b'2') as usize]; + let mut t: Vec = Vec::new(); + for a in &ans { + for b in s.chars() { + t.push(format!("{}{}", a, b)); + } + } + ans = t; } - let digits = digits.as_bytes(); - let map = vec![ - vec!['a', 'b', 'c'], - vec!['d', 'e', 'f'], - vec!['g', 'h', 'i'], - vec!['j', 'k', 'l'], - vec!['m', 'n', 'o'], - vec!['p', 'q', 'r', 's'], - vec!['t', 'u', 'v'], - vec!['w', 'x', 'y', 'z'] - ]; - let mut res = Vec::new(); - Self::dfs(0, digits, &map, &mut String::new(), &mut res); - res + ans } } diff --git a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.ts b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.ts index 9f7b4a9ef0dee..e879eb6a5972d 100644 --- a/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.ts +++ b/solution/0000-0099/0017.Letter Combinations of a Phone Number/Solution.ts @@ -1,29 +1,18 @@ -const map = { - '2': ['a', 'b', 'c'], - '3': ['d', 'e', 'f'], - '4': ['g', 'h', 'i'], - '5': ['j', 'k', 'l'], - '6': ['m', 'n', 'o'], - '7': ['p', 'q', 'r', 's'], - '8': ['t', 'u', 'v'], - '9': ['w', 'x', 'y', 'z'], -}; - function letterCombinations(digits: string): string[] { - const n = digits.length; - if (n === 0) { + if (digits.length == 0) { return []; } - const res = []; - const dfs = (i: number, str: string) => { - if (i === n) { - res.push(str); - return; + const ans: string[] = ['']; + const d = ['abc', 'def', 'ghi', 'jkl', 'mno', 'pqrs', 'tuv', 'wxyz']; + for (const i of digits) { + const s = d[parseInt(i) - 2]; + const t: string[] = []; + for (const a of ans) { + for (const b of s) { + t.push(a + b); + } } - for (const c of map[digits[i]]) { - dfs(i + 1, str + c); - } - }; - dfs(0, ''); - return res; + ans.splice(0, ans.length, ...t); + } + return ans; } diff --git a/solution/0000-0099/0018.4Sum/README_EN.md b/solution/0000-0099/0018.4Sum/README_EN.md index a8c5ed0f3ab4c..a8d7491cd79d3 100644 --- a/solution/0000-0099/0018.4Sum/README_EN.md +++ b/solution/0000-0099/0018.4Sum/README_EN.md @@ -40,9 +40,17 @@ ## Solutions -**Solution 1: Two Pointers** +**Solution 1: Sorting + Double Pointers** -Time complexity $O(n^3)$, Space complexity $O(\log n)$. +We notice that the problem requires us to find non-repeating quadruplets. Therefore, we can first sort the array, which makes it easy to skip duplicate elements. + +Next, we enumerate the first two elements of the quadruplet, $nums[i]$ and $nums[j]$, where $i \lt j$. During the enumeration process, we skip duplicate $nums[i]$ and $nums[j]$. Then, we use two pointers $k$ and $l$ to point to the two ends behind $nums[i]$ and $nums[j]$. Let $x = nums[i] + nums[j] + nums[k] + nums[l]$, we compare $x$ with $target$ and perform the following operations: + +- If $x \lt target$, then update $k = k + 1$ to get a larger $x$; +- If $x \gt target$, then update $l = l - 1$ to get a smaller $x$; +- Otherwise, it means that a quadruplet $(nums[i], nums[j], nums[k], nums[l])$ is found. Add it to the answer, then we update the pointers $k$ and $l$, and skip all duplicate elements to prevent the answer from containing duplicate quadruplets, and continue to find the next quadruplet. + +The time complexity is $O(n^3)$, and the space complexity is $O(\log n)$. Here, $n$ is the length of the array. diff --git a/solution/0000-0099/0019.Remove Nth Node From End of List/README.md b/solution/0000-0099/0019.Remove Nth Node From End of List/README.md index b2cd12854a039..61a5915c1970d 100644 --- a/solution/0000-0099/0019.Remove Nth Node From End of List/README.md +++ b/solution/0000-0099/0019.Remove Nth Node From End of List/README.md @@ -52,11 +52,11 @@ **方法一:快慢指针** -定义两个指针 `fast` 和 `slow`,初始时都指向链表的虚拟头结点 `dummy`。 +我们定义两个指针 `fast` 和 `slow`,初始时都指向链表的虚拟头结点 `dummy`。 接着 `fast` 指针先向前移动 $n$ 步,然后 `fast` 和 `slow` 指针同时向前移动,直到 `fast` 指针到达链表的末尾。此时 `slow.next` 指针指向的结点就是倒数第 `n` 个结点的前驱结点,将其删除即可。 -时间复杂度 $O(n)$,空间复杂度 $O(1)$。其中 $n$ 为链表的长度。 +时间复杂度 $O(n)$,其中 $n$ 为链表的长度。空间复杂度 $O(1)$。 diff --git a/solution/0000-0099/0019.Remove Nth Node From End of List/README_EN.md b/solution/0000-0099/0019.Remove Nth Node From End of List/README_EN.md index 7c159ab950773..9438572104a84 100644 --- a/solution/0000-0099/0019.Remove Nth Node From End of List/README_EN.md +++ b/solution/0000-0099/0019.Remove Nth Node From End of List/README_EN.md @@ -43,6 +43,14 @@ ## Solutions +**Solution 1: Fast and Slow Pointers** + +We define two pointers `fast` and `slow`, both initially pointing to the dummy head node of the linked list. + +Next, the `fast` pointer moves forward $n$ steps first, then `fast` and `slow` pointers move forward together until the `fast` pointer reaches the end of the linked list. At this point, the node pointed to by `slow.next` is the predecessor of the $n$-th node from the end, and we can delete it. + +The time complexity is $O(n)$, where $n$ is the length of the linked list. The space complexity is $O(1)$. + ### **Python3** diff --git a/solution/0000-0099/0020.Valid Parentheses/README_EN.md b/solution/0000-0099/0020.Valid Parentheses/README_EN.md index 55aed31cf83be..4e9d5af8b5881 100644 --- a/solution/0000-0099/0020.Valid Parentheses/README_EN.md +++ b/solution/0000-0099/0020.Valid Parentheses/README_EN.md @@ -46,6 +46,18 @@ ## Solutions +**Solution 1: Stack** + +Traverse the bracket string $s$. When encountering a left bracket, push the current left bracket into the stack; when encountering a right bracket, pop the top element of the stack (if the stack is empty, directly return `false`), and judge whether it matches. If it does not match, directly return `false`. + +Alternatively, when encountering a left bracket, you can push the corresponding right bracket into the stack; when encountering a right bracket, pop the top element of the stack (if the stack is empty, directly return `false`), and judge whether they are equal. If they do not match, directly return `false`. + +> The difference between the two methods is only the timing of bracket conversion, one is when pushing into the stack, and the other is when popping out of the stack. + +At the end of the traversal, if the stack is empty, it means the bracket string is valid, return `true`; otherwise, return `false`. + +The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the bracket string $s$. + ### **Python3** diff --git a/solution/0000-0099/0021.Merge Two Sorted Lists/README_EN.md b/solution/0000-0099/0021.Merge Two Sorted Lists/README_EN.md index 55b6157e44de2..19ebe5351e645 100644 --- a/solution/0000-0099/0021.Merge Two Sorted Lists/README_EN.md +++ b/solution/0000-0099/0021.Merge Two Sorted Lists/README_EN.md @@ -43,6 +43,25 @@ ## Solutions +**Solution 1: Recursion** + +First, we judge whether the linked lists $l_1$ and $l_2$ are empty. If one of them is empty, we return the other linked list. Otherwise, we compare the head nodes of $l_1$ and $l_2$: + +- If the value of the head node of $l_1$ is less than or equal to the value of the head node of $l_2$, we recursively call the function $mergeTwoLists(l_1.next, l_2)$, and connect the head node of $l_1$ with the returned linked list head node, and return the head node of $l_1$. +- Otherwise, we recursively call the function $mergeTwoLists(l_1, l_2.next)$, and connect the head node of $l_2$ with the returned linked list head node, and return the head node of $l_2$. + +The time complexity is $O(m + n)$, and the space complexity is $O(m + n)$. Here, $m$ and $n$ are the lengths of the two linked lists respectively. + +**Solution 2: Iteration** + +We can also use iteration to implement the merging of two sorted linked lists. + +First, we define a dummy head node $dummy$, then loop through the two linked lists, compare the head nodes of the two linked lists, add the smaller node to the end of $dummy$, until one of the linked lists is empty, then add the remaining part of the other linked list to the end of $dummy$. + +Finally, return $dummy.next$. + +The time complexity is $O(m + n)$, where $m$ and $n$ are the lengths of the two linked lists respectively. Ignoring the space consumption of the answer linked list, the space complexity is $O(1)$. + ### **Python3** diff --git a/solution/0000-0099/0022.Generate Parentheses/README_EN.md b/solution/0000-0099/0022.Generate Parentheses/README_EN.md index e14fa0a985317..aac0dccd98dca 100644 --- a/solution/0000-0099/0022.Generate Parentheses/README_EN.md +++ b/solution/0000-0099/0022.Generate Parentheses/README_EN.md @@ -23,7 +23,18 @@ ## Solutions -DFS. +**Solution 1: DFS + Pruning** + +The range of $n$ in the problem is $[1, 8]$, so we can directly solve this problem through "brute force search + pruning". + +We design a function $dfs(l, r, t)$, where $l$ and $r$ represent the number of left and right brackets respectively, and $t$ represents the current bracket sequence. Then we can get the following recursive structure: + +- If $l \gt n$ or $r \gt n$ or $l \lt r$, then the current bracket combination $t$ is invalid, return directly; +- If $l = n$ and $r = n$, then the current bracket combination $t$ is valid, add it to the answer array `ans`, and return directly; +- We can choose to add a left bracket, and recursively execute `dfs(l + 1, r, t + "(")`; +- We can also choose to add a right bracket, and recursively execute `dfs(l, r + 1, t + ")")`. + +The time complexity is $O(2^{n\times 2} \times n)$, and the space complexity is $O(n)$. diff --git a/solution/0000-0099/0023.Merge k Sorted Lists/README_EN.md b/solution/0000-0099/0023.Merge k Sorted Lists/README_EN.md index 479fadbc8b8ea..4b39d965a2841 100644 --- a/solution/0000-0099/0023.Merge k Sorted Lists/README_EN.md +++ b/solution/0000-0099/0023.Merge k Sorted Lists/README_EN.md @@ -52,6 +52,12 @@ merging them into one sorted list: ## Solutions +**Solution 1: Priority Queue (Min Heap)** + +We can create a min heap $pq$ to maintain the head nodes of all linked lists. Each time, we take out the node with the smallest value from the min heap, add it to the end of the result linked list, and then add the next node of this node to the heap. Repeat the above steps until the heap is empty. + +The time complexity is $O(n \times \log k)$, and the space complexity is $O(k)$. Here, $n$ is the total number of all linked list nodes, and $k$ is the number of linked lists given in the problem. + ### **Python3** diff --git a/solution/0000-0099/0024.Swap Nodes in Pairs/README_EN.md b/solution/0000-0099/0024.Swap Nodes in Pairs/README_EN.md index 24af047a090f2..764f60e855613 100644 --- a/solution/0000-0099/0024.Swap Nodes in Pairs/README_EN.md +++ b/solution/0000-0099/0024.Swap Nodes in Pairs/README_EN.md @@ -40,21 +40,21 @@ **Solution 1: Recursion** -We can swap the two nodes in the linked list in a recursive way. +We can implement swapping two nodes in the linked list through recursion. -The termination condition of the recursion is that there is no node in the linked list, or there is only one node in the linked list, and the swap cannot be performed, so return the node directly. +The termination condition of recursion is that there are no nodes in the linked list, or there is only one node in the linked list. At this time, swapping cannot be performed, so we directly return this node. -Otherwise, we recursively swap the linked list $head.next.next$, and the head node of the swap is $t$. Then we set the next node of $head$ to $p$, then set $p$ to point to $head$, and finally return $p$. +Otherwise, we recursively swap the linked list $head.next.next$, and let the swapped head node be $t$. Then we let $p$ be the next node of $head$, and let $p$ point to $head$, and $head$ point to $t$, finally return $p$. -The time complexity is $O(n)$, the space complexity is $O(n)$, where $n$ is the length of the linked list. +The time complexity is $O(n)$, and the space complexity is $O(n)$. Here, $n$ is the length of the linked list. **Solution 2: Iteration** -We set a virtual head node $dummy$, which initially points to $head$, and then set two pointers $pre$ and $cur$, $pre$ initially points to $dummy$, and $cur$ points to $head$. +We set a dummy head node $dummy$, initially pointing to $head$, and then set two pointers $pre$ and $cur$, initially $pre$ points to $dummy$, and $cur$ points to $head$. -Next, we traverse the linked list. Each time we need to swap the two nodes after $pre$, so we first determine whether $cur$ and $cur.next$ are empty. If they are not empty, swap them, otherwise terminate the loop. +Next, we traverse the linked list. Each time we need to swap the two nodes after $pre$, so we first judge whether $cur$ and $cur.next$ are empty. If they are not empty, we perform the swap, otherwise we terminate the loop. -The time complexity is $O(n)$, and the space complexity is $O(1)$, where $n$ is the length of the linked list. +The time complexity is $O(n)$, and the space complexity is $O(1)$. Here, $n$ is the length of the linked list. diff --git a/solution/0000-0099/0025.Reverse Nodes in k-Group/README_EN.md b/solution/0000-0099/0025.Reverse Nodes in k-Group/README_EN.md index e788c3eb07f82..87a9f6a6bf708 100644 --- a/solution/0000-0099/0025.Reverse Nodes in k-Group/README_EN.md +++ b/solution/0000-0099/0025.Reverse Nodes in k-Group/README_EN.md @@ -41,11 +41,11 @@ **Solution 1: Iteration** -Time complexity $O(n)$, Space complexity $O(1)$. +The time complexity is $O(n)$, and the space complexity is $O(1)$. Here, $n$ is the length of the linked list. **Solution 2: Recursion** -Time complexity $O(n)$, Space complexity $O(\log _k n)$. +The time complexity is $O(n)$, and the space complexity is $O(\log_k n)$. Here, $n$ is the length of the linked list. diff --git a/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README.md b/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README.md index 290fdeca6675b..ee70ed091a46e 100644 --- a/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README.md +++ b/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README.md @@ -81,7 +81,9 @@ for (int i = 0; i < k; i++) { - 由于相同的数字最多保留 $k$ 个,那么原数组的前 $k$ 个元素我们可以直接保留; - 对于后面的数字,能够保留的前提是:当前数字 $x$ 与前面已保留的数字的倒数第 $k$ 个元素比较,不同则保留,相同则跳过。 -相似题目:[80. 删除有序数组中的重复项 II](/solution/0000-0099/0080.Remove%20Duplicates%20from%20Sorted%20Array%20II/README.md) +相似题目: + +- [80. 删除有序数组中的重复项 II](/solution/0000-0099/0080.Remove%20Duplicates%20from%20Sorted%20Array%20II/README.md) diff --git a/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README_EN.md b/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README_EN.md index 577c6f050823a..bc9815b756d58 100644 --- a/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README_EN.md +++ b/solution/0000-0099/0026.Remove Duplicates from Sorted Array/README_EN.md @@ -61,6 +61,27 @@ It does not matter what you leave beyond the returned k (hence they are undersco ## Solutions +**Solution 1: Single Pass** + +We use a variable $k$ to record the current length of the processed array. Initially, $k=0$ represents an empty array. + +Then we traverse the array from left to right. For each element $x$ we encounter, if $k=0$ or $x \neq nums[k-1]$, we place $x$ in the position of $nums[k]$, and then increment $k$ by $1$. Otherwise, $x$ is the same as $nums[k-1]$, so we skip this element. Continue to traverse until the entire array is traversed. + +In this way, when the traversal ends, the first $k$ elements in $nums$ are the answer we are looking for, and $k$ is the length of the answer. + +The time complexity is $O(n)$, and the space complexity is $O(1)$. Here, $n$ is the length of the array. + +Supplement: + +The original problem requires that the same number appear at most once. We can extend it to keep at most $k$ identical numbers. + +- Since the same number can be kept at most $k$ times, we can directly keep the first $k$ elements of the original array; +- For the following numbers, the premise of being able to keep them is: the current number $x$ is compared with the last $k$th element of the previously retained numbers. If they are different, keep them, otherwise skip them. + +Similar problems: + +- [80. Remove Duplicates from Sorted Array II](/solution/0000-0099/0080.Remove%20Duplicates%20from%20Sorted%20Array%20II/README_EN.md) + ### **Python3** diff --git a/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README.md b/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README.md index 4db37debec3cf..ee2ef8cfa843c 100644 --- a/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README.md +++ b/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README.md @@ -44,17 +44,17 @@ 以字符串 `haystack` 的每一个字符为起点与字符串 `needle` 进行比较,若发现能够匹配的索引,直接返回即可。 -假设字符串 `haystack` 长度为 `n`,字符串 `needle` 长度为 `m`,则时间复杂度为 $O((n-m) \times m)$,空间复杂度 $O(1)$。 +假设字符串 `haystack` 长度为 $n$,字符串 `needle` 长度为 $m$,则时间复杂度为 $O((n-m) \times m)$,空间复杂度 $O(1)$。 **方法二:Rabin-Karp 字符串匹配算法** [Rabin-Karp 算法](https://zh.wikipedia.org/zh-hans/%E6%8B%89%E5%AE%BE-%E5%8D%A1%E6%99%AE%E7%AE%97%E6%B3%95)本质上是利用滑动窗口配合哈希函数对固定长度的字符串哈希之后进行比较,可以将比较两个字符串是否相同的时间复杂度降为 $O(1)$。 -假设字符串 `haystack` 长度为 `n`,字符串 `needle` 长度为 `m`,则时间复杂度为 $O(n+m)$,空间复杂度 $O(1)$。 +假设字符串 `haystack` 长度为 $n$,字符串 `needle` 长度为 $m$,则时间复杂度为 $O(n+m)$,空间复杂度 $O(1)$。 **方法三:KMP 字符串匹配算法** -假设字符串 `haystack` 长度为 `n`,字符串 `needle` 长度为 `m`,则时间复杂度为 $O(n+m)$,空间复杂度 $O(m)$。 +假设字符串 `haystack` 长度为 $n$,字符串 `needle` 长度为 $m$,则时间复杂度为 $O(n+m)$,空间复杂度 $O(m)$。 diff --git a/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README_EN.md b/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README_EN.md index d101438345f9e..e95cefa2f205d 100644 --- a/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README_EN.md +++ b/solution/0000-0099/0028.Find the Index of the First Occurrence in a String/README_EN.md @@ -34,17 +34,21 @@ The first occurrence is at index 0, so we return 0. ## Solutions -**Solution 1: Traverse** +**Solution 1: Traversal** -Time complexity $O((n-m) \times m)$, Space complexity $O(1)$. +We compare the string `needle` with each character of the string `haystack` as the starting point. If we find a matching index, we return it directly. -**Solution 2: Rabin-Karp** +Assuming the length of the string `haystack` is $n$ and the length of the string `needle` is $m$, the time complexity is $O((n-m) \times m)$, and the space complexity is $O(1)$. -Time complexity $O(n+m)$, Space complexity $O(1)$. +**Solution 2: Rabin-Karp String Matching Algorithm** -**Solution 3: KMP** +The [Rabin-Karp algorithm](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) essentially uses a sliding window combined with a hash function to compare the hashes of fixed-length strings, which can reduce the time complexity of comparing whether two strings are the same to $O(1)$. -Time complexity $O(n+m)$, Space complexity $O(m)$. +Assuming the length of the string `haystack` is $n$ and the length of the string `needle` is $m$, the time complexity is $O(n+m)$, and the space complexity is $O(1)$. + +**Solution 3: KMP String Matching Algorithm** + +Assuming the length of the string `haystack` is $n$ and the length of the string `needle` is $m$, the time complexity is $O(n+m)$, and the space complexity is $O(m)$. diff --git a/solution/0000-0099/0029.Divide Two Integers/README_EN.md b/solution/0000-0099/0029.Divide Two Integers/README_EN.md index df7d914f1ec35..253d4cfd96ae3 100644 --- a/solution/0000-0099/0029.Divide Two Integers/README_EN.md +++ b/solution/0000-0099/0029.Divide Two Integers/README_EN.md @@ -39,9 +39,13 @@ ## Solutions -**Solution 1: Quick Power** +**Solution 1: Simulation + Fast Power** -Time complexity $O(\log a \times \log b)$, Space complexity $O(1)$. +Division is essentially subtraction. The problem requires us to calculate the integer result after dividing two numbers, which is actually calculating how many divisors and a number less than the divisor constitute the dividend. However, only one subtraction can be done in one loop, which is too inefficient and will lead to timeout. This can be optimized by using the idea of fast power. + +It should be noted that since the problem explicitly requires that only 32-bit signed integers can be used at most, the divisor and dividend need to be converted to negative numbers for calculation. Because converting to positive numbers may cause overflow, such as when the dividend is `INT32_MIN`, it will be greater than `INT32_MAX` when converted to a positive number. + +Assuming the dividend is `a` and the divisor is `b`, the time complexity is $O(\log a \times \log b)$, and the space complexity is $O(1)$. diff --git a/solution/0000-0099/0030.Substring with Concatenation of All Words/README_EN.md b/solution/0000-0099/0030.Substring with Concatenation of All Words/README_EN.md index 09c528ae0066d..af533b18b9b5d 100644 --- a/solution/0000-0099/0030.Substring with Concatenation of All Words/README_EN.md +++ b/solution/0000-0099/0030.Substring with Concatenation of All Words/README_EN.md @@ -61,13 +61,13 @@ The substring starting at 12 is "thefoobar". It is the concatenation o **Solution 1: Hash Table + Sliding Window** -We use a hash table $cnt$ to count the number of times each word in $words$ appears, and use a hash table $cnt1$ to count the number of times each word in the current sliding window appears. Let the length of the string $s$ be $m$, the number of words in the string array $words$ be $n$, and the length of each word be $k$. +We use a hash table $cnt$ to count the number of times each word appears in $words$, and use a hash table $cnt1$ to count the number of times each word appears in the current sliding window. We denote the length of the string $s$ as $m$, the number of words in the string array $words$ as $n$, and the length of each word as $k$. -We can enumerate the start point $i$ of the sliding window, where $0 \lt i \lt k$. For each start point, we maintain a sliding window, with the left boundary as $l$, the right boundary as $r$, the number of words in the sliding window as $t$, and a hash table $cnt1$ to count the number of times each word in the sliding window appears. +We can enumerate the starting point $i$ of the sliding window, where $0 \lt i < k$. For each starting point, we maintain a sliding window with the left boundary as $l$, the right boundary as $r$, and the number of words in the sliding window as $t$. Additionally, we use a hash table $cnt1$ to count the number of times each word appears in the sliding window. -Each time, we extract the string $s[r:r+k]$. If $s[r:r+k]$ is not in the hash table $cnt$, it means that the words in the current sliding window are invalid. We update the left boundary $l$ to $r$, and clear the hash table $cnt1$ and reset the number of words $t$ to 0. If $s[r:r+k]$ is in the hash table $cnt$, it means that the words in the current sliding window are valid. We add 1 to the number of words $t$, and add 1 to the number of times $s[r:r+k]$ appears in the hash table $cnt1$. If $cnt1[s[r:r+k]]$ is greater than $cnt[s[r:r+k]]$, it means that the number of times $s[r:r+k]$ appears in the current sliding window is too large. We need to move the left boundary $l$ to the right until $cnt1[s[r:r+k]] = cnt[s[r:r+k]]$. If $t = n$, it means that the words in the current sliding window are exactly valid, and we add the left boundary $l$ to the answer array. +Each time, we extract the string $s[r:r+k]$. If $s[r:r+k]$ is not in the hash table $cnt$, it means that the words in the current sliding window are not valid. We update the left boundary $l$ to $r$, clear the hash table $cnt1$, and reset the word count $t$ to 0. If $s[r:r+k]$ is in the hash table $cnt$, it means that the words in the current sliding window are valid. We increase the word count $t$ by 1, and increase the count of $s[r:r+k]$ in the hash table $cnt1$ by 1. If $cnt1[s[r:r+k]]$ is greater than $cnt[s[r:r+k]]$, it means that $s[r:r+k]$ appears too many times in the current sliding window. We need to move the left boundary $l$ to the right until $cnt1[s[r:r+k]] = cnt[s[r:r+k]]$. If $t = n$, it means that the words in the current sliding window are exactly valid, and we add the left boundary $l$ to the answer array. -Time complexity $O(m \times k)$, space complexity $O(n \times k)$. $m$ and $n$ are the lengths of the string $s$ and the string array $words$, respectively. $k$ is the length of the words in the string array $words$. +The time complexity is $O(m \times k)$, and the space complexity is $O(n \times k)$. Here, $m$ and $n$ are the lengths of the string $s$ and the string array $words$ respectively, and $k$ is the length of the words in the string array $words$. diff --git a/solution/0000-0099/0033.Search in Rotated Sorted Array/README.md b/solution/0000-0099/0033.Search in Rotated Sorted Array/README.md index 30dcbe33d77f8..abbaba1344372 100644 --- a/solution/0000-0099/0033.Search in Rotated Sorted Array/README.md +++ b/solution/0000-0099/0033.Search in Rotated Sorted Array/README.md @@ -54,18 +54,20 @@ **方法一:二分查找** -我们使用二分,将数组分割成 `[left, mid]`, `[mid + 1, right]` 两部分,这时候可以发现,其中有一部分一定是有序的。 +我们使用二分,将数组分割成 $[left,.. mid]$, $[mid + 1,.. right]$ 两部分,这时候可以发现,其中有一部分一定是有序的。 -因此,我们可以根据有序的那一部分,判断 `target` 是否在这一部分中: +因此,我们可以根据有序的那一部分,判断 $target$ 是否在这一部分中: -- 若 `[0, mid]` 范围内的元素构成有序数组: - - 若满足 `nums[0] <= target <= nums[mid]`,那么我们搜索范围可以缩小为 `[left, mid]`; - - 否则,在 `[mid + 1, right]` 中查找; -- 若 `[mid + 1, n - 1]` 范围内的元素构成有序数组: - - 若满足 `nums[mid] < target <= nums[n - 1]`,那么我们搜索范围可以缩小为 `[mid + 1, right]`; - - 否则,在 `[left, mid]` 中查找。 +- 若 $[0,.. mid]$ 范围内的元素构成有序数组: + - 若满足 $nums[0] \leq target \leq nums[mid]$,那么我们搜索范围可以缩小为 $[left,.. mid]$; + - 否则,在 $[mid + 1,.. right]$ 中查找; +- 若 $[mid + 1, n - 1]$ 范围内的元素构成有序数组: + - 若满足 $nums[mid] \lt target \leq nums[n - 1]$,那么我们搜索范围可以缩小为 $[mid + 1,.. right]$; + - 否则,在 $[left,.. mid]$ 中查找。 -二分查找终止条件是 `left >= right`,若结束后发现 `nums[left]` 与 `target` 不等,说明数组中不存在值为 `target` 的元素,返回 -1,否则返回下标 left。 +二分查找终止条件是 $left \geq right$,若结束后发现 $nums[left]$ 与 $target$ 不等,说明数组中不存在值为 $target$ 的元素,返回 $-1$,否则返回下标 $left$。 + +时间复杂度 $O(\log n)$,其中 $n$ 是数组 $nums$ 的长度。空间复杂度 $O(1)$。 diff --git a/solution/0000-0099/0033.Search in Rotated Sorted Array/README_EN.md b/solution/0000-0099/0033.Search in Rotated Sorted Array/README_EN.md index a8378bc2d17cc..9a672fb77866d 100644 --- a/solution/0000-0099/0033.Search in Rotated Sorted Array/README_EN.md +++ b/solution/0000-0099/0033.Search in Rotated Sorted Array/README_EN.md @@ -36,7 +36,22 @@ ## Solutions -Binary search. +**Solution 1: Binary Search** + +We use binary search to divide the array into two parts, $[left,.. mid]$ and $[mid + 1,.. right]$. At this point, we can find that one part must be sorted. + +Therefore, we can determine whether $target$ is in this part based on the sorted part: + +- If the elements in the range $[0,.. mid]$ form a sorted array: + - If $nums[0] \leq target \leq nums[mid]$, then our search range can be narrowed down to $[left,.. mid]$; + - Otherwise, search in $[mid + 1,.. right]$; +- If the elements in the range $[mid + 1, n - 1]$ form a sorted array: + - If $nums[mid] \lt target \leq nums[n - 1]$, then our search range can be narrowed down to $[mid + 1,.. right]$; + - Otherwise, search in $[left,.. mid]$. + +The termination condition for binary search is $left \geq right$. If at the end we find that $nums[left]$ is not equal to $target$, it means that there is no element with a value of $target$ in the array, and we return $-1$. Otherwise, we return the index $left$. + +The time complexity is $O(\log n)$, where $n$ is the length of the array $nums$. The space complexity is $O(1)$. 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 15e0a87279005..ba756d39383e7 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 @@ -51,7 +51,7 @@ 我们可以进行两次二分查找,分别查找出左边界和右边界。 -时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。其中 $n$ 是数组 `nums` 的长度。 +时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。其中 $n$ 是数组 $nums$ 的长度。 以下是二分查找的两个通用模板: 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 670ab0c60b905..b018d9b28fe1b 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 @@ -33,7 +33,13 @@ ## Solutions -Binary search. +**Solution 1: Binary Search** + +We can perform binary search twice to find the left and right boundaries respectively. + +The time complexity is $O(\log n)$, and the space complexity is $O(1)$. Here, $n$ is the length of the array $nums$. + +Below are two general templates for binary search: Template 1: diff --git a/solution/0000-0099/0035.Search Insert Position/README.md b/solution/0000-0099/0035.Search Insert Position/README.md index 5ec32ec10f71c..2fb3ef5106c07 100644 --- a/solution/0000-0099/0035.Search Insert Position/README.md +++ b/solution/0000-0099/0035.Search Insert Position/README.md @@ -50,9 +50,9 @@ **方法一:二分查找** -由于 `nums` 数组已经有序,因此我们可以使用二分查找的方法找到目标值 `target` 的插入位置。 +由于 $nums$ 数组已经有序,因此我们可以使用二分查找的方法找到目标值 $target$ 的插入位置。 -时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。其中 $n$ 为数组 `nums` 的长度。 +时间复杂度 $O(\log n)$,空间复杂度 $O(1)$。其中 $n$ 为数组 $nums$ 的长度。 diff --git a/solution/0000-0099/0035.Search Insert Position/README_EN.md b/solution/0000-0099/0035.Search Insert Position/README_EN.md index a13851db2deec..6452d4e9e86f7 100644 --- a/solution/0000-0099/0035.Search Insert Position/README_EN.md +++ b/solution/0000-0099/0035.Search Insert Position/README_EN.md @@ -42,7 +42,11 @@ ## Solutions -Binary search. +**Solution 1: Binary Search** + +Since the array $nums$ is already sorted, we can use the binary search method to find the insertion position of the target value $target$. + +The time complexity is $O(\log n)$, and the space complexity is $O(1)$. Here, $n$ is the length of the array $nums$. From c2fd463cd8eb0ce4c620226b5d0132aafcd60912 Mon Sep 17 00:00:00 2001 From: yanglbme Date: Tue, 28 Nov 2023 20:26:06 +0800 Subject: [PATCH 2/2] feat: add solutions --- solution/0000-0099/0037.Sudoku Solver/README_EN.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/solution/0000-0099/0037.Sudoku Solver/README_EN.md b/solution/0000-0099/0037.Sudoku Solver/README_EN.md index 193aeb83d9062..8601925ab6878 100644 --- a/solution/0000-0099/0037.Sudoku Solver/README_EN.md +++ b/solution/0000-0099/0037.Sudoku Solver/README_EN.md @@ -39,6 +39,14 @@ ## Solutions +**Solution 1: Backtracking** + +We use arrays `row`, `col`, and `box` to record whether a number has appeared in each row, each column, and each 3x3 grid respectively. If the number `i` has appeared in the `r`th row, the `c`th column, and the `b`th 3x3 grid, then `row[r][i]`, `col[c][i]`, and `box[b][i]` are all `true`. + +We traverse each empty space in `board`, enumerate the numbers `v` that it can fill in. If `v` has not appeared in the current row, the current column, and the current 3x3 grid, then we can try to fill in the number `v` and continue to search for the next empty space. If we search to the end and all spaces are filled, it means that a feasible solution has been found. + +The time complexity is $O(9^{81})$, and the space complexity is $O(9^2)$. + ### **Python3**