給定陣列 nums 與值 val,原地移除所有等於 val 的元素,返回新長度。元素順序可改變,超出新長度部分內容不限。
思路:left 從前找目標值,right 從後找非目標值,交換並縮小區間。
優點:交換次數最少(最多 min(目標個數, 非目標個數) 次)。
缺點:順序完全打亂。
int removeElement(vector& nums, int val) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
if (nums[left] != val) ++left;
else if (nums[right] == val) --right;
else nums[left++] = nums[right--]; // 覆蓋而非 swap,因右側將被捨棄
}
return right + 1; // 或 left
}
思路:維護有效長度 n,i 從 0 掃描。遇到目標值,用尾端元素 nums[--n] 覆蓋 nums[i],i 不動(因新覆蓋元素未檢查)。
優點:程式碼極簡,無需雙指針。
缺點:若尾端連續是目標值,會重複覆蓋同位置。
int removeElement(vector& nums, int val) {
int n = nums.size();
for (int i = 0; i < n; ) {
if (nums[i] == val) nums[i] = nums[--n];
else ++i;
}
return n;
}
思路:slow 指向下一個放置非目標值的位置,fast 掃描全陣列。非目標值複製到 slow 並 slow++。
優點:保持相對順序。
缺點:需移動所有非目標元素,若目標在開頭則全陣列搬移。
int removeElement(vector& nums, int val) {
int slow = 0;
for (int fast = 0; fast < nums.size(); ++fast)
if (nums[fast] != val) nums[slow++] = nums[fast];
return slow;
}
| 指標 | 方案一 雙指針 | 方案二 單指針縮減 | 方案三 快慢指針 |
|---|---|---|---|
| 時間複雜度 | O(n) | O(n) | O(n) |
| 寫入次數 | 最少(≤ n/2) | 中等 | 最多(n - 目標數) |
| 保持順序 | 否 | 否 | 是 |
| 程式碼簡潔度 | 中 | 最高 | 高 |
// Java 方案二
public int removeElement(int[] nums, int val) {
int n = nums.length;
for (int i = 0; i < n; ) {
if (nums[i] == val) nums[i] = nums[--n];
else i++;
}
return n;
}
# Python 方案三(列表推導式最簡潔,但非原地)
def removeElement(nums, val):
slow = 0
for fast in range(len(nums)):
if nums[fast] != val:
nums[slow] = nums[fast]
slow += 1
return slow