算法导论实践——最大连续子序列(LeetCode 53题)
很巧妙的算法,没有看参考思路的话自己应该想不出来。之前觉得不用分治递归无论如何都需要O(n2)的时间复杂度(想要应用动态规划,但却不知道怎么用,脑子里却死活都是二维定义M[i,j]之类的 ),就算是教材上的分治递归也需要O(nlogn)。经过高人指点后,不得不赞扬算法的神奇。
理论
算法关键:(设M[i]为以i结尾的连续最大子数组的和)
-
最优子结构
将代表M[i]的连续最大子数组用k = i - 1划分后,M[i] = Max{M[i-1]+A[i],A[i]}这里M[i-1]是以i-1结尾的连续最大子数组,如果说M[i-1]+A[i]大于A[i],这也就是说,A[i]可以加入前一个子数组如果M[i-1]+A[i]小于A[i],这也就是说,A[i]就应该等于M[i],A[i]本身就是以i结尾的连续最大子数组
(小于的情况可以用反证法予以论证)
设A[k,i-1]是M[i-1]所代表的最大连续子数组,且M[i]所对应的连续最大子数组为A[j……k……i-1,i],下面证明矛盾。因为A[k,i-1]是A[1,i-1]的最大连续子数组,因此A[j……k……i-1]的和小于A[k,i-1],因此A[j……k……i-1,i]的和小于A[k……i-1,i],而又M[i-1]+A[i]小于A[i],因此A[k……i-1,i]小于A[i],所以A[i]是最大连续子数组,M[i]就应该等于A[i]这样,与M[i]所对应的连续最大子数组为A[j……k……i-1,i]假设相反,故证明矛盾。根据论证知,此时M[i] = A[i] -
重叠子问题
以[1,2,3]为例易得。 -
代价方程
M[i] = A[i] | i == 0 |
---|---|
M[i] = MAX{(M[i - 1] + A[i]), A[i]} | if i > 0 |
- 构造最优解
遍历M,找到最大解即最优解 - 时间复杂度分析
- 代价:
只需遍历数组A - 构造最优解:
只需遍历数组M - 总时间复杂度为O(n)
- 代价:
实践
BigMaxSubArray.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace BigInteger
{
public static class BigMaxSubArray
{
/*
* 演算:
* (设M[i]为以i结尾的连续最大子数组的和)
*
* 最优子结构
* 将代表M[i]的连续最大子数组用k = i - 1将数组划分后,M[i] = Max{M[i-1]+A[i],A[i]}
* 这里M[i-1]是以i-1结尾的连续最大子数组,如果说M[i-1]+A[i]大于A[i],这也就是说,A[i]可以加入前一个子数组
* 如果M[i-1]+A[i]小于A[i],这也就是说,A[i]就应该等于M[i],A[i]本身就是以i结尾的连续最大子数组
*
* (小于的情况可以用反证法予以论证)
* 设A[k,i-1]是M[i-1]所代表的最大连续子数组,且M[i]所对应的连续最大子数组为A[j……k……i-1,i],下面证明矛盾。
* 因为A[k,i-1]是A[1,i-1]的最大连续子数组,因此A[j……k……i-1]的和小于A[k,i-1],因此A[j……k……i-1,i]的和
* 小于A[k……i-1,i],而又M[i-1]+A[i]小于A[i],因此A[k……i-1,i]小于A[i],所以A[i]是最大连续子数组,M[i]就应该等于A[i]
* 这样,与M[i]所对应的连续最大子数组为A[j……k……i-1,i]假设相反,故证明矛盾。根据论证知,此时M[i] = A[i]
*
*
*
*
* 重叠子问题(以[1,2,3]为例)
* (1 2 3)
* (1)(2)(3)(1 + 2)(2 + 3)(1 + 2 + 3)
* (1 + 2) (2 + 3)
*
* 代价:
*
* M[i] = A[i] if i == 0
* M[i] = MAX{(M[i - 1] + A[i]), A[i]} if i > 0
*
* 代价保存:
* M这个一维数组中
*
* 构造优化解:
* 遍历M,找到最大解即最优解
*
* 时间复杂度分析:
* 代价:
* 只需遍历数组A
*
* 构造最优解:
* 只需遍历数组M
*
* 总时间复杂度为O(n)
*/
private const int MAX_ARRAYNUM = 10;
private static int[] M = new int[MAX_ARRAYNUM];
public static int[] MaxSubArray(int[] A)
{
for(int i = 0; i < A.Length; i++)
{
if (i == 0)
{
M[i] = A[i];
}
else
{
M[i] = Math.Max(M[i - 1] + A[i], A[i]);
}
}
return M;
}
}
}
Program.cs
using System;
using BigInteger;
using System.Linq;
namespace BigInteger
{
class Program
{
static void Main(string[] args)
{
//int[] A = { 11, 12, 14, 13, 15, 5, 6, 7, 8, 9, 1, 2, 3, 4, 10, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
//int Element = BigDataMedianUtil.Select_i_th_Data(A, 0, A.Length - 1, A.Length/2);
//Console.WriteLine(Element);
int[] A = { -2, 1, -3, 4, -1, 2, 1, -5, 4 };//LeetCode 53题上的示例
int[] M = BigMaxSubArray.MaxSubArray(A);
Console.WriteLine(M.Max());
}
}
}