LeetCode 題解之 53. Maximum Subarray(連續子陣列的最大和問題)
阿新 • • 發佈:2019-01-11
53. Maximum Subarray(連續子陣列的最大和問題)
題目描述和難度
- 題目描述:
給定一個整數陣列 nums
,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
示例:
輸入: [-2,1,-3,4,-1,2,1,-5,4], 輸出: 6 解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。
進階:
如果你已經實現複雜度為 O(n) 的解法,嘗試使用更為精妙的分治法求解。
思路分析
求解關鍵:連續子陣列的問題,一般我們著眼於以當前遍歷到的元素結尾的那個子陣列,這樣做分析會簡化問題。
參考解答
參考解答1
public class Solution {
// 給定一個整數陣列 nums ,找到一個具有最大和的連續子陣列(子陣列最少包含一個元素),返回其最大和。
/**
* 定義狀態:
* dp[i] : 表示以 nums[i] 結尾的連續子陣列的最大和
* <p>
* 狀態轉移方程:
* dp[i] = max{num[i],dp[i-1] + num[i]}
*
* @param nums
* @return
*/
public int maxSubArray(int [] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
int[] dp = new int[len];
dp[0] = nums[0];
for (int i = 1; i < len; i++) {
dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]);
}
// 最後這一步,是求一個全域性的最優值
int res = dp[0];
for (int i = 1; i < len; i++) {
res = Math.max(res,dp[i]);
}
return res;
}
}
參考解答2 :和參考解答 1 是一樣的,只不過在遍歷的過程中,就把最優解解求出來了。
public class Solution2 {
/**
* 和 Solution 一樣,空間複雜度更小
* 時間複雜度:O(n)
* 空間複雜度:O(1)
*
* @param nums
* @return
*/
public int maxSubArray(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
int segmentSum = nums[0];
int res = nums[0];
for (int i = 1; i < len; i++) {
segmentSum = Math.max(nums[i], segmentSum + nums[i]);
res = Math.max(res, segmentSum);
}
return res;
}
public static void main(String[] args) {
int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
Solution2 solution = new Solution2();
int maxSubArray = solution.maxSubArray(nums);
System.out.println(maxSubArray);
}
}
參考解答3:使用分治思想,個人覺得稍顯繁瑣,但是我們可以通過這個問題了解分治思想。
public class Solution3 {
/**
* 使用分治演算法完成計算
* https://www.geeksforgeeks.org/divide-and-conquer-maximum-sum-subarray/
*
* @param nums
* @return
*/
public int maxSubArray(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
return maxSubArraySum(nums, 0, len - 1);
}
/**
* 一定會包含 nums[mid] 這個元素
*
* @param nums
* @param l
* @param m
* @param r
* @return
*/
private int maxCrossingSum(int[] nums, int l, int m, int r) {
int sum = 0;
int leftSum = Integer.MIN_VALUE;
// 左半邊包含 nums[mid] 元素,最多可以到什麼地方
// 走到最邊界,看看最值是什麼
// 計算以 mid 結尾的最大的子陣列的和
for (int i = m; i >= l; i--) {
sum += nums[i];
if (sum > leftSum) {
leftSum = sum;
}
}
sum = 0;
int rightSum = Integer.MIN_VALUE;
// 右半邊不包含 nums[mid] 元素,最多可以到什麼地方
// 計算以 mid+1 開始的最大的子陣列的和
for (int i = m + 1; i <= r; i++) {
sum += nums[i];
if (sum > rightSum) {
rightSum = sum;
}
}
return leftSum + rightSum;
}
/**
* @param nums
* @param l
* @param r
* @return
*/
private int maxSubArraySum(int[] nums, int l, int r) {
if (l == r) {
return nums[l];
}
int mid = l + (r - l) / 2;
return max3(maxSubArraySum(nums, l, mid),
maxSubArraySum(nums, mid + 1, r),
maxCrossingSum(nums, l, mid, r));
}
private int max3(int num1, int num2, int num3) {
return Math.max(num1, Math.max(num2, num3));
}
}