【问题标题】:Interview - Find magnitude pole in an array采访 - 在数组中查找幅度极点
【发布时间】:2013-03-13 22:21:58
【问题描述】:

Magnitude Pole:数组中左侧元素小于或等于它且右侧元素大于或等于它的元素。

示例输入

3,1,4,5,9,7,6,11

期望的输出

4,5,11

我在面试中被问到这个问题,我必须返回元素的索引,并且只返回第一个满足条件的元素。

我的逻辑

  1. 取两个 MultiSet(这样我们也可以考虑重复),一个用于元素的右侧,一个用于元素的左侧 元素(极点)。
  2. 从第 0 个元素开始,将其余所有元素放在“正确的集合”中。
  3. 如果第 0 个元素小于或等于“右集”上的所有元素,则返回其索引。
  4. 否则将其放入“左集”并从索引 1 处的元素开始。
  5. 遍历数组,每次从“左集”中选择最大值,从“右集”中选择最小值并进行比较。
  6. 在任何时刻,对于任何元素,其左侧的所有值都在“左集合”中,而其右侧的值都在“右集合”中

代码

int magnitudePole (const vector<int> &A) {  
   multiset<int> left, right;        
   int left_max, right_min;          
   int size = A.size();
   for (int i = 1; i < size; ++i)
       right.insert(A[i]);
   right_min = *(right.begin()); 
   if(A[0] <= right_min)
       return 0;
   left.insert(A[0]);
   for (int i = 1; i < size; ++i) {
       right.erase(right.find(A[i]));
       left_max = *(--left.end());
       if (right.size() > 0)
           right_min = *(right.begin());
       if (A[i] > left_max && A[i] <= right_min)
           return i;
       else
           left.insert(A[i]);
   }
   return -1;
}

我的问题

  • 我被告知我的逻辑不正确,我无法理解为什么这个逻辑不正确(尽管我已经检查了一些案例和 它正在返回正确的索引)
  • 出于我自己的好奇心,如何在 O(n) 时间内不使用任何 set/multiset 的情况下做到这一点。

【问题讨论】:

  • 你对算法背后逻辑的描述似乎对细节有点不精确(你什么时候从 RHS 中删除?你什么时候比较?你什么时候插入 LHS?)但是在总的来说,这个想法似乎是正确的,因为它基本上是一种蛮力搜索,它使用多集来帮助记账。
  • 其实是在线测试,所以我先写了代码,但是当他们回答说错了,我就告诉他们这是我做的。所以可能是我的算法不详细,但是他们根据代码中的算法来判断。
  • 4 不是震级吗?

标签: algorithm


【解决方案1】:

对于 O(n) 算法:

  1. 计算 [0, length(n)) 中所有 k 从 n[0] 到 n[k] 的最大元素,将答案保存在数组 maxOnTheLeft 中。这需要 O(n);
  2. 对 [0, length(n)) 中的所有 k 计算从 n[k] 到 n[length(n)-1] 的最小元素,将答案保存在数组 minOnTheRight 中。这需要 O(n);
  3. 循环遍历整个事物并找到 maxOnTheLeft

你的代码在这里(至少)是错误的:

if (A[i] > left_max && A[i] <= right_min) // <-- should be >= and <=

