|
| 1 | +# 3354. Make Array Elements Equal to Zero |
| 2 | + |
| 3 | +You are given an integer array `nums`. |
| 4 | + |
| 5 | +Start by selecting a starting position `curr` such that `nums[curr] == 0`, and choose a movement direction of either left or right. |
| 6 | + |
| 7 | +After that, you repeat the following process: |
| 8 | + |
| 9 | +- If `curr` is out of the `range [0, n - 1]`, this process ends. |
| 10 | +- If `nums[curr] == 0`, move in the current direction by incrementing `curr` if you are moving right, or decrementing `curr` if you are moving left. |
| 11 | +- Else if `nums[curr] > 0`: |
| 12 | + - Decrement `nums[curr]` by 1. |
| 13 | + - Reverse your movement direction (left becomes right and vice versa). |
| 14 | + - Take a step in your new direction. |
| 15 | +A selection of the initial position `curr` and movement direction is considered valid if every element in `nums` becomes 0 by the end of the process. |
| 16 | + |
| 17 | +Return the number of possible valid selections. |
| 18 | + |
| 19 | +**Constraints:** |
| 20 | + |
| 21 | +- `1 <= nums.length <= 100` |
| 22 | +- `0 <= nums[i] <= 100` |
| 23 | +- There is at least one element `i` where `nums[i] == 0`. |
| 24 | + |
| 25 | +## 基礎思路 |
| 26 | + |
| 27 | +本題要求我們判斷從哪些起始位置與方向出發,最終能使陣列 `nums` 的所有元素都歸零。 |
| 28 | + |
| 29 | +整個模擬過程如下: |
| 30 | + |
| 31 | +1. 選擇一個初始位置 `curr`,且該位置的值必須為 `0`。 |
| 32 | +2. 選擇移動方向(向左或向右)。 |
| 33 | +3. 不斷重複以下動作直到 `curr` 超出邊界: |
| 34 | + - 若 `nums[curr] == 0`,則在當前方向上前進一步; |
| 35 | + - 若 `nums[curr] > 0`,則先將該值減一、反轉移動方向,再前進一步。 |
| 36 | + |
| 37 | +我們要找出所有「能使最終所有元素變為 0」的初始 `(curr, direction)` 配置。 |
| 38 | + |
| 39 | +在理解過程後,可以發現直接模擬所有情況的成本太高(每次都會改變陣列內容)。因此必須尋找**規律性條件**: |
| 40 | + |
| 41 | +- 假設整體數組中有 `totalSum` 代表所有元素的總和; |
| 42 | +- 當我們從左側移動到右側時,左側累積的和(`leftSum`)逐漸增加; |
| 43 | +- 若以 `i` 為起點,則左側的能量與右側的能量差應該平衡,否則不可能讓整體歸零。 |
| 44 | + |
| 45 | +於是可以導出條件: |
| 46 | + |
| 47 | +- 當 `nums[i] == 0` 時,計算 `difference = |2 * leftSum - totalSum|`; |
| 48 | +- 若差值為 `0`,代表左右能量平衡,可以往**左右兩邊**出發; |
| 49 | +- 若差值為 `1`,代表左右能量相差一單位,只能往其中一側出發; |
| 50 | +- 其他情況無法平衡。 |
| 51 | + |
| 52 | +藉此即可在單次線性掃描中,統計所有合法起始選項。 |
| 53 | + |
| 54 | +## 解題步驟 |
| 55 | + |
| 56 | +### Step 1:初始化變數與計算總和 |
| 57 | + |
| 58 | +首先遍歷整個陣列,計算 `totalSum`(代表所有元素總量),並建立一個變數 `leftSum` 來累積左側部分的和。 |
| 59 | + |
| 60 | +```typescript |
| 61 | +// 取得陣列長度 |
| 62 | +const length = nums.length; |
| 63 | + |
| 64 | +// 計算所有元素總和 |
| 65 | +let totalSum = 0; |
| 66 | +for (let i = 0; i < length; i++) { |
| 67 | + totalSum += nums[i]; |
| 68 | +} |
| 69 | + |
| 70 | +// 統計合法選擇數量 |
| 71 | +let totalValidSelections = 0; |
| 72 | + |
| 73 | +// 累計左側前綴和 |
| 74 | +let leftSum = 0; |
| 75 | +``` |
| 76 | + |
| 77 | +### Step 2:逐一檢查每個可能起始點 |
| 78 | + |
| 79 | +僅在 `nums[i] == 0` 的情況下檢查,因為題目明確要求初始位置必須為零。 |
| 80 | + |
| 81 | +```typescript |
| 82 | +for (let i = 0; i < length; i++) { |
| 83 | + // 只考慮數值為 0 的位置作為起點 |
| 84 | + if (nums[i] === 0) { |
| 85 | + // 計算左右區域能量差異 |
| 86 | + const difference = Math.abs(2 * leftSum - totalSum); |
| 87 | + |
| 88 | + // 若差值 <= 1,則可能為合法起始組合 |
| 89 | + // 差值 = 0 → 可向左右兩邊出發 (加 2) |
| 90 | + // 差值 = 1 → 只可向一側出發 (加 1) |
| 91 | + if (difference <= 1) { |
| 92 | + totalValidSelections += 2 - difference; |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + // 每輪更新左側和,為下一次迭代做準備 |
| 97 | + leftSum += nums[i]; |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +### Step 3:返回最終結果 |
| 102 | + |
| 103 | +掃描結束後,所有可能起始組合的數量即為答案。 |
| 104 | + |
| 105 | +```typescript |
| 106 | +// 回傳總合法起始配置數量 |
| 107 | +return totalValidSelections; |
| 108 | +``` |
| 109 | + |
| 110 | +## 時間複雜度 |
| 111 | + |
| 112 | +- 一次遍歷求總和為 $O(n)$; |
| 113 | +- 再次線性掃描以檢查所有位置,亦為 $O(n)$; |
| 114 | +- 所有操作皆為常數級運算。 |
| 115 | +- 總時間複雜度為 $O(n)$。 |
| 116 | + |
| 117 | +> $O(n)$ |
| 118 | +
|
| 119 | +## 空間複雜度 |
| 120 | + |
| 121 | +- 只使用了常數級變數(`totalSum`, `leftSum`, `difference` 等); |
| 122 | +- 未使用額外陣列或輔助結構。 |
| 123 | +- 總空間複雜度為 $O(1)$。 |
| 124 | + |
| 125 | +> $O(1)$ |
0 commit comments