程式碼隨想錄演算法訓練營第一天 | 陣列part01:陣列理論基礎,704. 二分查詢,27. 移除元素 977.有序陣列的平方

zzzzzzzhs發表於2024-08-28

陣列理論基礎

陣列是存放在連續記憶體空間上的相同型別資料的集合

陣列徐璈注意的是:

  • 陣列的下標都是從0開始的
  • 陣列記憶體空間是的地址是連續的
    正因為舒適的記憶體空間是連續的,所以在刪除和增添元素的時候,需要移動其他元素的地址。

在c++中,vector的底層實現是array,嚴格來說,vector是容器,不是陣列。

陣列的元素不能刪除,只能被覆蓋。

704 二分查詢
題目連結:https://leetcode.cn/problems/binary-search/

給定一個 n 個元素有序的(升序)整型陣列 nums 和一個目標值 target ,寫一個函式搜尋 nums 中的 target,如果目標值存在返回下標,否則返回 -1。

示例 1:

輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中並且下標為 4
示例 2:

輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1

之前做過,但是現在一看還是很懵,沒有一個清晰的解題思路。先看以便隨想錄上的解題思路,自己實現,總結收穫。

解題思路:
首先需要看題的條件:有序陣列,陣列中無重複元素。如果是包含重複元素的陣列,使用二分法查詢返回元素下標可能不是唯一的。

區間的定義兩種,左閉右閉即[left, right],或者左閉右開即[left, right)。
第一種解法 左閉右閉:
[left, right]區間注意:

  • while (left <= right) 要使用 <= ,因為left == right是有意義的,所以使用 <=
  • if (nums[middle] > target) right 要賦值為 middle - 1,因為當前這個nums[middle]一定不是target,那麼接下來要查詢的左區間結束下標位置就是 middle - 1

自己按照思路寫出的程式碼

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = **nums.size()**-1;
        while(left <= right){
            //int middle = left + ((right - left) / 2);// 防止溢位 等同於(left + right)/2 這裡不是很懂。
            **int mid = (right + left)/2;**
            if(nums[mid] > target) {
                **right = mid;** //這裡應該是right=mid-1,因為如果當前mid對應的值大於目標值,應該在[left,mid-1]區間繼續找,因為是雙閉的區間。
            }else if(nums[mid] < target){
                **left = mid;** //所以這裡也就是left = mid+1
            }else return mid;                    
        }
        return -1;
    }
};

總結:
vector的size .size()
為什麼要用left + ((right - left) / 2):
在計算中間值時,常見的表示式是 int middle = (left + right) / 2;。然而,當 left 和 right 是非常大的整數時,它們的和 left + right 可能會超過整數的最大值,從而導致溢位錯誤。此時,結果將是不正確的,可能會引發程式錯誤或崩潰。

第二種解法:左閉右開[left, right)
左閉右開時,僅需注意while(left < right) 兩個不能相等
if(nums[mid] > target) right = mid; 即可

27.移除元素
題目連結:https://leetcode.cn/problems/remove-element/description/

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

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

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

題目分析:
原地:不使用額外的陣列空間,在原地修改輸入陣列並在O(1)額外空間的條件下完成。
元素順序可以改變,不需要考慮陣列中超出新長度後面的元素。

暴力解法
思路:依次遍歷陣列中的所有元素,若等於val,size--,否則繼續遍歷。這時如何改變nums陣列的值?即遇到值=val如何讓陣列後邊的值覆蓋前邊的值。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for(int i = 0;i < size;i++){
            if(nums[i] == val){
                **for(int j = i+1; j < size; j++){ //遇到陣列中值=val,讓後一個陣列值覆蓋當前值**
**                    nums[j-1] = nums[j]; **
                }
            **i--;// i--是當前值被後值覆蓋,所以是新值,在下一輪迴圈還需要針對當前的i值做判斷**
            size--;
            }
        }
        return size;
    }
};

重點 雙指標法:
我的印象是設定一個fast一個slow指標。一個用於遍歷陣列,一個用於指示刪除元素後陣列的下標。
看完程式碼隨想錄後:
遇到陣列值=val,fast繼續向後遍歷,slow不變,之後依次覆蓋前面陣列數值。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        int slow = 0;
        for(int fast = 0; fast < size; fast++){
            //錯誤寫法 這裡我想錯了,總想等於val的時候應該做什麼操作。
            // if(nums[fast] == val){
            //     fast++;
            //     nums[fast-1] = nums[fast];
            // }
            // else slow++;
            //只需要nums[fast] != val,才去給nums[slow]賦值就可以了。
            if(nums[fast] != val)
                nums[slow++] = nums[fast];
        }
        return slow;
    }
};

總結:
暴力解法注意雙層遍歷讓nums陣列後值覆蓋前值。
雙指標法注意只有nums[fast] != val,再去給nums[slow]賦值。

977.有序陣列的平方
題目連結:https://leetcode.cn/problems/squares-of-a-sorted-array/

給你一個按 非遞減順序 排序的整數陣列 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]

題目分析
陣列是非遞減,輸出的新陣列要求也按照非遞減排序。
我的思路
這裡給每一個陣列元素平方在返回比較簡單,但是如何返回的陣列元素也按照非遞減的順序排列,沒有太好的思路。依舊是雙指標法,雙指標怎麼用?
看完隨想錄後
其實挺簡單,讓兩個指標分別指向頭和尾,定義一個新陣列,兩指標依次向中間遍歷,看平方值的大小來判斷哪個先放入新陣列。

我的錯誤解法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result;
        int i = 0, j = nums.size()-1;
        while(i <= j){
            //應該預先分配好result的值,再向陣列加元素,這樣減少頻繁push_back
            //這個問題很大,我想的是如果i的nums值<j的nums值,就先向陣列加入i的值,再加入j的值,這樣是不對的。
            if(nums[i] * nums[i] < nums[j] * nums[j]){
                result.push_back(nums[i] * nums[i]);
                result.push_back(nums[j] * nums[j]);
            }
            if(nums[i] * nums[i] == nums[j] * nums[j]){
                result.push_back(nums[i] * nums[i]);
                result.push_back(nums[i] * nums[i]);
            }
            i++;
            j--;
        }
        return result;
    }
};

正確解法:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result(nums.size(),0);
        int i = 0, j = nums.size()-1;
        //定義一個新的全域性k值,用於新陣列的下標值
        int k = j;
        while(i <= j){
            // j--和i++只需要在其中nums[i]和nums[j]加入了新陣列才用
            if(nums[i] * nums[i] < nums[j] * nums[j]){
                result[k--] = nums[j] * nums[j];
                j--;
            }else{
                result[k--] = nums[i] * nums[i];
                i++;
            }
        }
        return result;
    }
};

總結
我覺得最大的問題就是我總想著雙指標i和j一起+或者-,這樣是不對的,當滿足條件僅改變其中一個就可以。

相關文章