雖然這題在 leetcode 上標註的是「簡單」難度,可是解法有 4 種,而且都很是具備表明性。比較容易想到的是基礎的動態規劃法。javascript
定義狀態數組dp[i]
的含義:數組中元素下標爲[0, i]
的連續子數組最大和。java
狀態轉移的過程以下:git
dp[0] = nums[0]
nums[i] > 0
,那麼 dp[i] = nums[i] + dp[i - 1]
nums[i] <= 0
,那麼 dp[i] = nums[i]
代碼實現以下:github
// ac地址:https://leetcode-cn.com/problems/maximum-subarray/ // 原文地址:https://xxoo521.com/2020-03-09-max-sub-sum/ /** * @param {number[]} nums * @return {number} */ var maxSubArray = function(nums) { const dp = []; let res = (dp[0] = nums[0]); for (let i = 1; i < nums.length; ++i) { dp[i] = nums[i]; if (dp[i - 1] > 0) { dp[i] += dp[i - 1]; } res = Math.max(res, dp[i]); } return res; };
時間複雜度和空間複雜度都是\(O(N)\)。數組
解法 1 中開闢了 dp 數組。其實在原數組上作修改,用nums[i]
來表示dp[i]
。因此解法 1 的代碼能夠優化爲:優化
// ac地址:https://leetcode-cn.com/problems/maximum-subarray/ // 原文地址:https://xxoo521.com/2020-03-09-max-sub-sum/ /** * @param {number[]} nums * @return {number} */ var maxSubArray = function(nums) { let res = nums[0]; for (let i = 1; i < nums.length; ++i) { if (nums[i - 1] > 0) { nums[i] += nums[i - 1]; } res = Math.max(res, nums[i]); } return res; };
不用開闢額外空間,因此空間複雜度降爲\(O(1)\)。ui
貪心法的題目比較少見,並且通常都比較難證實。本題的貪心法的思路是:在循環中找到不斷找到當前最優的和 sum。spa
注意:sum 是 nums[i]
和 sum + nums[i]
中最大的值。這種作法保證了 sum 是一直是針對連續數組算和。code
代碼實現以下:blog
// ac地址:https://leetcode-cn.com/problems/maximum-subarray/ // 原文地址:https://xxoo521.com/2020-03-09-max-sub-sum/ /** * @param {number[]} nums * @return {number} */ var maxSubArray = function(nums) { let maxSum = (sum = nums[0]); for (let i = 1; i < nums.length; ++i) { sum = Math.max(nums[i], sum + nums[i]); maxSum = Math.max(maxSum, sum); } return maxSum; };
空間複雜度爲\(O(1)\),時間複雜度爲\(O(N)\)
分治法的作題思路是:先將問題分解爲子問題;解決子問題後,再將子問題合併,解決主問題。
使用分治法解本題的思路是:
[1, 2, 3, 4]
被分爲 [1, 2]
和 [3, 4]
總體過程能夠參考來自 Leetcode 官方題解的圖:
能夠看到,分治法可行的關鍵的是:最大子序列和只可能出如今左子數組、右子數組或橫跨左右子數組 這三種狀況下。
代碼實現以下:
// ac地址:https://leetcode-cn.com/problems/maximum-subarray/ // 原文地址:https://xxoo521.com/2020-03-09-max-sub-sum/ /** * @param {number[]} nums * @param {number} left * @param {number} right * @param {number} mid * @return {number} */ function crossSum(nums, left, right, mid) { if (left === right) { return nums[left]; } let leftMaxSum = Number.MIN_SAFE_INTEGER; let leftSum = 0; for (let i = mid; i >= left; --i) { leftSum += nums[i]; leftMaxSum = Math.max(leftMaxSum, leftSum); } let rightMaxSum = Number.MIN_SAFE_INTEGER; let rightSum = 0; for (let i = mid + 1; i <= right; ++i) { rightSum += nums[i]; rightMaxSum = Math.max(rightMaxSum, rightSum); } return leftMaxSum + rightMaxSum; } /** * @param {number[]} nums * @param {number} left * @param {number} right * @return {number} */ function __maxSubArray(nums, left, right) { if (left === right) { return nums[left]; } const mid = Math.floor((left + right) / 2); const lsum = __maxSubArray(nums, left, mid); const rsum = __maxSubArray(nums, mid + 1, right); const cross = crossSum(nums, left, right, mid); return Math.max(lsum, rsum, cross); } /** * @param {number[]} nums * @return {number} */ var maxSubArray = function(nums) { return __maxSubArray(nums, 0, nums.length - 1); };
時間複雜度是\(O(NlogN)\)。因爲遞歸調用,因此空間複雜度是\(O(logN)\)
整理不易,若對您有幫助,請給個「關注+點贊」,您的支持是我更新的動力 👇