将序列的位移定义为最大和最小元素之间的差异。
给定一系列整数,找到长度为m
的所有连续子序列的最大位移。
例如,如果我们的序列是[1,5,7,0,2,-4]和m = 3,
如果我们让n表示输入序列的长度,那么我的解决方案在O(nlog(m))时间内运行。有什么方法可以做得更好吗?我觉得必须有一个我缺失的线性时间算法。 出于这个问题的目的,我所关心的只是渐近时间的复杂性。
#include <vector>
#include <set>
#include <iostream>
int find_max_displacement(std::vector<int> seq, int m){
std::multiset<int> subseq;
// insert the m items of first subsequence into tree
for (int i = 0; i < m; i++){
subseq.insert( seq[i] );
}
int max_disp = *subseq.rbegin() - *subseq.begin(); // max minus min
for (int i = 0; i < seq.size() - m; i++){
subseq.erase(subseq.find(seq[i])); // kick oldest element out of subsequence
subseq.insert( seq[i+m] ); // insert new element into subsequence
int new_disp = *subseq.rbegin() - *subseq.begin();
if (new_disp > max_disp){
max_disp = new_disp;
}
}
return max_disp;
}
int main(){
std::vector<int> arr {1, 5, 7, 0, 2, -4};
int max_disp = find_max_displacement(arr, 3);
std::cout << max_disp << std::endl;
return 0;
}
答案 0 :(得分:3)
你是对的,有一个线性时间算法。您可以计算具有滑动最大值的数组和具有最小滑动的数组,并找到这两个数组之间的最大差异。
在线性时间内计算滑动最大值是一个标准问题,对不同技术here有一个很好的解释。如果链接断开,这里是该链接的线性时间算法的描述:
这里给出的算法是递增的最小值算法;它 需要O(n)时间和O(k)空间。一般的想法是找到 在窗口中最小,然后在剩余的最小值 窗口,等等。升序最小值之间的值可以忽略。
更正式地说,让W是长度为k的值的向量。定义 升序mimima序列A,如下:
令A [0]为W中的最小值,并且对于j> 0,令A [j]为最小值 W中的值,索引大于A [j-1]的索引。 (如果两个 位置具有相同的最小值取后一个。)示例:
W = 5,2,8,6,4,7 A = 2,4,7
显然A的长度是1,如果是 如果W是单调的,W中的最小值是W和k中的最后一个元素 越来越多。现在假设我们在V上有一个窗口W并且我们知道 上升的最小值向量A.考虑当我们移动时会发生什么 窗口一个位置。我们在窗口的末尾添加一个元素 从窗口的开头删除一个元素。设x为 新添加的元素。然后A可以通过
更新a:删除大于或等于x的所有元素
b:将x附加到A,
c:并删除A的初始元素(如果从窗口中删除)。
我们不需要记录窗口;我们所需要的只是 递增的最小序列。但是有必要记录一下 序列中的条目将从窗口中删除。为此原因 如果A的元素有两个字段,第一个是a,这很有用 V的值,即某些i的V [i],第二个是索引 当条目从窗口消失时。这发生了k个条目 后面。
由于A的长度是有界的,因为A是队列,所以很自然 将其存储在环形缓冲区中。
步骤(b)和(c)是直截了当的,没有重要意义 备择方案。在步骤(a)中,我们需要找到A中的最后一个值 小于新添加的x。乍一看似乎是一个 A的二进制搜索将是最佳的。不是这种情况;最优的 搜索是从一个简单的循环中回到前面。
证明很简单;线性搜索循环删除元素 逐个删除每次删除的O(1)时间成本。平均而言 A的删除次数与添加次数相同。该 结果是将窗口移动一个位置的平均时间成本 是O(1)。