陣列part02

YueHuai發表於2024-08-01

2024年8月1日,今天學習了陣列的第二部分。
1.鞏固了昨天的雙指標問題,即滑動視窗/雙指標;注意,雙指標是為了減少for迴圈,使用的時候小心迴圈的寫法和快慢指標的增長方法。
2.學習了陣列模擬的螺旋矩陣問題,注意迴圈不變數;
3.學習了字首和的方法,字首和常用來解決區間和問題,其實是避免重複讀取計算陣列中的值,提前算好。

5. 209長度最小的子陣列(滑動視窗/雙指標)

題目:給定一個含有 n 個正整數的陣列和一個正整數 s ,找出該陣列中滿足其和 ≥ s 的長度最小的 連續 子陣列,並返回其長度。如果不存在符合條件的子陣列,返回 0。


暴力解法兩層for迴圈,第一個for遍歷子陣列起點,第二個for遍歷終點。

要想變成一個for迴圈,考慮使用雙指標,兩個指標都只需要遍歷一次,兩個指標之間的即為子陣列/滑動視窗。很明顯:

i:起始位置,視窗大於等於s,向前移動

j:結束位置,視窗不到s,向後移動。

  1. 雙指標問題考慮迴圈的寫法,一般是fast指標做迴圈的索引,所以j是for中的index。
  2. j在for中更新了,如何更新i呢?應該是每次動了j,都要把sum和target比較,若是sum>target,就移動i。但是這裡不能是if(sum>target),應該是while(sum>target),因為需要不停的移動i,直到sum<target。

注意:雙指標問題中兩個ptr分別是怎麼動的,是在for動呢,還是for裡頭,for裡頭的話,是比較一次動一次(if)就好,還是多次比較多次動直至滿足條件(while)。

int minSubArrayLen(int target, 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];
            // 每次更新 i,並不斷比較子序列是否符合條件
            while (sum >= target)
            {
                subLength = (j - i + 1); // 取子序列的長度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; //滑動視窗的精髓:不斷變更i
            }
        }
        // 如果result沒有被賦值,返回0,說明沒有符合條件的子序列
        return result == INT32_MAX ? 0 : result;
    }

6. 59 螺旋矩陣

題目:給定一個正整數 n,生成一個包含 1 到 n^2 所有元素,且元素按順時針順序螺旋排列的正方形矩陣。

示例: 輸入: 3 輸出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]


要寫成程式碼多次迴圈,就要堅持迴圈不變數原則,不難發現橫豎左右四條邊都要左閉右開,因此對應的圖如下:

那麼程式碼至少需要知道以下幾個量:

  1. 迴圈幾次?這個就是最外層的迴圈,即迴圈幾圈。

    1. 舉例子,3*3需要1次加一個正中間的點,4*4需要2次。
    2. int loop = n / 2;,n為奇數時中間的點需要單獨處理。
    3. mid = n / 2; 矩陣中間的位置,例如:n為3,中間的位置就是(1,1)。
  2. 每次的起始位置和終止位置?

    1. 首先,明確左上角此圈開始的位置,每次都要+1,所以可以設定一個startx = 0, starty = 0,即每迴圈一個圈的起始位置。
    2. 上邊從左到右:for (j=starty ; j<n-offset; j++)
    3. 右列從上到下:for (i; i < n - offset; i++)
    4. 下邊從右到左:for (; j > starty; j--),承接上頭的j,此時的j=n-offset。
    5. 左列從下到上:for (; i >startx; i--)
  3. 每一圈裡每一條邊遍歷的長度?

    透過一個變數offset來控制。最開始offset為1,即最外圈應該是n-offset那麼長,之後每一圈的邊都要減2,而起始位置startx和starty都加了1,那麼控制結尾的offset只需要加1即可。

7. 區間和(字首和:避免陣列重複讀取計算,藉助額外空間)

題目:給定一個整數陣列 Array,請計算該陣列在每個指定區間內元素的總和。


暴力解法:首先讀入,需要[1,5]之間的,那就再迴圈1到5之間的數值。所以有多少個查詢,就有多少次迴圈。需要注意不知道有多少次查詢,ACM形式讀入資料時採用這種方法:

while (cin >> a >> b) {}

字首和解法:來一個算一個太重複了,首先就把每個字首和都算出來,用哪個取哪個。p[i] 表示 下標 0 到 i 的 vec[i] 累加 之和。

int main() {
    int n, a, b;
    cin >> n;
    vector<int> vec(n);
    vector<int> p(n);
    int presum = 0;
    for (int i = 0; i < n; i++) {
        scanf("%d", &vec[i]);
        presum += vec[i];
        p[i] = presum;
    }

    while (~scanf("%d%d", &a, &b)) {
        int sum;
        if (a == 0) sum = p[b];
        else sum = p[b] - p[a - 1];
        printf("%d\n", sum);
    }
}
  1. 注意不知道多少行時,讀取資料的方法。

  2. 大資料量使用scanf等,不用cin

    ~scanf("%d%d", &a, &b):

    1. scanf 函式返回成功讀入的資料項數,讀入資料時遇到了“檔案結束”則返回EOF。
    2. scanf ( "%d %d" ,&a,&b):如果a和b都被成功讀入,那麼返回值就是2;如果只有a被成功讀入,返回值為1;
      如果a和b都未被成功讀入,返回值為0;如果遇到錯誤或遇到end of file,返回值為EOF
    3. EOF的值為-1,其取反的的值為0(-1的補碼錶示全是1,按位取反後全是0)。

今日古詩

點絳唇·感興

王禹偁〔宋代〕

雨恨雲愁,江南依舊稱佳麗。水村漁市,一縷孤煙細。
天際徵鴻,遙認行如綴。平生事,此時凝睇,誰會憑欄意。

這算是王禹偁傳世的唯一詞作了。此詞以清麗的筆觸、沉鬱而高曠的格調,即事即目,描繪了江南水鄉的風物景色,並透過描繪江南雨景,寄寓了詞人積極用世、渴望有所作為的政治理想和懷才不遇的苦悶情懷。全詞寓情於景,因情繪景,感情質樸,風格清麗。

相關文章