banner
darkan

darkan

配列

二分探索#

第一種書寫法(左閉右閉)#

//第一種書寫法
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定義target在左閉右閉的區間裡,[left, right]
        while (left <= right) { // 當left==right,區間[left, right]依然有效,所以用 <=
            int middle = left + ((right - left) / 2);// 防止溢出 等同於(left + right)/2
            if (nums[middle] > target) {
                right = middle - 1; // target 在左區間,所以[left, middle - 1]
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右區間,所以[middle + 1, right]
            } else { // nums[middle] == target
                return middle; // 陣列中找到目標值,直接返回下標
            }
        }
        // 未找到目標值
        return -1;
    }
};

第二種書寫法(左閉右開)#

// 版本二
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定義target在左閉右開的區間裡,即:[left, right)
        while (left < right) { // 因為left == right的時候,在[left, right)是無效的空間,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左區間,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右區間,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 陣列中找到目標值,直接返回下標
            }
        }
        // 未找到目標值
        return -1;
    }
};

雙指針法#

移除元素#

雙指針法 (快慢指針法): 透過一個快指針和慢指針在一個 for 循環下完成兩個 for 循環的工作。
定義快慢指針:

  • 快指針:尋找新陣列的元素,新陣列就是不含有目標元素的陣列
  • 慢指針:指向更新 新陣列下標的位置
// 時間複雜度:O(n)
// 空間複雜度:O(1)
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]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

有序陣列的平方#

class Solution {
public:
    vector<int> sortedSquares(vector<int>& A) {
        int k = A.size() - 1;
        vector<int> result(A.size(), 0);
        for (int i = 0, j = A.size() - 1; i <= j;) { // 注意這裡要i <= j,因為最後要處理兩個元素
            if (A[i] * A[i] < A[j] * A[j])  {
                result[k--] = A[j] * A[j];
                j--;
            }
            else {
                result[k--] = A[i] * A[i];
                i++;
            }
        }
        return result;
    }
};

滑動窗口#

所謂滑動窗口,就是不斷的調節子序列的起始位置和終止位置,從而得出我們想要的結果
窗口就是滿足其和 ≥ s 的長度最小的 連續子陣列。
窗口的起始位置如何移動:如果當前窗口的值大於等於 s 了,窗口就要向前移動了(也就是該縮小了)。
窗口的結束位置如何移動:窗口的結束位置就是遍歷陣列的指針,也就是 for 循環裡的索引。
image.png|450
滑動窗口的精妙之處在於根據當前子序列和大小的情況,不斷調節子序列的起始位置。從而將 O (n^2) 暴力解法降為 O (n)。

長度最小的子陣列#

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑動窗口數值之和
        int i = 0; // 滑動窗口起始位置
        int subLength = 0; // 滑動窗口的長度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意這裡使用while,每次更新 i(起始位置),並不斷比較子序列是否符合條件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的長度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 這裡體現出滑動窗口的精髓之處,不斷變更i(子序列的起始位置)
            }
        }
        // 如果result沒有被賦值的話,就返回0,說明沒有符合條件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

螺旋矩陣 2#

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定義一個二維陣列
        int startx = 0, starty = 0; // 定義每循環一圈的起始位置
        int loop = n / 2; // 每個圈循環幾次,例如n為奇數3,那麼loop = 1 只是循環一圈,矩陣中間的值需要單獨處理
        int mid = n / 2; // 矩陣中間的位置,例如:n為3, 中間的位置就是(1,1),n為5,中間位置為(2, 2)
        int count = 1; // 用來給矩陣中每一個空格賦值
        int offset = 1; // 需要控制每一條邊遍歷的長度,每次循環右邊界收縮一位
        int i,j;
        while (loop --) {
            i = startx;
            j = starty;

            // 下面開始的四個for就是模擬轉了一圈
            // 模擬填充上行從左到右(左閉右開)
            for (j; j < n - offset; j++) {
                res[i][j] = count++;
            }
            // 模擬填充右列從上到下(左閉右開)
            for (i; i < n - offset; i++) {
                res[i][j] = count++;
            }
            // 模擬填充下行從右到左(左閉右開)
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            // 模擬填充左列從下到上(左閉右開)
            for (; i > startx; i--) {
                res[i][j] = count++;
            }

            // 第二圈開始的時候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
            startx++;
            starty++;

            // offset 控制每一圈裡每一條邊遍歷的長度
            offset += 1;
        }

        // 如果n為奇數的話,需要單獨給矩陣最中間的位置賦值
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }
};

總結#

image.png|650

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。