From 58c2190ce16a21a0ed8a21c2bd02e8ea855654f3 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Thu, 16 Jan 2025 17:38:01 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.0622 (#3959) No.0622.Design Circular Queue --- .../0622.Design Circular Queue/README.md | 71 +++++++++++-------- .../0622.Design Circular Queue/README_EN.md | 71 +++++++++++-------- .../0622.Design Circular Queue/Solution.py | 8 +-- .../0622.Design Circular Queue/Solution.rs | 44 ++++++------ .../README.md | 14 ++-- .../README_EN.md | 19 ++++- .../0629.K Inverse Pairs Array/README.md | 4 +- .../0629.K Inverse Pairs Array/README_EN.md | 24 ++++++- .../0629.K Inverse Pairs Array/Solution.ts | 4 +- 9 files changed, 160 insertions(+), 99 deletions(-) diff --git a/solution/0600-0699/0622.Design Circular Queue/README.md b/solution/0600-0699/0622.Design Circular Queue/README.md index fae9aaced7aca..71ae8a11c9c72 100644 --- a/solution/0600-0699/0622.Design Circular Queue/README.md +++ b/solution/0600-0699/0622.Design Circular Queue/README.md @@ -66,7 +66,23 @@ circularQueue.Rear();  // 返回 4 -### 方法一 +### 方法一:数组模拟 + +我们可以使用一个长度为 $k$ 的数组 $q$ 来模拟循环队列,用一个指针 $\textit{front}$ 记录队首元素的位置,初始时队列为空,而 $\textit{front}$ 为 $0$。另外,我们用一个变量 $\textit{size}$ 记录队列中元素的个数,初始时 $\textit{size}$ 为 $0$。 + +调用 `enQueue` 方法时,我们首先检查队列是否已满,即 $\textit{size} = k$,如果满了则直接返回 $\textit{false}$。否则,我们将元素插入到 $(\textit{front} + \textit{size}) \bmod k$ 的位置,然后 $\textit{size} = \textit{size} + 1$,表示队列中元素的个数增加了 $1$。最后返回 $\textit{true}$。 + +调用 `deQueue` 方法时,我们首先检查队列是否为空,即 $\textit{size} = 0$,如果为空则直接返回 $\textit{false}$。否则,我们将 $\textit{front} = (\textit{front} + 1) \bmod k$,表示队首元素出队,然后 $\textit{size} = \textit{size} - 1$, + +调用 `Front` 方法时,我们首先检查队列是否为空,即 $\textit{size} = 0$,如果为空则返回 $-1$。否则,返回 $q[\textit{front}]$。 + +调用 `Rear` 方法时,我们首先检查队列是否为空,即 $\textit{size} = 0$,如果为空则返回 $-1$。否则,返回 $q[(\textit{front} + \textit{size} - 1) \bmod k]$。 + +调用 `isEmpty` 方法时,我们只需判断 $\textit{size} = 0$ 即可。 + +调用 `isFull` 方法时,我们只需判断 $\textit{size} = k$ 即可。 + +时间复杂度方面,以上操作的时间复杂度均为 $O(1)$。空间复杂度为 $O(k)$。 @@ -74,17 +90,17 @@ circularQueue.Rear();  // 返回 4 ```python class MyCircularQueue: + def __init__(self, k: int): self.q = [0] * k - self.front = 0 self.size = 0 self.capacity = k + self.front = 0 def enQueue(self, value: int) -> bool: if self.isFull(): return False - idx = (self.front + self.size) % self.capacity - self.q[idx] = value + self.q[(self.front + self.size) % self.capacity] = value self.size += 1 return True @@ -101,8 +117,7 @@ class MyCircularQueue: def Rear(self) -> int: if self.isEmpty(): return -1 - idx = (self.front + self.size - 1) % self.capacity - return self.q[idx] + return self.q[(self.front + self.size - 1) % self.capacity] def isEmpty(self) -> bool: return self.size == 0 @@ -395,24 +410,19 @@ class MyCircularQueue { ```rust struct MyCircularQueue { - queue: Vec, - left: usize, - right: usize, + q: Vec, + size: usize, capacity: usize, + front: usize, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MyCircularQueue { fn new(k: i32) -> Self { - let k = k as usize; - Self { - queue: vec![0; k], - left: 0, - right: 0, - capacity: k, + MyCircularQueue { + q: vec![0; k as usize], + size: 0, + capacity: k as usize, + front: 0, } } @@ -420,8 +430,9 @@ impl MyCircularQueue { if self.is_full() { return false; } - self.queue[self.right % self.capacity] = value; - self.right += 1; + let rear = (self.front + self.size) % self.capacity; + self.q[rear] = value; + self.size += 1; true } @@ -429,30 +440,34 @@ impl MyCircularQueue { if self.is_empty() { return false; } - self.left += 1; + self.front = (self.front + 1) % self.capacity; + self.size -= 1; true } fn front(&self) -> i32 { if self.is_empty() { - return -1; + -1 + } else { + self.q[self.front] } - self.queue[self.left % self.capacity] } fn rear(&self) -> i32 { if self.is_empty() { - return -1; + -1 + } else { + let rear = (self.front + self.size - 1) % self.capacity; + self.q[rear] } - self.queue[(self.right - 1) % self.capacity] } fn is_empty(&self) -> bool { - self.right - self.left == 0 + self.size == 0 } fn is_full(&self) -> bool { - self.right - self.left == self.capacity + self.size == self.capacity } } ``` diff --git a/solution/0600-0699/0622.Design Circular Queue/README_EN.md b/solution/0600-0699/0622.Design Circular Queue/README_EN.md index 74443b85a2463..a80fc2aa4b5a0 100644 --- a/solution/0600-0699/0622.Design Circular Queue/README_EN.md +++ b/solution/0600-0699/0622.Design Circular Queue/README_EN.md @@ -75,7 +75,23 @@ myCircularQueue.Rear(); // return 4 -### Solution 1 +### Solution 1: Array Simulation + +We can use an array $q$ of length $k$ to simulate a circular queue, with a pointer $\textit{front}$ to record the position of the front element. Initially, the queue is empty, and $\textit{front}$ is $0$. Additionally, we use a variable $\textit{size}$ to record the number of elements in the queue, initially $\textit{size}$ is $0$. + +When calling the `enQueue` method, we first check if the queue is full, i.e., $\textit{size} = k$. If it is full, we return $\textit{false}$. Otherwise, we insert the element at position $(\textit{front} + \textit{size}) \bmod k$, then $\textit{size} = \textit{size} + 1$, indicating that the number of elements in the queue has increased by $1$. Finally, we return $\textit{true}$. + +When calling the `deQueue` method, we first check if the queue is empty, i.e., $\textit{size} = 0$. If it is empty, we return $\textit{false}$. Otherwise, we set $\textit{front} = (\textit{front} + 1) \bmod k$, indicating that the front element has been dequeued, then $\textit{size} = \textit{size} - 1$. + +When calling the `Front` method, we first check if the queue is empty, i.e., $\textit{size} = 0$. If it is empty, we return $-1$. Otherwise, we return $q[\textit{front}]$. + +When calling the `Rear` method, we first check if the queue is empty, i.e., $\textit{size} = 0$. If it is empty, we return $-1$. Otherwise, we return $q[(\textit{front} + \textit{size} - 1) \bmod k]$. + +When calling the `isEmpty` method, we simply check if $\textit{size} = 0$. + +When calling the `isFull` method, we simply check if $\textit{size} = k$. + +In terms of time complexity, the above operations all have a time complexity of $O(1)$. The space complexity is $O(k)$. @@ -83,17 +99,17 @@ myCircularQueue.Rear(); // return 4 ```python class MyCircularQueue: + def __init__(self, k: int): self.q = [0] * k - self.front = 0 self.size = 0 self.capacity = k + self.front = 0 def enQueue(self, value: int) -> bool: if self.isFull(): return False - idx = (self.front + self.size) % self.capacity - self.q[idx] = value + self.q[(self.front + self.size) % self.capacity] = value self.size += 1 return True @@ -110,8 +126,7 @@ class MyCircularQueue: def Rear(self) -> int: if self.isEmpty(): return -1 - idx = (self.front + self.size - 1) % self.capacity - return self.q[idx] + return self.q[(self.front + self.size - 1) % self.capacity] def isEmpty(self) -> bool: return self.size == 0 @@ -404,24 +419,19 @@ class MyCircularQueue { ```rust struct MyCircularQueue { - queue: Vec, - left: usize, - right: usize, + q: Vec, + size: usize, capacity: usize, + front: usize, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MyCircularQueue { fn new(k: i32) -> Self { - let k = k as usize; - Self { - queue: vec![0; k], - left: 0, - right: 0, - capacity: k, + MyCircularQueue { + q: vec![0; k as usize], + size: 0, + capacity: k as usize, + front: 0, } } @@ -429,8 +439,9 @@ impl MyCircularQueue { if self.is_full() { return false; } - self.queue[self.right % self.capacity] = value; - self.right += 1; + let rear = (self.front + self.size) % self.capacity; + self.q[rear] = value; + self.size += 1; true } @@ -438,30 +449,34 @@ impl MyCircularQueue { if self.is_empty() { return false; } - self.left += 1; + self.front = (self.front + 1) % self.capacity; + self.size -= 1; true } fn front(&self) -> i32 { if self.is_empty() { - return -1; + -1 + } else { + self.q[self.front] } - self.queue[self.left % self.capacity] } fn rear(&self) -> i32 { if self.is_empty() { - return -1; + -1 + } else { + let rear = (self.front + self.size - 1) % self.capacity; + self.q[rear] } - self.queue[(self.right - 1) % self.capacity] } fn is_empty(&self) -> bool { - self.right - self.left == 0 + self.size == 0 } fn is_full(&self) -> bool { - self.right - self.left == self.capacity + self.size == self.capacity } } ``` diff --git a/solution/0600-0699/0622.Design Circular Queue/Solution.py b/solution/0600-0699/0622.Design Circular Queue/Solution.py index f95c1de0f220c..3658636df182b 100644 --- a/solution/0600-0699/0622.Design Circular Queue/Solution.py +++ b/solution/0600-0699/0622.Design Circular Queue/Solution.py @@ -1,15 +1,14 @@ class MyCircularQueue: def __init__(self, k: int): self.q = [0] * k - self.front = 0 self.size = 0 self.capacity = k + self.front = 0 def enQueue(self, value: int) -> bool: if self.isFull(): return False - idx = (self.front + self.size) % self.capacity - self.q[idx] = value + self.q[(self.front + self.size) % self.capacity] = value self.size += 1 return True @@ -26,8 +25,7 @@ def Front(self) -> int: def Rear(self) -> int: if self.isEmpty(): return -1 - idx = (self.front + self.size - 1) % self.capacity - return self.q[idx] + return self.q[(self.front + self.size - 1) % self.capacity] def isEmpty(self) -> bool: return self.size == 0 diff --git a/solution/0600-0699/0622.Design Circular Queue/Solution.rs b/solution/0600-0699/0622.Design Circular Queue/Solution.rs index 450fad52c5237..39044327c571c 100644 --- a/solution/0600-0699/0622.Design Circular Queue/Solution.rs +++ b/solution/0600-0699/0622.Design Circular Queue/Solution.rs @@ -1,22 +1,17 @@ struct MyCircularQueue { - queue: Vec, - left: usize, - right: usize, + q: Vec, + size: usize, capacity: usize, + front: usize, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MyCircularQueue { fn new(k: i32) -> Self { - let k = k as usize; - Self { - queue: vec![0; k], - left: 0, - right: 0, - capacity: k, + MyCircularQueue { + q: vec![0; k as usize], + size: 0, + capacity: k as usize, + front: 0, } } @@ -24,8 +19,9 @@ impl MyCircularQueue { if self.is_full() { return false; } - self.queue[self.right % self.capacity] = value; - self.right += 1; + let rear = (self.front + self.size) % self.capacity; + self.q[rear] = value; + self.size += 1; true } @@ -33,29 +29,33 @@ impl MyCircularQueue { if self.is_empty() { return false; } - self.left += 1; + self.front = (self.front + 1) % self.capacity; + self.size -= 1; true } fn front(&self) -> i32 { if self.is_empty() { - return -1; + -1 + } else { + self.q[self.front] } - self.queue[self.left % self.capacity] } fn rear(&self) -> i32 { if self.is_empty() { - return -1; + -1 + } else { + let rear = (self.front + self.size - 1) % self.capacity; + self.q[rear] } - self.queue[(self.right - 1) % self.capacity] } fn is_empty(&self) -> bool { - self.right - self.left == 0 + self.size == 0 } fn is_full(&self) -> bool { - self.right - self.left == self.capacity + self.size == self.capacity } } diff --git a/solution/0600-0699/0628.Maximum Product of Three Numbers/README.md b/solution/0600-0699/0628.Maximum Product of Three Numbers/README.md index 9a5241f911850..afd0c0716aaae 100644 --- a/solution/0600-0699/0628.Maximum Product of Three Numbers/README.md +++ b/solution/0600-0699/0628.Maximum Product of Three Numbers/README.md @@ -60,14 +60,14 @@ tags: ### 方法一:排序 + 分类讨论 -我们先对数组 $nums$ 进行排序,接下来分两种情况讨论: +我们先对数组 $\textit{nums}$ 进行排序,接下来分两种情况讨论: -- 如果 $nums$ 中全是非负数或者全是非正数,那么答案即为最后三个数的乘积,即 $nums[n-1] \times nums[n-2] \times nums[n-3]$; -- 如果 $nums$ 中既有正数也有负数,那么答案可能是两个最小负数和一个最大整数的乘积,即 $nums[n-1] \times nums[0] \times nums[1]$;也可能是最后三个数的乘积,即 $nums[n-1] \times nums[n-2] \times nums[n-3]$。 +- 如果 $\textit{nums}$ 中全是非负数或者全是非正数,那么答案即为最后三个数的乘积,即 $\textit{nums}[n-1] \times \textit{nums}[n-2] \times \textit{nums}[n-3]$; +- 如果 $\textit{nums}$ 中既有正数也有负数,那么答案可能是两个最小负数和一个最大整数的乘积,即 $\textit{nums}[n-1] \times \textit{nums}[0] \times \textit{nums}[1]$;也可能是最后三个数的乘积,即 $\textit{nums}[n-1] \times \textit{nums}[n-2] \times \textit{nums}[n-3]$。 最后返回两种情况的最大值即可。 -时间复杂度 $O(n \times \log n)$,空间复杂度 $O(\log n)$。其中 $n$ 为数组 $nums$ 的长度。 +时间复杂度 $O(n \times \log n)$,空间复杂度 $O(\log n)$。其中 $n$ 为数组 $\textit{nums}$ 的长度。 @@ -146,11 +146,11 @@ function maximumProduct(nums: number[]): number { ### 方法二:一次遍历 -我们可以不用对数组进行排序,而是维护五个变量,其中 $mi1$ 和 $mi2$ 表示数组中最小的两个数,而 $mx1$、$mx2$ 和 $mx3$ 表示数组中最大的三个数。 +我们可以不用对数组进行排序,而是维护五个变量,其中 $\textit{mi1}$ 和 $\textit{mi2}$ 表示数组中最小的两个数,而 $\textit{mx1}$, $\textit{mx2}$ 和 $\textit{mx3}$ 表示数组中最大的三个数。 -最后返回 $max(mi1 \times mi2 \times mx1, mx1 \times mx2 \times mx3)$ 即可。 +最后返回 $\max(\textit{mi1} \times \textit{mi2} \times \textit{mx1}, \textit{mx1} \times \textit{mx2} \times \textit{mx3})$ 即可。 -时间复杂度 $O(n)$,空间复杂度 $O(1)$。 +时间复杂度 $O(n)$,其中 $n$ 为数组长度。空间复杂度 $O(1)$。 diff --git a/solution/0600-0699/0628.Maximum Product of Three Numbers/README_EN.md b/solution/0600-0699/0628.Maximum Product of Three Numbers/README_EN.md index 1dc90552924b4..3f311ceb04c5e 100644 --- a/solution/0600-0699/0628.Maximum Product of Three Numbers/README_EN.md +++ b/solution/0600-0699/0628.Maximum Product of Three Numbers/README_EN.md @@ -45,7 +45,16 @@ tags: -### Solution 1 +### Solution 1: Sorting + Case Analysis + +First, we sort the array $\textit{nums}$, and then discuss two cases: + +- If $\textit{nums}$ contains all non-negative or all non-positive numbers, the answer is the product of the last three numbers, i.e., $\textit{nums}[n-1] \times \textit{nums}[n-2] \times \textit{nums}[n-3]$; +- If $\textit{nums}$ contains both positive and negative numbers, the answer could be the product of the two smallest negative numbers and the largest positive number, i.e., $\textit{nums}[n-1] \times \textit{nums}[0] \times \textit{nums}[1]$, or the product of the last three numbers, i.e., $\textit{nums}[n-1] \times \textit{nums}[n-2] \times \textit{nums}[n-3]$. + +Finally, return the maximum of the two cases. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(\log n)$. Here, $n$ is the length of the array $\textit{nums}$. @@ -122,7 +131,13 @@ function maximumProduct(nums: number[]): number { -### Solution 2 +### Solution 2: Single Pass + +We can avoid sorting the array by maintaining five variables: $\textit{mi1}$ and $\textit{mi2}$ represent the two smallest numbers in the array, while $\textit{mx1}$, $\textit{mx2}$, and $\textit{mx3}$ represent the three largest numbers in the array. + +Finally, return $\max(\textit{mi1} \times \textit{mi2} \times \textit{mx1}, \textit{mx1} \times \textit{mx2} \times \textit{mx3})$. + +The time complexity is $O(n)$, where $n$ is the length of the array. The space complexity is $O(1)$. diff --git a/solution/0600-0699/0629.K Inverse Pairs Array/README.md b/solution/0600-0699/0629.K Inverse Pairs Array/README.md index 42c298d7b8c6e..24ad73b586825 100644 --- a/solution/0600-0699/0629.K Inverse Pairs Array/README.md +++ b/solution/0600-0699/0629.K Inverse Pairs Array/README.md @@ -170,9 +170,9 @@ func kInversePairs(n int, k int) int { ```ts function kInversePairs(n: number, k: number): number { - const f: number[] = new Array(k + 1).fill(0); + const f: number[] = Array(k + 1).fill(0); f[0] = 1; - const s: number[] = new Array(k + 2).fill(1); + const s: number[] = Array(k + 2).fill(1); s[0] = 0; const mod: number = 1e9 + 7; for (let i = 1; i <= n; ++i) { diff --git a/solution/0600-0699/0629.K Inverse Pairs Array/README_EN.md b/solution/0600-0699/0629.K Inverse Pairs Array/README_EN.md index c8eec2c12a9bb..06cd8bc5e4448 100644 --- a/solution/0600-0699/0629.K Inverse Pairs Array/README_EN.md +++ b/solution/0600-0699/0629.K Inverse Pairs Array/README_EN.md @@ -51,7 +51,25 @@ tags: -### Solution 1 +### Solution 1: Dynamic Programming + Prefix Sum + +We define $f[i][j]$ as the number of arrays of length $i$ with $j$ inverse pairs. Initially, $f[0][0] = 1$, and the rest $f[i][j] = 0$. + +Next, we consider how to obtain $f[i][j]$. + +Assume the first $i-1$ numbers are already determined, and now we need to insert the number $i$. We discuss the cases of inserting $i$ into each position: + +- If $i$ is inserted into the 1st position, the number of inverse pairs increases by $i-1$, so $f[i][j] += f[i-1][j-(i-1)]$. +- If $i$ is inserted into the 2nd position, the number of inverse pairs increases by $i-2$, so $f[i][j] += f[i-1][j-(i-2)]$. +- ... +- If $i$ is inserted into the $(i-1)$th position, the number of inverse pairs increases by 1, so $f[i][j] += f[i-1][j-1]$. +- If $i$ is inserted into the $i$th position, the number of inverse pairs does not change, so $f[i][j] += f[i-1][j]$. + +Therefore, $f[i][j] = \sum_{k=1}^{i} f[i-1][j-(i-k)]$. + +We notice that the calculation of $f[i][j]$ actually involves prefix sums, so we can use prefix sums to optimize the calculation process. Moreover, since $f[i][j]$ only depends on $f[i-1][j]$, we can use a one-dimensional array to optimize the space complexity. + +The time complexity is $O(n \times k)$, and the space complexity is $O(k)$. Here, $n$ and $k$ are the array length and the number of inverse pairs, respectively. @@ -148,9 +166,9 @@ func kInversePairs(n int, k int) int { ```ts function kInversePairs(n: number, k: number): number { - const f: number[] = new Array(k + 1).fill(0); + const f: number[] = Array(k + 1).fill(0); f[0] = 1; - const s: number[] = new Array(k + 2).fill(1); + const s: number[] = Array(k + 2).fill(1); s[0] = 0; const mod: number = 1e9 + 7; for (let i = 1; i <= n; ++i) { diff --git a/solution/0600-0699/0629.K Inverse Pairs Array/Solution.ts b/solution/0600-0699/0629.K Inverse Pairs Array/Solution.ts index b9b11bacc37cb..67fc79b55b58f 100644 --- a/solution/0600-0699/0629.K Inverse Pairs Array/Solution.ts +++ b/solution/0600-0699/0629.K Inverse Pairs Array/Solution.ts @@ -1,7 +1,7 @@ function kInversePairs(n: number, k: number): number { - const f: number[] = new Array(k + 1).fill(0); + const f: number[] = Array(k + 1).fill(0); f[0] = 1; - const s: number[] = new Array(k + 2).fill(1); + const s: number[] = Array(k + 2).fill(1); s[0] = 0; const mod: number = 1e9 + 7; for (let i = 1; i <= n; ++i) {