Leetcode 283:Move Zeroes
Leetcode 283:Move Zeroes
Given an array
nums
, write a function to move all0's
to the end of it while maintaining the relative order of the non-zero elements.
幾個要點:
- 將 0 全部移動到陣列後面
- 不打算非 0 元素的順序
[法1] 暴力法
思路
① 新建一個相同大小的陣列 result,陣列初始化的時候所有元素本身就預設為 0;
② 遍歷原陣列 nums,將不為 0 的元素依次放入 result 中;
③ 複製 result 陣列到 nums 陣列
程式碼
class Solution {
public void moveZeroes(int[] nums) {
//臨時陣列
int[] result = new int[nums.length];
//臨時陣列索引
int j = 0;
//遍歷原陣列
for(int i = 0;i < nums.length; i++){
//找到非0的元素
if(nums[i] != 0){
//複製給臨時陣列 result
result[j] = nums[i];
//迭代 result 陣列的索引
j++;
}
}
//複製 result 到 nums
for(int i = 0;i<nums.length;i++){
nums[i] = result[i];
}
}
}
提交結果
程式碼分析
- 時間複雜度:程式碼中只有一層迴圈(雖然有2次迴圈,不過不是巢狀的),很明顯是 O(n)。
- 空間複雜度:我們使用了一個臨時陣列,所以空間複雜度是 O(n)。
改進思路
從在 Leetcode
上的提交結果可以看出我們當前演算法比較大的問題就是記憶體消耗比較大,而我們記憶體消耗大的原因就是我們使用了一個臨時陣列,使得我們的空間複雜度為 O(n),故而改進思路的方式就是想辦法不借用臨時陣列,直接在原陣列上操作
。
[法2] 不用輔助空間
思路
仔細思考一下,其實這裡輔助空間是完全沒有必要的。
我們只需要在遇到非 0 元素的時候,直接把它賦值到前面就可以了。
而且由於 非 0 元素的個數一定是小於或等於整個陣列的元素個數的,所以在賦值到前面的時候,我們不用擔心這個賦值會覆蓋掉任何的資料。
整體思路如下:
① 遍歷 nums,將非 0 元素複製到前面
② 把後面的元素全部置為 0
程式碼
class Solution {
public void moveZeroes(int[] nums) {
//非 0 元素的索引,[0...j) 的元素為非0元素
int j = 0;
//遍歷原陣列
for(int i = 0;i < nums.length; i++){
//找到非0的元素
if(nums[i] != 0){
//將非0元素移動到前面
nums[j] = nums[i];
//迭代索引
j++;
}
}
//後面全部置為0
for(int i = j;i<nums.length;i++){
nums[i] = 0;
}
}
}
提交結果
程式碼分析
- 時間複雜度:O(n)
- 空間複雜度:O(1)
改進思路
前面我們改進了空間複雜度。我們再看看程式碼的話會發現我們這裡使用了 2 個步驟:複製非0元素和置0後面的元素。
一個改進思路就是:能不能在複製非0元素的同時將後面的元素置為0
。
[法3] 一步到位
思路
要做到在複製非0元素的同時將後面的元素置為0,很明顯思路就是:將非0元素和0元素進行交換
。
整體思路如下:
① 建立 2 個索引,一個是非0元素索引j
,一個是陣列遍歷索引 i
② 遍歷到陣列第 i 個元素後,保證 [0…i] 中所有非0元素都按順序排列在 [0…j) 當中,同時要保證 [j…i] 為0
程式碼
class Solution {
public void moveZeroes(int[] nums) {
//nums 中[0....k)的元素為非0元素
int j = 0;
//遍歷陣列
for(int i = 0;i < nums.length; i++){
//找到非0的元素
if(nums[i] != 0){
//保證[0...i]中所有非0元素按順序排列在[0...j)中
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
//迭代索引,保證 [j..i] 全為0
j++;
}
}
}
}
提交結果
程式碼分析
- 時間複雜度:O(n)
- 空間複雜度:O(1)
改進思路
當前程式碼我們是將非0元素和0元素進行交換,做到一步到位。但是就有一個問題了:非0元素經常會進行自己跟自己進行交換
。舉一個簡單的例子就是所有元素都是非0的,那麼這個情況就非常明顯了,所以改進思路就是:避免自身交換
。
[法4] 避免自身交換
思路
想一下什麼情況會進行自身交換,其實就是當 i = j
的時候,這個時候程式“誤”認為 [j…i] 都為0,所以進行了自身交換。因此我們只需要加一個判斷:當 i ≠ j 的時候,才進行交換
。
程式碼
class Solution {
public void moveZeroes(int[] nums) {
//nums 中[0....k)的元素為非0元素
int j = 0;
//遍歷陣列
for(int i = 0;i < nums.length; i++){
//找到非0的元素
if(nums[i] != 0){
if(i != j){
//保證[0...i]中所有非0元素按順序排列在[0...j)中
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
//迭代索引,保證 [j..i] 全為0
j++;
}
}
}
}