2024年8月1日,今天學習了陣列的第二部分。
1.鞏固了昨天的雙指標問題,即滑動視窗/雙指標;注意,雙指標是為了減少for迴圈,使用的時候小心迴圈的寫法和快慢指標的增長方法。
2.學習了陣列模擬的螺旋矩陣問題,注意迴圈不變數;
3.學習了字首和的方法,字首和常用來解決區間和問題,其實是避免重複讀取計算陣列中的值,提前算好。
5. 209長度最小的子陣列(滑動視窗/雙指標)
題目:給定一個含有 n 個正整數的陣列和一個正整數 s ,找出該陣列中滿足其和 ≥ s 的長度最小的 連續 子陣列,並返回其長度。如果不存在符合條件的子陣列,返回 0。
暴力解法兩層for迴圈,第一個for遍歷子陣列起點,第二個for遍歷終點。
要想變成一個for迴圈,考慮使用雙指標,兩個指標都只需要遍歷一次,兩個指標之間的即為子陣列/滑動視窗。很明顯:
i:起始位置,視窗大於等於s,向前移動
j:結束位置,視窗不到s,向後移動。
- 雙指標問題考慮迴圈的寫法,一般是fast指標做迴圈的索引,所以j是for中的index。
- 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 ] ]
要寫成程式碼多次迴圈,就要堅持迴圈不變數原則,不難發現橫豎左右四條邊都要左閉右開,因此對應的圖如下:
那麼程式碼至少需要知道以下幾個量:
-
迴圈幾次?這個就是最外層的迴圈,即迴圈幾圈。
- 舉例子,3*3需要1次加一個正中間的點,4*4需要2次。
int loop = n / 2;
,n為奇數時中間的點需要單獨處理。mid = n / 2;
矩陣中間的位置,例如:n為3,中間的位置就是(1,1)。
-
每次的起始位置和終止位置?
- 首先,明確左上角此圈開始的位置,每次都要+1,所以可以設定一個
startx = 0, starty = 0
,即每迴圈一個圈的起始位置。 - 上邊從左到右:
for (j=starty ; j<n-offset; j++)
- 右列從上到下:
for (i; i < n - offset; i++)
- 下邊從右到左:
for (; j > starty; j--)
,承接上頭的j,此時的j=n-offset。 - 左列從下到上:
for (; i >startx; i--)
- 首先,明確左上角此圈開始的位置,每次都要+1,所以可以設定一個
-
每一圈裡每一條邊遍歷的長度?
透過一個變數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);
}
}
注意不知道多少行時,讀取資料的方法。
大資料量使用
scanf
等,不用cin
。
~scanf("%d%d", &a, &b)
:
scanf
函式返回成功讀入的資料項數,讀入資料時遇到了“檔案結束”則返回EOF。scanf ( "%d %d" ,&a,&b)
:如果a和b都被成功讀入,那麼返回值就是2;如果只有a被成功讀入,返回值為1;
如果a和b都未被成功讀入,返回值為0;如果遇到錯誤或遇到end of file
,返回值為EOF
。EOF
的值為-1,其取反的的值為0(-1的補碼錶示全是1,按位取反後全是0)。
今日古詩
點絳唇·感興
王禹偁〔宋代〕
雨恨雲愁,江南依舊稱佳麗。水村漁市,一縷孤煙細。
天際徵鴻,遙認行如綴。平生事,此時凝睇,誰會憑欄意。
這算是王禹偁傳世的唯一詞作了。此詞以清麗的筆觸、沉鬱而高曠的格調,即事即目,描繪了江南水鄉的風物景色,並透過描繪江南雨景,寄寓了詞人積極用世、渴望有所作為的政治理想和懷才不遇的苦悶情懷。全詞寓情於景,因情繪景,感情質樸,風格清麗。