清明假期马上就要结束了,小熊给大家带来一道笔试和面试中与「动态规划」相关的常考的简单题,这道题被字节、微软、亚马逊和苹果等各大互联网大厂作为笔试题。
这道题就是 Leetcode 的第 53 题-最大子序和,了解「动态规划」的童鞋,在看到最大两个字的时候,很容易就会想到用「动态规划」去解答,因为涉及到「最优解」的问题,一般都可以通过动归去做。本题小熊提供「动态规划」的思路供大家参考,希望对大家有所帮助。
本题是一道典型的「动态规划」的题,因此采用动态规划的策略去解答。
「定义状态」 dp[i]:以 nums[i]结尾(包含 nums[i])的连续子数组的最大和。
「状态转移方程」 dp[i] = max(dp[i - 1] + nums[i], nums[i]),其中 i >= 1,当 dp[i - 1] < 0 时,抛弃当前的和最大的连续子数组,从 nums[i] 开始,重新寻找和最大的连续子数组,否则,将 nums[i] 加入到当前的和最大的连续子数组。
「边界条件」 dp[0] = nums[0]。
「举栗」
以数组 nums[i] = [-2,1,-3,4,-1,2,1,-5,4] 为例子,如下图示。
从上图可以看出:dp[0] = nums[0] = -1 < 0,由于当前的连续子数组的最大和小于零,因此应该丢弃 num[0],从 nums[1] = 1 开始重新寻找和最大的连续子数组。
寻找和最大的连续子数组的完整动图,如下所示:
由状态转移方程可知,dp[i] 只与 dp[i - 1] 和 nums[i] 相关,因此没必要再去定义 dp,直接复用 nums 即可。
「C++」
1 int maxSubArray(vector<int>& nums) { 2 int maxSum = nums[0]; 3 for (int i = 1; i < nums.size(); ++i) { 4 nums[i] = max(nums[i - 1], 0) + nums[i]; 5 maxSum = max(maxSum, nums[i]); 6 } 7 8 return maxSum; 9 }View Code
「Java」
1 int maxSubArray(int[] nums) { 2 int maxSum = nums[0]; 3 for (int i = 1; i < nums.length; ++i) { 4 nums[i] = Math.max(nums[i - 1], 0) + nums[i]; 5 maxSum = Math.max(maxSum, nums[i]); 6 } 7 8 return maxSum; 9 }View Code
「Python3」
1 def maxSubArray(self, nums: List[int]) -> int: 2 for i in range(1, len(nums)): 3 nums[i] = max(nums[i - 1], 0) + nums[i] 4 5 return max(nums)View Code
「Golang」
1 func maxSubArray(nums []int) int { 2 maxSum := nums[0]; 3 for i := 1; i < len(nums); i++ { 4 if nums[i - 1] > 0 { 5 nums[i] += nums[i - 1] 6 } 7 8 if nums[i] > maxSum { 9 maxSum = nums[i] 10 } 11 } 12 13 return maxSum; 14 }View Code
「C」
1 int maxSubArray(int* nums, int numsSize){ 2 int maxSum = nums[0]; 3 for (int i = 1; i < numsSize; ++i) { 4 nums[i] = fmax(nums[i - 1] + nums[i], nums[i]); 5 maxSum = fmax(maxSum, nums[i]); 6 } 7 8 return maxSum; 9 }View Code
「复杂度分析」
时间复杂度:「O(n)」,其中 n 是数组的长度,需要遍历一遍数组。
空间复杂度:「O(1)」,未开辟额外的存储空间。