【问题标题】:Tricky Interview question on searching关于搜索的棘手面试问题
【发布时间】:2011-05-06 04:44:59
【问题描述】:

我最近从一位朋友那里听到这个问题,他在一次采访中被问到这个问题。他无法弄清楚,我也没有找到任何有效的解决方案。我希望这里有一位算法专家可以向我展示一种新方法

问题:

给定一个数组 A 和一个数 S',提供一个有效的算法(nlogn)来找到一个数 K是S'。

例如,给定A: [90,30,100,40,20]S' = 210K 将是60

【问题讨论】:

    标签: arrays algorithm search


    【解决方案1】:

    用 Python 编写,即使你不懂该语言也应该相当易读:

    #!/usr/bin/env python
    
    A = [90, 30, 100, 40, 20]
    S = 210
    K = 60
    
    A    = sorted(A)
    prev = 0
    sum  = 0
    
    for index, value in enumerate(A):
        # What do we need to set all subsequent values to to get the desired sum?
        solution = (S - sum) / (len(A) - index)
    
        # That answer can't be too big or too small.
        if prev < solution <= value:
            print solution
    
        sum += value
        prev = value
    

    结果:

    60
    

    排序是O(n log n),循环是O(n)。因此将算法作为一个整体组合为 O(n log n)。

    【讨论】:

    • 找到解决方案后,您可以跳出循环。
    • 不需要排序就可以在线性时间内完成,所以你不需要使用基数排序来获得线性时间(见我的sol'n)。
    • 我认为您在这里不需要变量“prev”。由于数组是排序的,所以 'solution' 不可能小于 'prev'。
    【解决方案2】:

    首先将列表从小到大排序,然后找出它有多长。然后开始将数字一一相加。在每个步骤中,还要找到总和的下限 - 如果您尚未添加的所有其余数字与当前数字相同,则整个列表的总和将是多少。

    在某些时候,总和的下限将从小于 S' 变为大于 S',此时您可以进行一些算术运算来确定截止值应该是多少。例如(C = 当前总和,L = 总和的下限):

    开始 [90 30 100 40 20] 种类 [20 30 40 90 100] 开始加起来 C1 = 20 L1 = 20 + 4*20 = 100 210 //太大了! S' = C3 + K*2 所以 K = (S'-C3)/2 = 60

    【讨论】:

    • 不错的答案。阅读问题后,我提出了相同的解决方案。不确定它是否是最优的,但它似乎是 O(NlogN)。我想知道它是否可以在不对数据进行排序的情况下解决(因为排序定义了解决方案中的 Big O 解决方案)。
    • @mazemaster - 嘿,写完答案后,我意识到我读错了这个问题,所以我在重写时删除了它。你没有这样做。 ;-)
    • 是的,正确答案,并且是 O(n log n)。排序步骤需要 O(n log n) 时间,其余时间只需 O(n)。
    • @mazemaster - 感谢您的回复。在上面的解决方案中,您能解释一下为什么将 K 乘以 2 (S' = C3 + K*2)
    • O(log n) 如果我们有排序数组的部分总和是可行的。
    【解决方案3】:

    这是我的解决方案。我基本上是在 [0, max(A)] 范围内对 K 的值进行二进制搜索。虽然这避免了必须首先对数组进行排序(从而保留原始数组),但它仍然是 O(n*log(k)),其中 n 是 A 中的元素数,k 是 A 中的最大值。

    #! /usr/bin/env python
    from itertools import imap
    
    def findK(A,S):
        lo,hi=0,max(A)
        while lo<hi:
            mid=(hi+lo+1)>>1
            result=sum(imap(lambda x: x if x<mid else mid,A))
            if result<=S:
                lo=mid
            else:
                hi=mid-1
        return lo
    
    if __name__=='__main__':
        print findK(A=[90,30,100,40,20],S = 210)            
    

    【讨论】:

      【解决方案4】:

      这可以通过使用线性时间选择的变体来实现,而无需在 O(n) 时间内进行排序,如下所示(请注意,while 循环的迭代的运行时间形成一个几何级数 - 分区子例程分割一个范围一个数组,从下到上,分成小于或大于rank mid的元素,运行时间与数组范围的大小成正比):

      foo(x, s) {
        sumBelow = 0;
        lower = 0;
        upper = x.size();
        while (lower + 1 != upper) {
          mid = (upper + lower) / 2;
          partition(x, lower, mid, upper); // O(upper - lower) time
          sumb = 0;
          maxb = 0; // assuming non-negative to avoid use of flags
          for (i = lower; i < mid; i++) {
            sumb += x[i];
            maxb = max(maxb, x[i]);
          }
          if (sumBelow + sumb + maxb * (x.size() - mid) <= s) {
            lower = mid;
            sumBelow += sumb;
          } else {
            upper = mid;
          }
        }
        K = (s - sumBelow) / (x.size() - lower);
        if (K > maxElement(x)) return error();
        else return K;
      }
      

      【讨论】:

      • 不,我还没有测试过,但如果您有点怀疑,您可以将其转换为您选择的语言,并在一堆随机实例上与基于排序的解决方案进行比较。不过这有点不重要,因为我没有给出分区子例程的代码,这是标准的,但可能需要一些工作才能实现。
      • +1,但也许只是写成partition(x, lower, upper),因为我认为mid实际上是不需要的。这会让你的观点更清楚。
      【解决方案5】:

      nlogn 排序

      int[] sortedArray;
      int s;
      
      int sum=0;
      
      for(int i=0; i<sortedArray.length; i++){
        sum = sum + sortedArray[i];
      
        if((s - sum)/(sortedArray.length - i) < sortedArray[i+1]){
          return (s - sum)/(sortedArray.length - i);
        }
      }
      

      【讨论】:

        【解决方案6】:

        好吧,看起来我迟到了,但无论如何希望这个算法有意义。

        1. 首先将 S 除以数组大小。您将获得 42 个。
        2. 找出数组中有多少个数大于 42,这里是 2 (P)。
        3. 将所有小于 42 的数字相加,得到 N = 210 -(小于 42 的数字之和)。
        4. 最后,N/P 应该会给你一个完美的答案,它的时间复杂度为 O(N),空间复杂度为 O(1)。

          在这里找到执行代码:http://ideone.com/MDL3iy

          import java.util.Scanner;
          class Ideone {
          
          public static void main(String args[]) {
          Scanner in = new Scanner(System.in);
          int S = in.nextInt();
          int[] array = {90,30,100,40,20};
          int len = array.length;
          int sAvg = S/len;
          
          int trackSmallerThanAverage = 0;
          int countMorethanAverage = 0;
          
          for(int i=0; i<len; i++) {
              if(array[i] > sAvg) {
                  countMorethanAverage ++;
              } else if (array[i]<sAvg) {
                  trackSmallerThanAverage += array[i];
                  }
          
              }
          
              int finalValue = ( S - trackSmallerThanAverage )/countMorethanAverage;
              System.out.println(finalValue);
              in.close();
          
          }
          }
          

        【讨论】:

        • 超级算法
        猜你喜欢
        • 2011-07-27
        • 1970-01-01
        • 1970-01-01
        • 2011-01-26
        • 2011-07-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多