程式碼隨想錄演算法訓練營第一天 | 704. 二分查詢、 27. 移除元素、977.有序陣列的平方 (下)

apate_清行發表於2024-10-18

2-27.移除元素

給你一個陣列 nums 和一個值 val,你需要 原地 移除所有數值等於 val 的元素。元素的順序可能發生改變。然後返回 nums 中與 val 不同的元素的數量。

假設 nums 中不等於 val 的元素數量為 k,要透過此題,您需要執行以下操作:

  • 更改 nums 陣列,使 nums 的前 k 個元素包含不等於 val 的元素。nums 的其餘元素和 nums 的大小並不重要。
  • 返回 k

使用者評測:

評測機將使用以下程式碼測試您的解決方案:

int[] nums = [...]; // 輸入陣列
int val = ...; // 要移除的值
int[] expectedNums = [...]; // 長度正確的預期答案。
                            // 它以不等於 val 的值排序。

int k = removeElement(nums, val); // 呼叫你的實現

assert k == expectedNums.length;
sort(nums, 0, k); // 排序 nums 的前 k 個元素
for (int i = 0; i < actualLength; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有的斷言都透過,你的解決方案將會 透過

示例 1:

輸入:nums = [3,2,2,3], val = 3
輸出:2, nums = [2,2,_,_]
解釋:你的函式函式應該返回 k = 2, 並且 nums 中的前兩個元素均為 2。
你在返回的 k 個元素之外留下了什麼並不重要(因此它們並不計入評測)。

示例 2:

輸入:nums = [0,1,2,2,3,0,4,2], val = 2
輸出:5, nums = [0,1,4,0,3,_,_,_]
解釋:你的函式應該返回 k = 5,並且 nums 中的前五個元素為 0,0,1,3,4。
注意這五個元素可以任意順序返回。
你在返回的 k 個元素之外留下了什麼並不重要(因此它們並不計入評測)。

提示:

  • 0 <= nums.length <= 100
  • 0 <= nums[i] <= 50
  • 0 <= val <= 100

雙指標法:使用雙指標法的重點是理解兩個指標分別在做什麼。

程式碼隨想錄演算法訓練營第一天 | 704. 二分查詢、 27. 移除元素、977.有序陣列的平方 (下)

快指標的作用是遍歷陣列,來刪除掉特定的val值。慢指標的作用是判斷快指標指向的值是否是val並將非val的值儲存到目前所指向的位置上。當迴圈結束後,快指標會到陣列地址的末尾,但慢指標所指向的是我們所求陣列的末尾。

由於陣列的特性,在不考慮陣列後面地址的情況下已經完成了刪除操作。而此時慢指標的值就是新陣列的大小,且快指標-慢指標+1的值就是val值的數量。程式碼如下:

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //雙指標法
        int slowIndex = 0;
        for(int fastIndex = 0;fastIndex < nums.size(); fastIndex++){
            if(val != nums[fastIndex]){//fast先動,當fast指向的數字不為val時,就把fast指向的數字填入slow指向的位置。
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;//最後給出慢指標數值,也就是陣列大小即可
    }
};

3-977.有序陣列的平方

給你一個按 非遞減順序 排序的整數陣列 nums,返回 每個數字的平方 組成的新陣列,要求也按 非遞減順序 排序。

示例 1:

輸入:nums = [-4,-1,0,3,10]
輸出:[0,1,9,16,100]
解釋:平方後,陣列變為 [16,1,0,9,100]
排序後,陣列變為 [0,1,9,16,100]

示例 2:

輸入:nums = [-7,-3,2,3,11]
輸出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • nums 已按 非遞減順序 排序

進階:

  • 請你設計時間複雜度為 O(n) 的演算法解決本問題

由於暴力法的時間複雜度較高,根據卡哥給出的思路,嘗試雙指標法來解決問題,看了下卡哥的講解後自己嘗試寫了一下,經歷一些小改動之後也是成功ac。程式碼如下:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int flag = nums.size() - 1;//flag作為新陣列的指標,負責從後向前來賦值
        vector<int>result(nums.size(),0);//定義一個新陣列來儲存數值
            for(int i = 0,j = nums.size() - 1; i <= j;){//雙指標i和j
                if((nums[i]*nums[i]) >= (nums[j]*nums[j])){
                    result[flag] = (nums[i]*nums[i]);
                    flag--;//賦值後將flag減少,並且移動左指標
                    i++;
                }else if((nums[j]*nums[j]) > (nums[i]*nums[i])){
                    result[flag] = (nums[j]*nums[j]);
                    flag--;//賦值後將flag減少,並且移動右指標
                    j--;
                }
            }
        return result;//輸出結果即可
    }
};

這裡說一下我覺得比較重要的部分,首先是陣列從後向前賦值,打破了我以前的一些思維定式,這樣就會方便很多。其次,for迴圈中的最後一個語句是可以不寫的,將i++和j--寫進判斷裡面來進行迴圈,這是我之前沒考慮過的事情。

最開始寫的時候我定義了一個左指標一個右指標,然後迴圈裡面的i只是做為計數器使用,這其實會比較麻煩。

然後奇妙的地方是當左右相等的時候,其實如果前面寫大於,後面就可以直接else,因為左右相等時先放哪一個都一樣,寫大於會比我這個大於等於少打一個符號,還有就是else的問題,我這裡用了else if是因為我有點擔心會不會有其他情況導致出現問題。

但實際上和卡哥講的一樣,當小於等於的時候直接右指標賦值就可以了,也不存在其他情況,直接else可以少寫很多內容。

某些地方還是可以做一些簡化和改動,比如c++中特有的,可以把flag--寫進裡面。這裡寫進去後的含義是,執行這段語句結束後將flag--,於是有了第二版的改動程式碼:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int flag = nums.size() - 1;//flag作為新陣列的指標,負責從後向前來賦值
        vector<int>result(nums.size(),0);//定義一個新陣列來儲存數值
            for(int i = 0,j = nums.size() - 1; i <= j;){//雙指標i和j
                if((nums[i]*nums[i]) > (nums[j]*nums[j])){
                    result[flag--] = (nums[i]*nums[i]);//賦值後將flag減少,並且移動左指標
                    i++;
                }else{
                    result[flag--] = (nums[j]*nums[j]);//賦值後將flag減少,並且移動左指標
                    j--;
                }
            }
        return result;//輸出結果即可
    }
};

由於今天事情比較多,所以只是補上了昨天的內容,今天原本要做的內容我明天來補!一刷演算法還是希望能跟上節奏,進階題目先放一下,把每天的題目弄明白就算成功。

相關文章