给定一组数字,返回所有其他数字的产品数组(无分区)

pol*_*nts 180 arrays algorithm

我在求职面试中被问到这个问题,我想知道其他人如何解决这个问题.我对Java最熟悉,但欢迎使用其他语言的解决方案.

给定一组数字,nums返回一个数字数组products,其中products[i]是所有数字的乘积nums[j], j != i.

Input : [1, 2, 3, 4, 5]
Output: [(2*3*4*5), (1*3*4*5), (1*2*4*5), (1*2*3*5), (1*2*3*4)]
      = [120, 60, 40, 30, 24]
Run Code Online (Sandbox Code Playgroud)

您必须在O(N)不使用除法的情况下执行此操作

Mic*_*son 244

polygenelubricants方法的解释是:诀窍是构造数组(在4个元素的情况下)

{              1,         a[0],    a[0]*a[1],    a[0]*a[1]*a[2],  }
{ a[1]*a[2]*a[3],    a[2]*a[3],         a[3],                 1,  }
Run Code Online (Sandbox Code Playgroud)

通过分别从左边缘和右边缘开始,可以在O(n)中完成这两个操作.

然后将两个数组逐个元素相乘,得到所需的结果

我的代码看起来像这样:

int a[N] // This is the input
int products_below[N];
p=1;
for(int i=0;i<N;++i) {
  products_below[i]=p;
  p*=a[i];
}

int products_above[N];
p=1;
for(int i=N-1;i>=0;--i) {
  products_above[i]=p;
  p*=a[i];
}

int products[N]; // This is the result
for(int i=0;i<N;++i) {
  products[i]=products_below[i]*products_above[i];
}
Run Code Online (Sandbox Code Playgroud)

如果你需要在太空中是O(1)你也可以这样做(这不太清楚恕我直言)

int a[N] // This is the input
int products[N];

// Get the products below the current index
p=1;
for(int i=0;i<N;++i) {
  products[i]=p;
  p*=a[i];
}

// Get the products above the curent index
p=1;
for(int i=N-1;i>=0;--i) {
  products[i]*=p;
  p*=a[i];
}
Run Code Online (Sandbox Code Playgroud)

  • 非常聪明!这个算法有名字吗? (8认同)
  • 这是O(n)运行时,但它在空间复杂度上也是O(n).你可以在O(1)空间中完成.我的意思是,除了输入和输出容器的大小当然. (4认同)
  • 如果任何一个元素为0,算法将失败.所以不要忘记检查0跳过. (3认同)
  • @MichaelAnderson很棒的工作人员,但请告诉我这背后的主要逻辑,一旦你得到这个要求,你是如何开始这个的. (2认同)
  • @Mani如果将元素设置为0,该算法很好。但是,可以在输入中扫描此类元素,如果找到它们,则效率更高。如果有两个零元素,则整个结果为零;如果只有一个,则说v_i = 0,则结​​果中唯一的非零条目是第ith个元素。但是,我怀疑添加一个用于检测和计数零元素的通道会损害解决方案的清晰度,并且在大多数情况下可能不会获得任何实际的性能提升。 (2认同)

Jas*_*eet 49

这是一个小的递归函数(在C++中)来进行修改.它需要O(n)额外空间(在堆栈上).假设数组在a中并且N保持数组长度,我们有

int multiply(int *a, int fwdProduct, int indx) {
    int revProduct = 1;
    if (indx < N) {
       revProduct = multiply(a, fwdProduct*a[indx], indx+1);
       int cur = a[indx];
       a[indx] = fwdProduct * revProduct;
       revProduct *= cur;
    }
    return revProduct;
}
Run Code Online (Sandbox Code Playgroud)


pol*_*nts 16

这是我尝试用Java解决它.抱歉为非标准格式化,但代码有很多重复,这是我能做的最好的,使它可读.

import java.util.Arrays;

public class Products {
    static int[] products(int... nums) {
        final int N = nums.length;
        int[] prods = new int[N];
        Arrays.fill(prods, 1);
        for (int
           i = 0, pi = 1    ,  j = N-1, pj = 1  ;
           (i < N)         && (j >= 0)          ;
           pi *= nums[i++]  ,  pj *= nums[j--]  )
        {
           prods[i] *= pi   ;  prods[j] *= pj   ;
        }
        return prods;
    }
    public static void main(String[] args) {
        System.out.println(
            Arrays.toString(products(1, 2, 3, 4, 5))
        ); // prints "[120, 60, 40, 30, 24]"
    }
}
Run Code Online (Sandbox Code Playgroud)

循环不变量是pi = nums[0] * nums[1] *.. nums[i-1]pj = nums[N-1] * nums[N-2] *.. nums[j+1].i左边的部分是"前缀"逻辑,j右边的部分是"后缀"逻辑.


递归单行

Jasmeet给出了一个(漂亮!)递归解决方案; 我把它变成了这个(丑陋的)Java单线程.它使用堆栈中的临时空间进行就地修改O(N).

static int multiply(int[] nums, int p, int n) {
    return (n == nums.length) ? 1
      : nums[n] * (p = multiply(nums, nums[n] * (nums[n] = p), n + 1))
          + 0*(nums[n] *= p);
}

int[] arr = {1,2,3,4,5};
multiply(arr, 1, 0);
System.out.println(Arrays.toString(arr));
// prints "[120, 60, 40, 30, 24]"
Run Code Online (Sandbox Code Playgroud)

  • 我认为2变量循环使得理解比必要更难(至少对于我可怜的大脑!),两个独立的循环也能完成这项工作. (3认同)

fre*_*low 14

将Michael Anderson的解决方案翻译成Haskell:

otherProducts xs = zipWith (*) below above

     where below = scanl (*) 1 $ init xs

           above = tail $ scanr (*) 1 xs
Run Code Online (Sandbox Code Playgroud)


sth*_*sth 13

悄悄地规避"不分裂"规则:

sum = 0.0
for i in range(a):
  sum += log(a[i])

for i in range(a):
  output[i] = exp(sum - log(a[i]))
Run Code Online (Sandbox Code Playgroud)

  • Nitpick:据我所知,计算机使用二项式展开来实现对数——这*确实*需要除法...... (2认同)

小智 10

在这里,简单而干净的解决方案具有O(N)复杂性:

int[] a = {1,2,3,4,5};
    int[] r = new int[a.length];
    int x = 1;
    r[0] = 1;
    for (int i=1;i<a.length;i++){
        r[i]=r[i-1]*a[i-1];
    }
    for (int i=a.length-1;i>0;i--){
        x=x*a[i];
        r[i-1]=x*r[i-1];
    }
    for (int i=0;i<r.length;i++){
        System.out.println(r[i]);
    }
Run Code Online (Sandbox Code Playgroud)


wil*_*ell 6

C++,O(n):

long long prod = accumulate(in.begin(), in.end(), 1LL, multiplies<int>());
transform(in.begin(), in.end(), back_inserter(res),
          bind1st(divides<long long>(), prod));
Run Code Online (Sandbox Code Playgroud)

  • 不允许分裂 (8认同)
  • “第一个序列的阶乘”?哇?我的意思是序列元素的乘积。 (2认同)

小智 5

  1. 向左旅行 - >正确并继续保存产品.称之为过去. - > O(n)
  2. 旅行权 - >左保留产品.称之为未来. - > O(n)
  3. 结果[i] =过去[i-1]*future [i + 1] - > O(n)
  4. 过去[-1] = 1; 和未来[n + 1] = 1;

上)