关于搜索的棘手面试问题

时间:2010-11-07 02:13:17

标签: arrays algorithm search

我最近听到一位朋友在接受采访时被问到这个问题。他无法弄清楚,我还没有找到任何有效的解决方案。我希望这里有一位算法师可以向我展示一种新的方法

问题:

给定数组A和数字S',提供一个有效的算法(nlogn)来查找数字K,这样如果A中大于K的所有元素都变为K,则结果数组中所有元素的总和将是S'。

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

6 个答案:

答案 0 :(得分:16)

用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 )。

答案 1 :(得分:7)

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

在某些时候,总和的这个下限将从小于S'变为大于S',并且在那时你可以做一些算术来确定截止应该是什么。例如(C =当前总和,L =总和的下限):

start
[90 30 100 40 20]
sort
[20 30 40 90 100]
start adding up the sum
C1 = 20
L1 = 20 + 4*20 = 100 < 210

C2 = 20 + 30 = 50
L2 = 50 + 3*30 = 140 < 210

C3 = 50 + 40 = 90
L3 = 90 + 2*40 = 170 < 210

C4 = 90 + 90 = 180
L4 = 180 + 1*90 = 270 > 210 //too big!

S' = C3 + K*2
therefore
K = (S'-C3)/2 = 60

答案 2 :(得分:5)

这可以通过使用线性时间选择的变化在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;
}

答案 3 :(得分:1)

这是我的解决方案。我基本上是在[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 :(得分:1)

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

  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();
    
    }
    }
    

答案 5 :(得分:0)

将其排序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);
  }
}