Kadane's algorithm与子数组最大和求解问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/seagal890/article/details/89062176

Kadane's algorithm与子数组最大和问题

关于求子数组最大和的问题

Leetcode原题链接:53. Maximum Subarray https://leetcode.com/problems/maximum-subarray/

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

类似的问题在LeetCode上还有:

121. Best Time to Buy and Sell Stock  https://leetcode.com/problems/best-time-to-buy-and-sell-stock/

152. Maximum Product Subarray https://leetcode.com/problems/maximum-product-subarray/

697. Degree of an Array https://leetcode.com/problems/degree-of-an-array/

978. Longest Turbulent Subarray https://leetcode.com/problems/longest-turbulent-subarray/

先来讨论 53. Maximum Subarray https://leetcode.com/problems/maximum-subarray/ 这个题目。

题目的大意是:给定一个整型数组nums,找到元素连续的子数组(包含至少一个数组元素),其元素累加和最大。
举例:nums=[-2,1,-3,4,-1,2,1,-5,4],所有的子数组中[4,-1,2,1] 可以累加出最大的和6,返回6.

/*
	 * 算法设计
	 * */
	public static int maxSum(int[] nums) {
        //当数组为null时,或者数组为空数组时(长度为0)直接返回0
		if (nums == null || nums.length == 0) {
			return 0;
		}
        //假设最大值max为Integer.MIN_VALUE
		int max = Integer.MIN_VALUE;
		//当前累加和为current,初始值为0
		int current = 0;
		//遍历当前数组
		for (int i = 0; i != nums.length; i++) {
			//将当前数组元素与current进行累加,结果仍然存入current中
			current += nums[i];
			//从最大和与当前的累加和中取最大值,存入最大和max中
			max = Math.max(max, current);
			//更新current
			current = current < 0 ? 0 : current;
		}
        //返回最大累加和
		return max;
	}

Maximum subarray problem,这个问题的来源可以参考下面的链接:
The maximum subarray problem was proposed by Ulf Grenander in 1977 as a simplified model for maximum likelihood estimation of patterns in digitized images. https://en.wikipedia.org/wiki/Maximum_subarray_problem

卡内基梅隆大学的 Jay Kadane 不久就为一维问题设计了一个O(N)时间算法,这个算法被称为:Kadane's algorithm。

A bit of a background: Kadane's algorithm is based on splitting up the set of possible solutions into mutually exclusive (disjoint) sets. It exploits the fact that any solution (i.e., any member of the set of solutions) will always have a last element {\displaystyle i}i (this is what is meant by "sum ending at position {\displaystyle i}i"). Thus, we simply have to examine, one by one, the set of solutions whose last element's index is {\displaystyle 1}1, the set of solutions whose last element's index is {\displaystyle 2}2, then {\displaystyle 3}3, and so forth to {\displaystyle n}n. It turns out that this process can be carried out in linear time.

Kadane's algorithm begins with a simple inductive question: if we know the maximum subarray sum ending at position {\displaystyle i}i (call this {\displaystyle B_{i}}B_{i}), what is the maximum subarray sum ending at position {\displaystyle i+1}i+1 (equivalently, what is {\displaystyle B_{i+1}}B_{{i+1}})? The answer turns out to be relatively straightforward: either the maximum subarray sum ending at position {\displaystyle i+1}i+1 includes the maximum subarray sum ending at position {\displaystyle i}i as a prefix, or it doesn't (equivalently, {\displaystyle B_{i+1}=\max(A_{i+1},A_{i+1}+B_{i})}{\displaystyle B_{i+1}=\max(A_{i+1},A_{i+1}+B_{i})}, where {\displaystyle A_{i+1}}A_{i+1} is the element at index {\displaystyle i+1}i+1).

Thus, we can compute the maximum subarray sum ending at position {\displaystyle i}i for all positions {\displaystyle i}i by iterating once over the array. As we go, we simply keep track of the maximum sum we've ever seen. Thus, the problem can be solved with the following code, expressed here in Python:

Python伪代码描述如下:

def max_subarray(A):
     max_ending_here = max_so_far = A[0]
     for x in A[1:]:
         max_ending_here = max(x, max_ending_here + x)
         max_so_far = max(max_so_far, max_ending_here)
     return max_so_far

算法描述:

  • 遍历该数组, 遍历之前设置了两个变量 max_ending_here, max_so_far。其中 max_ending_here 用于记录遍历过程中, 如果把当前元素 x 强制规定为子数列的最后一个元素时, 能找到的最大子数列的总和是多少
  • 由于真正的最大子数列必然存在一个结尾元素, 所以只需要从每个位置计算出的 max_ending_here 中, 找到最大值, 就是全局的最大子数列的值。
  • max_so_far 用于记录遍历过程中, 所发现的最大的 max_ending_here
  • 一次遍历之后, 变量 max_so_far 中存储的即为最大子片段的和值。

理解了 Kadane's algorithm 算法,上述问题就很容易解决了。

DP算法思路

初始化dp[0] = nums[0],dp[i] = max{dp[i] + nums[i] , nums[i]}, max(dp[i])即为所求。

	/*
	 * 算法设计(DP算法)
	 * */
	public static int maxSum(int[] nums) {

		int dp[] = new int[nums.length]; 
		int max = nums[0]; 
		dp[0] = nums[0]; 
		for (int i = 1; i < nums.length; i++) {			
			dp[i] = Math.max(dp[i-1] + nums[i] ,nums[i]);
			max = Math.max(max, dp[i]);
		}
		return max;
	}

猜你喜欢

转载自blog.csdn.net/seagal890/article/details/89062176
今日推荐