領釦網演算法學習筆記 -- 283
領釦網演算法學習筆記
本系列的演算法題目來自領釦網
陣列類演算法第一天
題目:
給定一個數組 nums
,編寫一個函式將所有 0
移動到陣列的末尾,同時保持非零元素的相對順序。
示例:
輸入: [0,1,0,3,12]
輸出: [1,3,12,0,0]
說明:
- 必須在原陣列上操作,不能拷貝額外的陣列。
- 儘量減少操作次數。
解題過程:
思路一:
看到這題的第一個思路是:檢測到0後,將陣列後面的數往前移,往陣列的末尾補0,這樣就確保在原陣列上操作,且不用拷貝額外的陣列。
程式碼如下:
class Solution {
public void moveZeroes(int[] nums) {
// 思路,從頭開始判斷是否是0,是的話,就將陣列後面的元素往前移一位,然後將0放置在最後。
int arraylength = nums.length; // 獲得陣列長度
int arrlength = arraylength; // 獲得迴圈次數
for(int i=0;i<arrlength;i++){
if(nums[i] == 0){ // 判斷當前值是否為0;
for (int j=i;j<arraylength-1;j++){ //將剩餘部分的資料往前移一位
nums[j]=nums[j+1];
}
nums[arraylength-1] = 0; // 將末位置0
i--;
arrlength--;
}
}
}
}
後續思考:
該程式碼在領釦上用時37ms
這樣做的話,陣列是隻用遍歷一次,但是陣列沒出現一個0,移動數組裡的元素的代價(次數)就很大,於是就思考能不能不移動,
思路二:
當出現0時我不移動,只是繼續往後讀,當往後讀遇到不是0的時候,將資料移到是0的位置,
於是我定義一個變數用來記錄0所在的位置,但是又遇到一個新問題,這個變數怎麼賦初值(就是起始0在哪),
這個問題其實很多餘,遍歷的資料不同,起始的0的位置也不同,沒辦法固定,所以我直接遍歷,當出現第一個0 的時候就設定這個值。然後繼續往下遍歷,不是0就與當前位置交換,當陣列遍歷完後,在之前的陣列長度與目前記錄0的位置的值的差值就是要補的0的個數,於是再補0就好。
程式碼如下:
class Solution {
public void moveZeroes(int[] nums) {
// 定義一個變數記錄當前為0的位置,然後查詢下一個資料是否為0,當資料不為0時,與當前資料互換位置
int i = 0; // 獲取一個迴圈變數,記錄當前迴圈到哪了
int locationnum = 0; // 暫時定義0位為第一個為0的位置
int arraylength = nums.length; // 獲得陣列長度
for(;i<arraylength;i++){ // 先讀取第一個0的位置
if(nums[i]==0){
locationnum = i;
break;
}
}
for(i++;i<arraylength;i++){ // 判斷接下來的數是否為0
if(nums[i]!=0){ // 如果不為0,則與當前的0的位置交換
nums[locationnum] = nums[i]; // 然後記錄0的位置往前移一位
locationnum++;
}
}
if(locationnum != 0){
for(;locationnum<arraylength;locationnum++){ // 將不是0的數全部往前挪了,剩下的就全是0
nums[locationnum] = 0;
}
}
}
}
// 用時2ms
領釦上面該題其他高質量範例:
class Solution {
public void moveZeroes(int[] nums) {
int j = 0 ;
for(int i=0;i<nums.length;i++){
if(nums[i]!=0){
int temp = nums[i];
nums[i] = nums[j];
nums[j++] = temp;
}
}
}
}
// 用時1ms
自我整理:這部分程式碼精簡,我看第一遍直接暈了,第二遍才反應過來,思路是這樣的:
-
定義一個變數 j 用來迴圈賦值
-
迴圈陣列
-
判斷當前迴圈值是不是為0,不為0,就將當前值與迴圈變數位置的值進行交換,然後將迴圈變數往前移一位
-
如果是0,則跳過
這程式碼比我的優在不用去找第一個0 在哪,直接迴圈就OK了,
但是我覺得,如果一個較大的陣列,裡面0的數量較少,那麼這個演算法我感覺就會時間變長,不能算是最優的,因為沒一個不為0 的數,它都要執行額外的三條語句。畢竟剛入門演算法,它的複雜度我暫時還不會算,所以只是個人猜想。
class Solution { public void moveZeroes(int[] nums) { int idx = 0; for (int num : nums) { if (num != 0) { nums[idx++] = num; } } while (idx < nums.length) { nums[idx++] = 0; } } } // 用時2ms
自我整理:這個程式碼比我的要簡潔,我擦,看樣子我要優化我的程式碼了,這個思路很清晰:
-
定義一個變數用來記錄非 0 的數量
-
將不是0的資料儲存出來(這裡只是讀取不是0的變數,然後一直儲存,不是像我之前那樣後面的資料全部往前移)
-
將0補齊
-
class Solution {
public void moveZeroes(int[] nums) {
int count = 0;
int nonZeroIndex = 0;
for(int i = 0;i<nums.length;i++)
{
if(nums[i] == 0)
{
count++;
}
else{
nums[nonZeroIndex++] = nums[i];
}
}
for(int i = nums.length-count;i<nums.length;i++)
{
nums[i] = 0;
}
}
}
// 用時3ms
自我整理:這個演算法思路也很清晰:
-
定義兩個變數,一個記錄0的個數,一個記錄當前資料已經儲存到那個位置了
-
然後迴圈陣列,遇到0,記錄 0 的個數就加一,不是0,就儲存資料,記錄當前儲存位置的變數就+1
-
後面就再補齊0
自我總結:演算法我覺得精簡是一條優化的道路,但是精簡的程式碼不代表執行效率就高,但是我目前很明顯的缺點是:邏輯是怎樣,程式碼就怎樣寫,導致程式碼過長,這一點反應了我的邏輯思維還不夠好,還需要鍛鍊。