LeetCode053——最大子序和

版权声明:版权所有,转载请注明原网址链接。 https://blog.csdn.net/qq_41231926/article/details/82796047

我的LeetCode代码仓:https://github.com/617076674/LeetCode

原题链接:https://leetcode-cn.com/problems/maximum-subarray/description/

题目描述:

知识点:

思路一:暴力解法(在LeetCode中提交会超时)

暴力解法的思路很简单,遍历所有可能的连续子数组,寻求其最大和。

时间复杂度是O(n ^ 3),其中n为nums数组的长度。空间复杂度是O(1)。

JAVA代码:

public class Solution {

	public int maxSubArray(int[] nums) {
		int n = nums.length;
		int result = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
			for (int j = i; j < n; j++) {
				int sum = 0;
				for (int k = i; k <= j; k++) {
					sum += nums[k];
				}
				result = Math.max(result, sum);
			}
		}
        return result;
    }
}

思路二:对暴力解法的改进

在思路一的最内层循环中,我们每次计算sum值时,都是从索引i一直加到索引j,事实上,计算sum时我们完全可以利用上一步的结果,即从索引i一直加到索引j可以变成从索引i一直加到索引j - 1的和再加上索引j。这就是本思路的由来。

时间复杂度是O(n ^ 2),其中n为nums数组的长度。空间复杂度为O(1)。

JAVA代码:

public class Solution {

	public int maxSubArray(int[] nums) {
		int n = nums.length;
		int result = Integer.MIN_VALUE;
        for (int i = 0; i < n; i++) {
        	int sum = 0;
			for (int j = i; j < n; j++) {
				sum += nums[j];
				result = Math.max(result, sum);
			}
		}
        return result;
    }
}

LeetCode解题报告:

思路三:分治算法

对于数组中索引在[left, right]范围内的元素,其具有最大和的连续子数组可能在左半边,也可能在右半边,也可能横跨左右两边。

递归终止条件

如果left == right,在此范围内只有一个元素,直接返回该元素的值即可。

递归过程

(1)递归调用函数求出左半边的最大和leftResult。

(2)递归调用函数求出右半边的最大和rightResult。

(3)求解横跨左右两边的最大和midResult。此时需要以mid为起点,向左和向右分别遍历数组,求出左半边最大和midLeftResult以及右半边最大和midRightResult,将这两者相加即得midResult。

时间复杂度是O(nlogn)级别的,其中n为nums数组的长度。空间复杂度即递归深度是O(logn)级别的。

JAVA代码:

public class Solution {

	public int maxSubArray(int[] nums) {
		int n = nums.length;
		int result = maxSubArray(nums, 0, n - 1);
        return result;
    }
	
	private int maxSubArray(int[] nums, int left, int right) {
		if(left == right) {
			return nums[left];
		}
		int mid = left + (right - left) / 2;
		int leftResult = maxSubArray(nums, left, mid);
		int rightResult = maxSubArray(nums, mid + 1, right);
		
		int leftSum = 0;
		int midLeftResult = Integer.MIN_VALUE;
		for (int i = mid; i >= left; i--) {
			leftSum += nums[i];
			midLeftResult = Math.max(midLeftResult, leftSum);
		}
		int rightSum = 0;
		int midRightResult = Integer.MIN_VALUE;
		for (int i = mid + 1; i <= right; i++) {
			rightSum += nums[i];
			midRightResult = Math.max(midRightResult, rightSum);
		}
		int midResult = midLeftResult + midRightResult;
		
		return Math.max(leftResult, Math.max(rightResult, midResult));
	}
}

LeetCode解题报告:

思路四:“在线算法”

对于nums中索引0 ~ n - 1,假设满足和最大的子串的左右索引分别是i和j,那么对于不论什么k(i <= k <= j),我们有nums中左右索引分别是i和k的子序列的和大于0。

因为假设存在k使得左右索引分别是i和k的子序列的和小于0。那么我们就有左右索引分别是k + 1和j的子序列的和大于左右索引分别是i和j的和,这与我们假设的array[i...j]就是array中和最大子串矛盾。

基于上述规律,我们可以一次扫描nums数组就求出具有最大和的连续子数组的最大和。关键就在于一旦出现sum < 0的情况,我们直接令sum = 0,即舍弃前面的所有的元素,从下一个元素i + 1开始计算起。因为一旦出现sum < 0的情况,说明前面的所有元素不该出现在具有最大和的连续子数组里,因为从索引i + 1开始算起肯定比算上i及到上一个起始索引之间的索引对应的nums数组的值的总和要大,因为i及到上一个起始索引之间的索引对应的nums数组的值的总和的sum < 0。

时间复杂度是O(n)级别的,其中n为nums数组的长度。空间复杂度是O(1)级别的。

JAVA代码:

public class Solution {

	public int maxSubArray(int[] nums) {
		int n = nums.length;
		int result = Integer.MIN_VALUE;
		int sum = 0;
		for (int i = 0; i < n; i++) {
			sum += nums[i];
			result = Math.max(result, sum);
			if(sum < 0) {
				sum = 0;
			}
		}
		return result;
    }
}

LeetCode解题报告:

猜你喜欢

转载自blog.csdn.net/qq_41231926/article/details/82796047
今日推荐