我正在为即将面试的工作做准备,并参加了一次实践技术测试。我在这些问题上做得很好,除了这个.
问题的前提是:给定一个数组,找出大小为"n“的数组的序列子集的最大差。示例
input = [6,8,4,5,3,1,7], n=3
[6,8,4] = the biggest diff = 4 (8-4)
[8,4,5] = the biggest diff = 4 (8-4)
[4,5,3] = 2
[5,3,1] = 4
[3,1,7] = 6
Final return from function:6
输入的限制是:数组的长度小于100 k,n小于数组的长度。函数必须在2秒内完成.
我最初是用python编写的,但只收到了3/6正确的测试用例,3次由于时间限制而失败,所以我用C语言重写,希望获得更好的性能。
int i,j;
int maxdiff = 0;
int localmax,localmin,localdiff;
for (i=0;i<v_length-d+1;i++){
localmax = v[i];
localmin = v[i];
localdiff = 0;
for(j=0;j<d;j++){
if(v[j+i] > localmax){
localmax = v[j+i];
}
if(v[j+i] < localmin){
localmin = v[j+i];
}
}
localdiff = localmax-localmin;
if(localdiff > maxdiff){
maxdiff = localdiff;
}
}
return maxdiff;
我试过运行这个,但结果一样。3/6正确,3/6由于运行时失败。
我是不是漏掉了什么?我意识到,我在数组ArraySize-n次中循环每个值,我可以在我的脑海中看到,只循环一次数组是可能的,但似乎不知道如何循环。有什么建议吗?谢谢!
发布于 2014-09-06 07:53:02
您可以在O(nlogn)中为子集使用一个min堆和一个最大堆来完成这一任务。遍历数组时,从堆中移除第一个元素并添加新元素。
发布于 2014-09-07 00:09:07
解决长输入序列的顺序子序列问题的最简单方法是使用(单端)队列,至少在概念上是这样。(在输入是向量而不是流的情况下,实际上不需要存储队列,但如果队列是显式的,则算法通常更清晰。)
在这种情况下,解决方案需要在队列的最大值和最小值之间找到最大的差异。如果我们想要一个O(1)
解决方案,我们需要一个队列,其中操作push_back
、pop_front
、min
和max
都是O(1)
。
首先要注意的是,如果我们正在寻找一个具有该属性的堆栈(其中pop_front
被pop_back
替换),那么解决方案非常简单:每当我们推送一个新值时,我们就计算出新的min和max,并将它们与新值一起推送。当我们弹出一个值时,我们也会弹出相关的min和max,并且保持在堆栈顶部的min和max也是正确的。
我们如何将其转换为队列?答案是,我们需要使用堆栈实现队列。或者更准确地说,使用两个堆栈。这就是所谓的“银行家队列”,它使用两个(功能性)堆栈提供一个摊销的O(1)
(functional)队列。
这是一个简单的技巧:一个堆栈--前面的堆栈--用来推,另一个堆栈--后面的堆栈--用于弹出。队列的前面保持在前面的堆栈中,而队列的后面在后端堆栈上保持相反的顺序,因此后端堆栈的顶部是队列中的第一个元素。这很好,直到后台堆栈为空,我们需要弹出第一个元素。在这一点上,我们只需一次弹出前面堆栈中的元素,然后将每个元素推到后面堆栈上。一旦我们这样做了,前面的堆栈是空的,后面堆栈的顶部是前面堆栈中的最后一个元素,这是队列的第一个元素,正如我们所希望的那样。
很明显,上面的实现是摊销的O(1)
,因为每个元素都被推到每个堆栈上一次(并且从每个堆栈弹出一次)。当然,每个操作实际上并不是O(1)
:每隔一段时间,pop_front
就会花费相当长的时间。但平均水平总是正确的。(另一方面,推总是O(1)
。)
因此,我们可以用两个最小-最大的堆栈组成一个最小-最大的队列,并利用它来解决最大范围问题。
有了这个大纲,很容易找到一些优化。首先,我们可以将两个堆栈存储在同一个数组中,如果我们向后存储堆栈并与前堆栈相邻,那么我们只需要跟踪两个堆栈之间边界的位置,而弹出前堆栈并将该值推到后端堆栈的操作包括简单地移动边界指针。(对于min堆栈,我们需要计算mins和maxes。)这将导致一个简单的循环缓冲区实现,这是一个常见的队列解决方案。
此外,在移动窗口的情况下,队列的大小是已知的,因此我们不必处理动态调整大小。而且,在输入是向量的情况下,我们不需要实际将元素推到前面的堆栈上,因为元素位于输入的已知位置,并且不需要前面堆栈中的堆栈min/max值。
我希望所有这些都足以解释这个C++实现:
#include <algorithm>
#include <vector>
using std::min;
using std::max;
struct minmax { int min; int max; };
int maxrange(const std::vector<int>& v, int n) {
int sz = v.size();
n = min(n, sz);
if (n <= 1) return 0;
// The stack only needs n - 2 elements. So this could be adjusted.
minmax* stack = new minmax[n];
int loback, hiback, lofront, hifront;
int maxrange = 0;
for (int s = n - 1, m = 0; s < sz; ++s, --m) {
if (m == 0) {
lofront = hifront = v[s];
loback = hiback = v[s - 1];
for (int i = 2; i < n; ++i) {
stack[i - 2] = minmax{loback, hiback};
loback = min(loback, v[s - i]);
hiback = max(hiback, v[s - i]);
}
m = n - 1;
} else {
lofront = min(lofront, v[s]);
hifront = max(hifront, v[s]);
loback = stack[m-1].min;
hiback = stack[m-1].max;
}
maxrange = max(maxrange, max(hifront, hiback) - min(lofront, loback));
}
delete[] stack;
return maxrange;
}
发布于 2014-09-23 08:58:23
输入:
int inp[]={...},n=...;
inp[N]
在ix[N]
升序中(保持inp[N]
不变)- so for any `i={0,1,2,...,N-2}` is `(inp[ix[i]]<=inp[ix[i+1]])==true`
- this can be done in `O(N*log(N))`
1<n<=N
开始的大小int i0=0,1,2,...N-n
的任何子集都是这样完成的:
int i1=i0+n-1;//子集的有效索引为: i0<=i<=i1;int min=0,max=0;for (i= 0;i< N;i++) if ((ixi>=i0)&(ixi<=i1)){ min=inp[ixi];max=;} for (i=N1;i>=0;i-) if (ixi>=i0)&&i0;(ixi<=i1){ max=inp[ixi];} int diff=max-min;- and find the max diff ...
备注
n
来说更快,对于较小的n
则更慢。https://stackoverflow.com/questions/25696819
复制相似问题