【问题标题】:Took a practice technical interview online,,, what am I missing?在线进行了一次练习技术面试,,,我错过了什么?
【发布时间】:2014-10-31 01:16:25
【问题描述】:

我正在准备近期的工作面试,并参加了实践技术测试。我在这些问题上做得很好,除了这个......

问题的前提是:给定一个数组,找出大小为“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

输入的限制类似于:数组的长度将小于 100k,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 中的每个值,我可以在脑海中以某种方式想象可以只循环一次数组,但似乎无法弄清楚如何。 有什么建议吗? 谢谢!

【问题讨论】:

    标签: c arrays performance


    【解决方案1】:

    您可以在 O(nlogn) 中使用一个最小堆和一个最大堆作为子集。 在遍历数组期间从堆中删除第一个元素并添加新元素

    【讨论】:

      【解决方案2】:

      解决涉及长输入序列的连续子序列的问题的最简单方法是使用(单端)队列,至少在概念上是这样。 (在输入是向量而不是流的情况下,实际上不需要存储队列,但如果队列明确,算法通常会更清晰。)

      在这种情况下,解决方案需要找到队列的最大值和最小值之间的最大差值。如果我们想要O(1) 解决方案,我们需要一个队列,其中操作push_back、pop_front、min 和max 都是O(1)。

      首先要注意的是,如果我们正在寻找具有该属性的 stack(其中 pop_front 被替换为 pop_back),解决方案很简单:每当我们推送一个新的值,我们计算新的最小值和最大值,并将它们与新值一起推送。当我们弹出一个值时,我们也弹出了关联的最小值和最大值,而留在栈顶的最小值和最大值再次正确。

      我们如何将其转换为队列?答案是我们需要使用栈来实现队列。或者更准确地说,使用两个堆栈。这就是所谓的“银行家队列”,它使用两个(函数式)堆栈提供了一个摊销的O(1)(函数式)队列。

      这是一个简单的技巧:其中一个堆栈(前堆栈)用于推送,而另一个堆栈(后堆栈)用于弹出。队列的前面保存在前面的栈中,队列的后面倒序保存在后面的栈中,这样后面的栈顶就是队列中的第一个元素。这工作正常,直到返回堆栈为空并且我们需要弹出第一个元素。那时,我们只需从前堆栈中一次弹出一个元素,然后将每个元素推入后堆栈。完成后,前栈为空,后栈的顶部是前栈中的最后一个元素,也就是队列的第一个元素。

      很明显,上面的实现是摊销的O(1),因为每个元素都被压入每个堆栈一次(并从每个堆栈弹出一次)。当然,每个操作实际上并不是O(1):每隔一段时间,pop_front 将花费相当长的时间。但平均值总是正确的。 (另一方面,推送总是O(1)。)

      所以我们可以从两个最小-最大堆栈中创建一个最小-最大队列,并用它来解决最大范围问题。

      有了这个大纲,很容易找到一些优化。首先,我们可以将两个栈存储在同一个数组中,如果我们将后面的栈向后存储并且与前面的栈连续,那么我们只需要跟踪两个栈的边界位置,以及出栈的操作前堆栈并将该值推入后堆栈只需移动边界指针。 (在最小-最大堆栈的情况下,我们需要计算最小值和最大值。)这导致了一个简单的循环缓冲区实现,这是一种常见的队列解决方案。

      此外,在移动窗口的情况下,队列的大小是已知的,因此我们不必处理动态调整大小。而且,在输入是向量的情况下,我们不需要实际将元素推送到前堆栈,因为元素位于输入中的已知位置,我们不需要堆栈中的最小值/最大值前栈。

      我希望所有这些都足以解释这个 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;
      }
      

      【讨论】:

      • 绝妙的解决方案! +1
      【解决方案3】:

      输入:

      int inp[]={...},n=...;
      
      1. 为输入创建 int 索引数组

        const int N=sizeof(inp)/sizeof(inp[0]);
        int ix[N];
        for (int i=0;i<N;i++) ix[i]=i;
        
      2. 索引排序inp[N] in ix[N]升序(保持inp[N]不变)

        • 所以对于任何i={0,1,2,...,N-2} 是(inp[ix[i]]&lt;=inp[ix[i+1]])==true
        • 这可以在O(N*log(N)) 中完成
      3. 现在从int i0=0,1,2,...N-n 开始的任何大小为1&lt;n&lt;=N 的子集都是这样完成的:

        int i1=i0+n-1; // valid indexes for subset are i0<=i<=i1;
        int min=0,max=0;
        for (i=  0;i< N;i++) if ((ix[i]>=i0)&&(ix[i]<=i1)) { min=inp[ix[i]]; break; }
        for (i=N-1;i>=0;i--) if ((ix[i]>=i0)&&(ix[i]<=i1)) { max=inp[ix[i]]; break; }
        int diff=max-min;
        
      4. 在所有位置循环子弹 3

        • 并找到最大差异...

      [注释]

      • 对于较大的n,这更快,但对于较小的n,速度较慢
      • 所以最好是根据 n 大小阈值使用您的方法

      【讨论】:

        猜你喜欢
        • 2012-03-20
        • 2011-01-27
        • 1970-01-01
        • 2021-06-23
        • 2022-07-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-09
        相关资源
        最近更新 更多