在leetcode題庫上的移除元素問題
今天剛寫了一道leetcode上的題,覺得這題的解題思維真的是太棒了。
希望能分享給大家
先看題:
1.給你一個數組 nums和一個值 val,你需要 原地 移除所有數值等於val的元素,並返回移除後陣列的新長度。
不要使用額外的陣列空間,你必須僅使用 O(1) 額外空間並 原地 修改輸入陣列,限時間複雜度為O(n)。
元素的順序可以改變。你不需要考慮陣列中超出新長度後面的元素。
請在下面函式作答:
intremoveElement(int*nums,intnumsSize,intval) { //語句塊} 來源:力扣(LeetCode)連結:https://leetcode-cn.com/problems/remove-element
先說一下,這些刷題網大致是分兩種的,一種是直接給你一個函式介面,你只需要完成函式介面部分即可;(今天這題是這種)
另一種是什麼都沒有,你必須自己寫出完整的c程式碼,也就是從main函式開始寫,標頭檔案都要自己引用;
首先,我們來分析一下題目的意思:
- nums中可能有任意多的數,且不都相同,你需要在nums中找到一個或多個val的值,然後原地刪除;
- 不能用額外的陣列,如果可以使用的話,就有一種比較簡單的方法;
- 空間複雜度為O(1),時間複雜度O(n),就意味著你不能使用像順序表刪除元素一樣,找到val值,就開始刪除,刪除的過程就是將val值用後一個數往前覆蓋,如此下來時間複雜度就會有O(n2);
- 原地刪除舉個例子:在[ 1,2,1,5,4] 中刪掉所有的1,你可以刪成這樣[ 2,5,4,0,0] 或者[ 2,5,4,1,1]我們不需要關係4後面的數是多少,只要陣列前面是2,4,5的排列組合就算完成任務;
先介紹一下用額外陣列的方法,因為我們可以從這個方法過渡到一種非常神奇的思想:
圖一:自己建立一個數組,並個給定要刪除的值,並給出刪除方法
圖二:判斷元素,不是刪除的數,放入新建陣列中,依次判斷
圖三:當判斷到有刪除數時,不放入新建陣列
圖四:判斷完後,得到的新建陣列
總結:這個方法雖然時間複雜度是滿足要求O(n),但是空間複雜度不滿足,實際上空間複雜度也是O(n);
接下來就是非常神奇的思想的講解,如何即不建立陣列又把時間複雜度控制在O(n)內:
假設我們開始有兩個不同顏色的標記,nums陣列跟val值都跟上面假設一致
圖五:準備兩個指標均指向首地址
接下來就是這個思想的過程:
圖六:前三個都是資料不變,到了第四步,指向了被刪除的數,黃色不動,黑走
圖七:看圖就明白了圖八:看圖就明白了
至此,我們就完成了不用額外陣列也可以完成刪除我們所要選的資料,且都滿足了空間複雜度為O(1),時間複雜度O(n);
剛開始、現在刪除完、之前新建陣列刪完的對比:
圖九:對比結果
那問題又來了,那該如何解決訪問到倒數第二個5就停止訪問呢?
其實很簡單,記錄下每次黑色遇到val值 2就可以了
那就我們直接上程式碼吧
1 int removeElement(int* nums, int numsSize, int val) 2 { 3 int* p1 = nums;//黑色標記 4 int* p2 = nums;//黃色標記 5 int count = 0;//記錄下每次黑色遇到val值 6 while (p1 < nums + numsSize)//黑色指到最後一個元素的下一個元素就停下 7 { 8 if (*(p1) != val)//不是val,存入黃色標記處 9 { 10 *(p2) = *(p1); 11 p2++;//黃色標記向後加一 12 } 13 else//是val記錄 14 { 15 count++; 16 } 17 p1++;//黑色標記向後加一 18 } 19 return numsSize - count;//返回總大小 - val出現的次數 20 } 21 #include <stdio.h> 22 int main()//測試 23 { 24 int arr[] = { 3,1,6,3,5,3,7 }; 25 int sz = sizeof(arr) / sizeof(arr[0]); 26 int ret = removeElement(arr, sz, 3); 27 printf("%d ", ret); 28 return 0; 29 }
執行結果:
跟我們的預期結果一致;
(注:只要前四個是1 , 6 , 5 , 7 的排列組合就可以了)
總結:這個方法本質來講就是用雙指標來解決問題,如果想了解更多可以到leetcode的官網去看看更多大神的操作;
更新
2022-03-05
12:05:01