件名のソース:https://leetcode-cn.com/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/
タイトル説明
整数配列numsと制限を表す整数制限を指定して、最長の連続サブ配列の長さを返します。サブ配列内の任意の2つの要素間の絶対差は、制限以下である必要があります。
条件を満たすサブ配列がない場合は、0が返されます。
示例 1:
输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。
示例 2:
输入:nums = [10,1,2,4,7,2], limit = 5
输出:4
解释:满足题意的最长子数组是 [2,4,7,2],其最大绝对差 |2-7| = 5 <= 5 。
示例 3:
输入:nums = [4,2,2,2,4,4,2,2], limit = 0
输出:3
提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
0 <= limit <= 10^9
一般的なアイデア
- 配列番号と制限制限が与えられた場合、返されるサブ配列が満たされ、条件max N um −minNumd≤limitmaxNum-minNumd≤limitを満たす最長の連続サブ配列を見つける必要があります。
MからX N U M−m i n N u m d≤l i m i t
マルチセットを使用して、間隔内の最大値をすばやく取得します
- ある間隔で最大値をすばやく取得し、その値を繰り返し可能にすることを考えると、単純で粗雑なマルチセットコンテナーを使用することを考えることができますが、複雑さは最適ではありません。
- マルチセットコンテナはstl標準ライブラリのメンバーです。その基礎となる実装は赤黒ツリー(最下層は配列+リンクリスト)を介して行われ、サブ配列はスライディングウィンドウを介して維持されます。サブ配列は最初にマルチセットに存在する必要があります。マルチセットの特性を使用して、maxNum-minNumの値を取得するには、順方向イテレータ(最初の要素)と逆方向イテレータ(最後の要素)にアクセスするだけで済みます。制限を超えると、移動を続けます。ウィンドウの左側で、マルチセットコンテナのnum [left]要素を削除します。
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
int len = nums.size(), ans = 0, left = 0;
multiset<int, less<int> > mltset;
for(int right = 0; right < len ; ++right){
mltset.insert(nums[right]);
while(*(mltset.rbegin()) - *(mltset.begin()) > limit){
mltset.erase(mltset.find(nums[left++]));
}
ans = max(ans, right - left + 1);
}
return ans;
}
};
複雑さの分析
- 時間計算量:O(nlogn)。nは配列の長さであり、マルチセット削除要素の時間計算量はO(logn)です。
- スペースの複雑さ:O(n)。最悪の場合、マルチセット内の要素の数は配列の長さに等しくなります
スライディングウィンドウ+双方向キュー
- 双方向キューdequeを使用して、間隔内のインクリメント(デクリメント)値を維持します。この質問では、スライディングウィンドウの左側の境界が条件とともに移動するため、ウィンドウ内の一部の値がこの時点で変更されるため、サブ配列の作成の最大値と最小値が変更されました。現時点では、双方向キューの特性を使用して単調なキューを維持できます。
- qMaxキューとqMinキューで表される単調に減少するキューと単調に増加するキューを維持し、それらのdeque.front()をそれぞれこの間隔の最大値と最小値にします。ウィンドウが移動すると、現在の左かどうかのみnums [left]の境界は、単調キューの最大(最小)値であり、その後、ヘッドから要素を削除します
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
int len = nums.size(), ans = 0, left = 0;
deque<int> qMax, qMin;
for(int right = 0 ; right < len ; ++right){
while(!qMax.empty() && qMax.back() < nums[right]) qMax.pop_back();
while(!qMin.empty() && qMin.back() > nums[right]) qMin.pop_back();
qMax.push_back(nums[right]);
qMin.push_back(nums[right]);
while(!qMax.empty() && !qMax.empty() && qMax.front() - qMin.front() > limit){
if(nums[left] == qMax.front()) qMax.pop_front();
if(nums[left] == qMin.front()) qMin.pop_front();
++left;
}
ans = max(ans, right - left + 1);
}
return ans;
}
};
複雑さの分析
- 時間計算量:O(n)。nは配列の長さであり、左右両方がn回スライドしていると見なすことができます。
- スペースの複雑さ:O(n)。nは配列の長さです。最悪の場合、単調なキューは元の配列と同じサイズになります。