移除元素
題目
給你一個數組 nums 和一個值 val,你需要 【原地】 移除所有數值等於 val 的元素,並返回移除後數組的新長度。
不要使用額外的數組空間,你必須僅使用 O(1) 額外空間並 【原地】 修改輸入數組。
元素的順序可以改變。你不需要考慮數組中超出新長度後面的元素。
示例 1:
給定 nums = [3,2,2,3], val = 3,
函數應該返回新的長度 2, 並且 nums 中的前兩個元素均爲 2。
你不需要考慮數組中超出新長度後面的元素。
示例 2:
給定 nums = [0,1,2,2,3,0,4,2], val = 2,
函數應該返回新的長度 5, 並且 nums 中的前五個元素爲 0, 1, 3, 0, 4。
注意這五個元素可爲任意順序。
你不需要考慮數組中超出新長度後面的元素。
說明:
爲什麼返回數值是整數,但輸出的答案是數組呢?
請注意,輸入數組是以「引用」方式傳遞的,這意味着在函數裏修改輸入數組對於調用者是可見的。
你可以想象內部操作如下:
// nums 是以“引用”方式傳遞的。也就是說,不對實參作任何拷貝
int len = removeElement(nums, val);
// 在函數裏修改輸入數組對於調用者是可見的。
// 根據你的函數返回的長度, 它會打印出數組中 該長度範圍內 的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}
函數原型
C的函數原型:
int removeElement(int* nums, int numsSize, int val){}
邊界判斷
int removeElement(int* nums, int numsSize, int val){
if( nums == NULL || numsSize == 0 )
return 0;
}
算法設計:雙指針
數據結構的、數組的、刪除操作,主要是元素前移如何設計呢?
啊哈,雙指針。
用一個指針遍歷數組,如果元素不等於 val
,那就用另一個指針重新爲數組一一賦值。
int removeElement(int* nums, int numsSize, int val){
if( nums == NULL || numsSize == 0 )
return 0;
int cnt = 0;
for(int i=0; i<numsSize; i++)
{
if( nums[i] != val ){
nums[cnt] = nums[i];
cnt++;
}
}
return cnt;
}
雙指針的複雜度:
- 時間複雜度:
- 空間複雜度:
算法設計:特殊情況處理
有種特殊情況:,那所有的元素都得前移 位。
這時其實可以直接讓最後一個元素佔第一個元素的位置,避免前移。
int removeElement(int* nums, int numsSize, int val){
if( nums == NULL || numsSize == 0 )
return 0;
int i = 0;
while(i < numsSize ){
if( nums[i] == val ){
nums[i] = nums[numsSize-1]; // 讓最後一個元素佔第一個元素的位置
numsSize --; // 最後一個元素已經有替身了,移除最後位置的元素
}else{
i ++;
}
}
return numsSize;
}
類似在一些垃圾回收算法裏,刪除數據,通常不是單個單個的刪除,而是批處理刪除。
在生活中,我們扔東西也不是直接給垃圾回收站,而是先扔進屋裏垃圾桶的垃圾,其實垃圾並沒有真的被扔掉,只有垃圾桶塞滿時,纔會清理垃圾桶,那時候垃圾才真正離開了我們家裏,徹底消失了。
思路:先記錄下已經刪除的數據。每次的刪除操作並不是真正地搬移數據,只是記錄數據已經被刪除。當數組沒有更多空間存儲數據時,我們再觸發執行一次真正的刪除操作,這樣就大大減少了刪除操作導致的數據搬移。