目录
题目描述
给定一个长度为 n
的环形整数数组 nums
,返回 nums
的非空 子数组 的最大可能和 。
环形数组 意味着数组的末端将会与开头相连呈环状。形式上, nums[i]
的下一个元素是 nums[(i + 1) % n]
, nums[i]
的前一个元素是 nums[(i - 1 + n) % n]
。
子数组 最多只能包含固定缓冲区 nums
中的每个元素一次。形式上,对于子数组 nums[i], nums[i + 1], ..., nums[j]
,不存在 i <= k1, k2 <= j
其中 k1 % n == k2 % n
。
示例 1:
输入:nums = [1,-2,3,-2] 输出:3 解释:从子数组 [3] 得到最大和 3
示例 2:
输入:nums = [5,-3,5] 输出:10 解释:从子数组 [5,5] 得到最大和 5 + 5 = 10
示例 3:
输入:nums = [3,-2,2,-3] 输出:3 解释:从子数组 [3] 和 [3,-2,2] 都可以得到最大和 3
提示:
n == nums.length
1 <= n <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
算法介绍
该题我觉得可用三种算法来写
第一种是单调队列
单调队列的介绍
单调队列是一种主要用于解决滑动窗口类问题的数据结构,即,在长度为 n 的序列中,求每个长度为 m 的区间的区间最值。它的时间复杂度是 O(n) ,在这个问题中比 O(nlogn) 的ST表和线段树要优。
单调队列的基本思想是,维护一个双向队列(deque),遍历序列,仅当一个元素可能成为某个区间最值时才保留它。
学习单调队列可以先去看看滑动窗口这个题,很典型的一个题
第二种是kanade
kanade 算法就是给定一个数组,寻找这个数组中最大的连续字串和,该算法是基于动态规划的逻辑思维
令 f[j] 为以 a[j]
结尾的最大子段和。也就是,
由于一个子段一定从某个位置截止,所以 就是需要的答案。
代码实现:
int ans = 0;
int res = 0;
for(int i = 0; i < n; i++)
{
ans = a[i] + max(ans,0);
res = max(res,ans);
}
return res ;
第三种是大根堆
大根堆的思路和单调队列的方式是差不多的,只是用大根堆来维护这个最小值,让最小值始终是在最前面。
解题思路
一、前缀和+单调队列
对于单调队列的写法有三种
单调队列
该题的中心思想是先计算前缀和,然后计算对于第 sum[i] 个前面可以减去的最小的那个前缀和得到的值就是在该点处可以取得的最大值,那么求sum[i] 前面可以减去的最小的那个前缀和的值可以运用单调队列的思路进行求解,然后求得的值的数组下标与前缀和对应的下标一样,那么就进行求哪一段的值最大。
因为我们要进行求环形的数组,而且数组的中的值都只能用一次,那么我们就将两个数组进行拼接。例如A = [1,2,3,4,5] , B = A + A,那么拼接的数就是 B = [1,2,3,4,5,1,2,3,4,5];
这里给出简单的拼接方法(vector)
vector<int> A,B;
A.insert(A.end(),A.begin(),A.end());
B = A;
大根堆(优先队列)
该题思路和上题的思路一样,只不过求求sum[i] 前面可以减去的最小的那个前缀和的值的方法不一样,该题还是看代码比较好理解。
二、kanade
这个的代码实现比较简单,但比较难想,需要对kanade有着比较深刻的理解才能写出来
完整代码
单调队列
class Solution {
public:
typedef pair<int,int> PII;
int maxSubarraySumCircular(vector<int>& nums) {
vector<int> a = nums;
a.insert(a.end(),a.begin(),a.end());
const int n = nums.size();
vector<int> sum(2 * n);
sum[0] = a[0];
for(int i = 1; i < 2 * n; i++)
{
sum[i] = sum[i-1] + a[i];
}
deque<int> d;
d.push_back(0);
int res = sum[0];
for(int i = 1; i < 2 * n; i++)
{
while(n != 1 && d.size() && d.front() < i - n) d.pop_front();
while(d.size() && sum[i] <= sum[d.back()]) d.pop_back();
res = max(res, sum[i]-sum[d.front()]);
d.push_back(i);
}
return res ;
}
};
大根堆
class Solution {
public:
typedef pair<int,int> PII;
int maxSubarraySumCircular(vector<int>& nums) {
vector<int> a = nums;
a.insert(a.end(),a.begin(),a.end());
const int n = nums.size();
vector<int> sum(2 * n);
sum[0] = a[0];
for(int i = 1; i < 2 * n; i++)
{
sum[i] = sum[i-1] + a[i];
}
priority_queue<PII,vector<PII>,greater<PII>> p;
p.push({sum[0],0});
int res = sum[0];
for(int i = 1; i < 2 * n; i++)
{
while(!p.empty()&&p.top().second < i-n) p.pop();
res = max(res,sum[i]-p.top().first);
p.push({sum[i],i});
}
return res ;
}
};
kanade
class Solution {
public:
typedef pair<int,int> PII;
int maxSubarraySumCircular(vector<int>& nums) {
int total = 0, maxSum = nums[0], curMax = 0, minSum = nums[0], curMin = 0;
for (int& a : nums) {
curMax = max(curMax + a, a);
maxSum = max(maxSum, curMax);
curMin = min(curMin + a, a);
minSum = min(minSum, curMin);
total += a;
}
return maxSum > 0 ? max(maxSum, total - minSum) : maxSum;
}
};