[LeetCode] Maximum Size Subarray Sum Equals k 最大子陣列之和為k
Given an array nums and a target value k, find the maximum length of a subarray that sums to k. If there isn't one, return 0 instead.
Example 1:
Given nums = [1, -1, 5, -2, 3]
, k = 3
,
return 4
. (because the subarray [1, -1, 5, -2]
sums to 3 and is the longest)
Example 2:
Given nums
[-2, -1, 2, 1]
, k = 1
,return
2
. (because the subarray [-1, 2]
sums to 1 and is the longest)
Follow Up:
Can you do it in O(n) time?
這道題給我們一個一維陣列nums,讓我們求和為k最大子陣列,預設子陣列必須連續,題目中提醒我們必須要在O(n)的時間複雜度完成,我試了下brute force無法通過OJ,那麼根據題目中的提示標籤,我們需要用雜湊表和累積和來做,關於累積和的用法可以參看我之前的部落格Range Sum Query - Immutable
nums: [1, -1, 5, -2, 3], k = 3
sums: [1, 0, 5, 3, 6]
我們可以看到累積和的第四個數字為3,和k相同,則說明前四個數字就是符合題意的一個子陣列,再來看第二個例子:
nums: [-2, -1, 2, 1], k = 1
sums: [-2, -3, -1, 0]
我們發現累積和中沒有數字等於k,但是我們知道這個例子的答案是[-1, 2],那麼我們看累積和陣列的第一和第三個數字,我們是否能看出一些規律呢,沒錯,第三個數字-1減去k,得到第一個數字,這就是規律,這也是累積和求區間和的方法,但是由於累計和陣列中可能會有重複數字,而雜湊表的關鍵字不能相同,比如下面這個例子:
nums: [1, 0, -1], k = -1
sums: [1, 1, 0]
我們發現累積和陣列的第一個和第二個數字都為1,那麼如何建立對映呢,我想的是用一個一維陣列將其都存起來,然後比較的話就比較陣列中的第一個數字,當我們建立完雜湊表後,開始遍歷這個雜湊表,當累積和跟k相同時,我們更新res,不相同的話我們檢測當前值減去k得到的值在雜湊表中存不存在,如果存在就更新結果,參見程式碼如下:
解法一:
class Solution { public: int maxSubArrayLen(vector<int>& nums, int k) { if (nums.empty()) return 0; int res = 0; unordered_map<int, vector<int>> m; m[nums[0]].push_back(0); vector<int> sum = nums; for (int i = 1; i < nums.size(); ++i) { sum[i] += sum[i - 1]; m[sum[i]].push_back(i); } for (auto it : m) { if (it.first == k) res = max(res, it.second.back() + 1); else if (m.find(it.first - k) != m.end()) { res = max(res, it.second.back() - m[it.first - k][0]); } } return res; } };
然而當我上網看大神們的解法時,才發現我圖樣圖森破,根本不需要我寫的那麼複雜,我們不需要另外建立一個累積和的陣列,而是直接用一個變數sum邊累加邊處理,而且我們雜湊表也完全不用建立和一維陣列的對映,只要儲存第一個出現該累積和的位置,後面再出現直接跳過,這樣算下來就是最長的子陣列,對於想出這解法的人,博主只想說,閣下何不隨風起,扶搖直上九萬里~參見程式碼如下:
解法二:
class Solution { public: int maxSubArrayLen(vector<int>& nums, int k) { int sum = 0, res = 0; unordered_map<int, int> m; for (int i = 0; i < nums.size(); ++i) { sum += nums[i]; if (sum == k) res = i + 1; else if (m.count(sum - k)) res = max(res, i - m[sum - k]); if (!m.count(sum)) m[sum] = i; } return res; } };
類似題目:
參考資料: