整数配列が与えられた場合
nums
、合計が最大の連続サブ配列を見つけ(サブ配列には少なくとも1つの要素が含まれます)、最大の合計を返します。
配列には複素数が含まれているため、この問題はスライディングウィンドウでは実行できず、ウィンドウのサイズを決定できません。
スライディングウィンドウは、合計がSである連続した正の数のシーケンスで使用されます
公式の問題説明にそのアイデアが見られますが、説明は非常に明確なので、ここでは繰り返しません... labuladongを
参照することもできます
方法1:動的計画法
class Solution {
public int maxSubArray(int[] nums) {
int tmp = 0; // tmp 表示以nums[i]为结尾时的最大子序和,我们的目的是求得所有的,然后找出最大的
int res = nums[0]; // res表示到目前nums[i]为止,之前以 nums[k],0≤k≤i 为结尾的所有最大子序和的最大值。
for(int each : nums){
tmp = Math.max(tmp + each, each);
res = Math.max(res, tmp);
}
return res;
}
}
時間計算量:O(n)
スペースの複雑さ:O(1)
方法2:分割統治
class Solution {
public class Status {
public int lSum, rSum, mSum, iSum;
public Status(int lSum, int rSum, int mSum, int iSum) {
this.lSum = lSum;
this.rSum = rSum;
this.mSum = mSum;
this.iSum = iSum;
}
}
public int maxSubArray(int[] nums) {
return getInfo(nums, 0, nums.length - 1).mSum;
}
public Status getInfo(int[] a, int l, int r) {
if (l == r) {
return new Status(a[l], a[l], a[l], a[l]);
}
int m = (l + r) >> 1;
Status lSub = getInfo(a, l, m);
Status rSub = getInfo(a, m + 1, r);
return pushUp(lSub, rSub);
}
public Status pushUp(Status l, Status r) {
int iSum = l.iSum + r.iSum;
int lSum = Math.max(l.lSum, l.iSum + r.lSum);
int rSum = Math.max(r.rSum, r.iSum + l.rSum);
int mSum = Math.max(Math.max(l.mSum, r.mSum), l.rSum + r.lSum);
return new Status(lSum, rSum, mSum, iSum);
}
}
時間計算量:時間計算量はO(n)です。
空間計算量:再帰はO(logn)スタック空間を使用するため、プログレッシブ空間計算量はO(logn)です。
「方法1」と比較すると、「方法2」は同じ時間計算量ですが、再帰を使用し、4つの情報の構造を維持するため、実行に少し時間がかかり、スペースの複雑さは方法1ほど良くありません。理解するのは難しいです。では、この方法の重要性は何ですか?
この質問については、それは本当です。しかし、間隔を解くだけでなく[0, n - 1][0,n−1]
、サブインターバルの[l, r][l,r]
問題を解くために、よく見る「方法II」を使用することもできます。我々は場合は[0, n - 1][0,n−1]
、パーティションのすべての情報サブ間隔がダウンウェイ・ダウンのヒープ記憶メモリに登場された後、それは本当のツリーを構築するためには、我々はOで任意の間隔(LOGN)時間内に答えを求めることができます、シーケンス内の値を変更し、簡単なメンテナンスを行うこともできます。それでも、O(logn)時間の任意の間隔で答えを見つけることができます。大規模なクエリの場合、この方法の利点が反映されます。このツリーは、上記の魔法のデータ構造である線分ツリーです。