【讨论】:

  • 感谢您的及时答复。您的算法是正确的,刚才我意识到是的,我错过了“=”,但是根据面试官的说法,我的算法不正确:(
  • @iamnotmaynard:为什么? O(N)+O(N)+...O(N) 任意常数时间 = O(N)。
  • 对于任何给定的k,找到从n[0]n[k] 的最大元素是O(n)。对[0, length(n)](即== n)中的所有 k 执行此操作将是 O(n²)。不会吗?
  • @iamnotmaynard 只需渐进式地进行,就会使其成为线性的。
【解决方案2】:
  • 创建两个 bool[N],分别称为 NorthPole 和 SouthPole(只是为了幽默。
  • 通过 A[] 跟踪到目前为止找到的最大元素,如果 A[i] > Max(A[0..i-1]) 则将 SouthPole[i] 设置为 true
  • 如果 A[i]
  • 通过 NorthPole 和 SouthPole 向前走,找到第一个同时设置为 true 的元素。

上述每一步都是O(N),因为访问每个节点一次,所以总体上是O(N)。

【讨论】:

    【解决方案3】:

    Java 实现:

    Collection<Integer> magnitudes(int[] A) {
        int length = A.length;
        // what's the maximum number from the beginning of the array till the current position
        int[] maxes = new int[A.length];
        // what's the minimum number from the current position till the end of the array
        int[] mins = new int[A.length];
    
        // build mins
        int min = mins[length - 1] = A[length - 1];
        for (int i = length - 2; i >= 0; i--) {
            if (A[i] < min) {
                min = A[i];
            }
            mins[i] = min;
        }
    
        // build maxes
        int max = maxes[0] = A[0];
        for (int i = 1; i < length; i++) {
            if (A[i] > max) {
                max = A[i];
            }
            maxes[i] = max;
        }
    
        Collection<Integer> result = new ArrayList<>();
        // use them to find the magnitudes if any exists
        for (int i = 0; i < length; i++) {
            if (A[i] >= maxes[i] && A[i] <= mins[i]) {
                // return here if first one only is needed
                result.add(A[i]);
            }
        }
        return result;
    }
    

    【讨论】:

      【解决方案4】:

      您的逻辑似乎完全正确(但没有检查实现)并且可以实现以提供 O(n) 时间算法!很好地从集合的角度思考。

      您的右侧集合可以实现为支持最小值的堆栈,而左侧集合可以实现为支持最大值的堆栈,这给出了 O(n) 时间算法。

      拥有一个支持 max/min 的堆栈是一个众所周知的面试问题,并且每次操作都可以完成(push/pop/min/max 为 O(1))。

      要将其用于您的逻辑,伪代码将如下所示

      foreach elem in a[n-1 to 0]
          right_set.push(elem)
      
      while (right_set.has_elements()) {
         candidate = right_set.pop();
         if (left_set.has_elements() && left_set.max() <= candidate <= right_set.min()) {
             break;
         } else if (!left.has_elements() && candidate <= right_set.min() {
              break;
         }
         left_set.push(candidate);
      }
      
      return candidate
      

      【讨论】:

      • 是的,当时我忘了我可以使用堆栈。
      • @JackSparrow:实施细节:-)。我真的很喜欢你思考这个问题的方式。它有一些成熟!
      • @JackSparrow:可能是你的实现是错误的。他们是否声称您的逻辑不正确?他们有没有说缺陷是什么?
      • @JackSparrow,顺便说一句,我刚刚看了你的实现。似乎您假设 right_min 在开头,而 left_max 在结尾。这是不正确的。你需要在你的实现中遍历整个集合。
      • 实际上他们告诉我的逻辑不正确,因为我在邮件中问他们出了什么问题,所以他们回答说我的逻辑不正确,我实际上需要三个循环,一个从头到尾,一个从头到尾一个过滤掉结果。我向他们解释说,因为我们必须返回满足这个条件的第一个值,所以我不需要过滤掉任何结果。
      【解决方案5】:

      我在 Codility 上看到了这个问题,用 Perl 解决了:

      sub solution {
              my (@A) = @_;            
      
              my ($max, $min) = ($A[0], $A[-1]);
              my %candidates;
      
              for my $i (0..$#A) {
                      if ($A[$i] >= $max) {
                              $max = $A[$i];
                              $candidates{$i}++;
                      }
              }
              for my $i (reverse 0..$#A) {
                      if ($A[$i] <= $min) {
                              $min = $A[$i];
                              return $i if $candidates{$i};
                      }
              }
              return -1;
      }
      

      【讨论】:

        【解决方案6】:

        下面的代码怎么样?我认为它的效率在最坏的情况下并不好,但它的预期效率会很好。

            int getFirstPole(int* a, int n)
        {
            int leftPole = a[0];
            for(int i = 1; i < n; i++)
            {
                if(a[j] >= leftPole)
                {
                    int j = i;
                    for(; j < n; j++)
                    {
                        if(a[j] < a[i])
                        {
                            i = j+1;  //jump the elements between i and j                   
                            break;
                        }
                        else if (a[j] > a[i])
                            leftPole = a[j];
                    }
                    if(j == n) // if no one is less than a[i] then return i
                        return i;
                }
            }
            return 0;
        }
        

        【讨论】:

          【解决方案7】:
          1. 创建名为 mags 的 int 数组和名为 maxMag 的 int 变量。
          2. 对于源数组中的每个元素,检查元素是否大于或等于maxMag
          3. 如果是:将元素添加到 mags 数组并设置 maxMag = element
          4. 如果不是:循环遍历 mags 数组并删除所有较小的元素。

          结果:震级极点数组

          【讨论】:

            【解决方案8】:

            有趣的问题,我在下面给出了自己的 C# 解决方案,请阅读 cmets 以了解我的方法。

            public int MagnitudePoleFinder(int[] A)
            {
                //Create a variable to store Maximum Valued Item i.e. maxOfUp
                int maxOfUp = A[0];
            
                //if list has only one value return this value
                if (A.Length <= 1) return A[0];
            
                //create a collection for all candidates for magnitude pole that will be found in the iteration
                var magnitudeCandidates = new List<KeyValuePair<int, int>>();
            
                //add the first element as first candidate
                var a = A[0];
                magnitudeCandidates.Add(new KeyValuePair<int, int>(0, a));
            
                //lets iterate
                for (int i = 1; i < A.Length; i++)
                {
                    a = A[i];
                    //if this item is maximum or equal to all above items ( maxofUp will hold max value of all the above items)
                    if (a >= maxOfUp)
                    {
                        //add it to candidate list
                        magnitudeCandidates.Add(new KeyValuePair<int, int>(i, a));
                        maxOfUp = a;
                    }
                    else
                    {
                        //remote all the candidates having greater values to this item
                        magnitudeCandidates = magnitudeCandidates.Except(magnitudeCandidates.Where(c => c.Value > a)).ToList();
                    }
                }
                //if no candidate return -1
                if (magnitudeCandidates.Count == 0) return -1;
                else
                    //return value of first candidate
                    return magnitudeCandidates.First().Key;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-04-19
              • 1970-01-01
              • 2014-04-26
              • 1970-01-01
              • 2021-08-18
              • 1970-01-01
              相关资源
              最近更新 更多