题目描述
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
1
2
3
|
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
|
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
题解
这道题依旧是动态规划,思考了一下,状态只有1个,即数组的当前长度,这样dp数组也是一维的,数组中数据的含义是以i为最后一个元素的数组的子序最大长度。
那么状态转移方程是这样的,
1
|
dp[i] = max{ dp[i - 1] + nums[i], nums[i] }
|
简单的解释是,当长度增长时,这个时候面临两种选择,一种是在之前的基础上加上当前值,一种是抛弃之前的长度,使用当前值,而当前结尾的数组的最大长度就是这两种情况的最大值。
这里依旧有个问题,即求的是数组中的子序最大值,这个最大值的子序并不一定是以当前数组的最后一个元素结尾的。所以这里还需要另外一个变量,比较判断所有元素结尾的长度中最大的那一个。
实现代码如下,算法复杂度为O(N):
1
2
3
4
5
6
7
8
9
10
11
12
|
int maxSubArray(vector<int>& nums) {
if (nums.empty()) return 0;
int *data = new int[nums.size()];
int max_data = data[0] = nums[0];
for (int i = 1; i < nums.size(); i++) {
data[i] = max(data[i - 1] + nums[i], nums[i]);
max_data = max(data[i], max_data);
}
return max_data;
}
|
这里说下坑吧,其实关于dp数组中值得含义自己最开始并未想明白,一直认为应该是当前长度数组的最大子序和,但这样总感觉不对,实现出来的代码也是不对的,没有上面代码中的max_data = max(data[i], max_data);
这一句,而最终取得结果也是dp[N - 1],这样得到的结果并不对。
仔细想了一下,明显结果中存的不是当前数组的最大子序和,但也没想明白到底要怎么搞,最后还是偷偷看了下讨论,才最终知道要加上那一句。
另外,自己在苦苦思索过程中,还想到了另外一个复杂度很高的方法,也做个示例,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
int maxSubArray(vector<int>& nums) {
if (nums.empty()) return 0;
int **data = new int*[nums.size()];
for (int i = 0; i < nums.size(); i++) {
data[i] = new int[nums.size()];
memset(data[i], 0, sizeof(int) * nums.size());
}
int max_num = data[0][0] = nums[0];
for (int i = 1; i < nums.size(); i++) {
for (int j = 0; j <= i; j++) {
data[i][j] = data[i - 1][j] + nums[i];
max_num = max(max_num, data[i][j]);
}
}
return max_num;
}
|
这套代码算法复杂度为O(N ^ 2),dp数组也是个二维的数组。这套代码的基本思路是这样的,以{ -2, 1, -3, 4, -1, 2, 1, -5, 4 }
为例。
求值过程如下:
1
2
3
4
5
6
7
8
9
|
-2 -1 -4 0 -1 1 2 -3 1
1 -2 2 1 3 4 -1 3
-3 1 0 2 3 -2 2
4 3 5 6 1 5
-1 1 2 -3 1
2 3 -2 2
1 -4 0
-5 -1
4
|
横轴为一维,纵轴为2维。横轴每加1,当前值都把之前横轴对应的纵轴上的值都加一遍。同时另存一个变量,记录所有的最大值。这种方法整体来说复杂度太高,在leetcode中测试超时。