4-209.長度最小的子陣列
給定一個含有 n
個正整數的陣列和一個正整數 target
。
找出該陣列中滿足其總和大於等於 target
的長度最小的 子陣列[numsl, numsl+1, ..., numsr-1, numsr]
,並返回其長度。如果不存在符合條件的子陣列,返回 0
。
示例 1:
輸入:target = 7, nums = [2,3,1,2,4,3]
輸出:2
解釋:子陣列 [4,3] 是該條件下的長度最小的子陣列。
示例 2:
輸入:target = 4, nums = [1,4,4]
輸出:1
示例 3:
輸入:target = 11, nums = [1,1,1,1,1,1,1,1]
輸出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
進階:
- 如果你已經實現
O(n)
時間複雜度的解法, 請嘗試設計一個O(n log(n))
時間複雜度的解法。
尋找子陣列,最簡單的想法就是直接用兩層for迴圈進行暴力求解,兩層for迴圈巢狀,據說會超時所以我就沒嘗試。
為了設計O(n)時間複雜度的演算法,就需要用到滑窗。其實從第一天就想問,時間複雜度是啥,本人不是計算機專業,所以以前涉及到寫程式碼的時候其實並沒有考慮這些,能動就行.jpg。於是又去補習了一下時間複雜度的問題。
時間複雜度是電腦科學中用於描述一個演算法的執行時間隨輸入規模變化而變化的指標。它衡量了演算法執行所需的時間隨著輸入大小的變化情況。時間複雜度幫助我們瞭解演算法在輸入規模增加時效率的變化趨勢,而不必關注具體的硬體或實現的細節。
通常,時間複雜度使用大O符號來表示,它表明了演算法在最壞情況下的執行時間增長率。
常見的時間複雜度型別
- O(1):常數時間複雜度,演算法的執行時間與輸入規模無關,始終是一個常數。例如,直接訪問陣列中的某個元素。
- O(log n):對數時間複雜度,隨著輸入規模增加,時間複雜度以對數速度增長。常見於二分查詢演算法。
- O(n):線性時間複雜度,執行時間與輸入規模成正比。例如,遍歷一個長度為 nn 的陣列時,演算法需要執行 nn 次操作。
- O(n log n):常見於高效的排序演算法,如歸併排序、堆排序。
- O(n^2):二次時間複雜度,執行時間隨著輸入規模的平方增長,常見於簡單的巢狀迴圈(如氣泡排序、選擇排序)。
- O(2^n):指數時間複雜度,執行時間隨著輸入規模的指數增長,常見於遞迴演算法(如解決“漢諾塔”問題的遞迴演算法)。
- O(n!):階乘時間複雜度,常見於暴力求解排列組合問題的演算法。
卡哥的影片講解比文字版好理解一些,但還是要自己琢磨一下才能想明白,其實滑窗本質上和雙指標思路一樣,j指標負責向後移動,在移動到某位置時,此時前面所有數的和大於目標值,首先記錄此時的子陣列長度,然後判斷是否需要更新result,最後開始嘗試移動i指標,此時只要依然大於目標值,就繼續記錄。
這裡文字說明非常無力,我畫個圖來演示
如上圖所示,我們的j就是上面的指標,隨著它的移動,前面所有項加和會逐漸增加,這裡我們設定目標值為7,那麼在第三張圖時其實已經達到8了,記錄最小值為4,也就是2312這四個數,此時下面的指標會移動,來嘗試縮小陣列,於是如圖。
此時sum的和就只有6了,不滿足我們的條件,於是不更新最小陣列值而是將j指標繼續移動。每次都會逐漸縮短陣列,於是最後就可以得出2的結果,也就是43的部分,就像一個貪吃蛇在逐漸伸縮身體去尋找最小長度覆蓋到指定數值。程式碼如下:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int sum = 0;//和
int i =0;//後指標
int subL = 0;//目前子集長度
int result = INT32_MAX;//將結果定義為最大值
for(int j = 0;j < nums.size(); j++){
sum += nums[j];
while(sum >= target){
subL = (j - i + 1);
result = result < subL ? result : subL;//判斷result和subl中的最小值,進行賦值
sum-=nums[i++];//c++特化寫法,省去一行半。
}
}
return result == INT32_MAX ? 0 : result;//如果沒成功找到任何一個,那麼返回一個0
}
};
5-59.螺旋矩陣II
給你一個正整數 n
,生成一個包含 1
到 n2
所有元素,且元素按順時針順序螺旋排列的 n x n
正方形矩陣 matrix
。
示例 1:
輸入:n = 3
輸出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
輸入:n = 1
輸出:[[1]]
提示:
1 <= n <= 20
螺旋矩陣問題的關鍵就在於列印每條邊的時候的規則,只要能夠統一列印規則,那麼問題就會簡單很多。採用每條邊列印第一個點,不列印最後一個點的原則進行編寫。程式碼如下:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n,(n*n)));//直接將矩陣賦值為n*n來規避奇數矩陣導致的最後一個數的問題
int startx = 0, starty = 0;//定義開始點
int offset = 1;
int count = 1;
int loop = n/2;
while(loop--){
int i = startx;
int j = starty;
for(j = starty; j < n - offset; j++){//列印第一條邊
res[startx][j] = count++;
}
for(i = startx; i < n - offset; i++){//列印第二條邊
res[i][j] = count++;
}
for(;j > starty; j--){//列印第三條邊
res[i][j] = count++;
}
for(;i > startx; i--){//列印第四條邊
res[i][j] = count++;
}
startx++;
starty++;
offset++;
}
return res;//輸出結果
}
};
這裡用了一個取巧的辦法,是卡哥影片評論區朋友的建議,就是最開始初始化的時候,可以直接將所有值初始化為n的平方,這樣即使最後一個值不進行輸入,也正好就是需要的值,如果是偶數的話就只是重複賦值了一次而已。個人認為比較巧妙。
這道題目裡面主要學習的點在於,慢慢理清每一條邊在遍歷賦值時的座標變化,透過畫圖的方式逐步推理,遍歷完一圈後,內部圈座標的變化。很多時候只用腦子想會很繞,這時候畫圖並記錄就會清晰很多。
陣列內容結束了,這裡引用程式碼隨想錄知識星球海螺人大佬製作的圖片,作為以後複習時的材料。
感謝大佬總結的知識導圖!
一刷暫時就不做擴充題了,先把基礎部分好好理清。由於程式碼基本功比較弱,很多地方都要自己去研究一下,力求把每一份程式碼中每一個部分都搞懂。