我在一次工作面试中被问到这个问题,我想知道其他人是如何解决这个问题的。我最擅长使用Java,但也欢迎使用其他语言的解决方案。

给定一个数字数组nums,返回一个数字数组products,其中products[i]是所有nums[j]的乘积,j != i。 输入:[1,2,3,4,5] 输出:[(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] 你必须在O(N)中不使用除法来做这个。


当前回答

以下是线性O(n)时间内的简单Scala版本:

def getProductEff(in:Seq[Int]):Seq[Int] = {

   //create a list which has product of every element to the left of this element
   val fromLeft = in.foldLeft((1, Seq.empty[Int]))((ac, i) => (i * ac._1, ac._2 :+ ac._1))._2

   //create a list which has product of every element to the right of this element, which is the same as the previous step but in reverse
   val fromRight = in.reverse.foldLeft((1,Seq.empty[Int]))((ac,i) => (i * ac._1,ac._2 :+ ac._1))._2.reverse

   //merge the two list by product at index
   in.indices.map(i => fromLeft(i) * fromRight(i))

}

这是可行的,因为本质上答案是一个数组,它是左右所有元素的乘积。

其他回答

下面是我尝试用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]"
    }
}

循环不变量为pi = nums[0] * nums[1] *..* nums[N-2] *..num [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]"

最近有人问我这个问题,虽然我不能得到O(N),但我有一个不同的方法(不幸的是O(N²)),但我想无论如何都要分享。

首先转换为列表<Integer>。

遍历原始数组array.length()次。

使用while循环乘下一组所需的数字:

while (temp < list.size() - 1) {
    res *= list.get(temp);
    temp++;
}

然后将res添加到一个新数组(当然,您已经在前面声明了),然后将数组[i]的值添加到List,依此类推。

我知道这不会有太大的用处,但这是我在面试的压力下想到的:)

    int[] array = new int[]{1, 2, 3, 4, 5};
    List<Integer> list = Arrays.stream(array).boxed().collect(Collectors.toList());
    int[] newarray = new int[array.length];
    int res = 1;
    for (int i = 0; i < array.length; i++) {
        int temp = i;
        while (temp < list.size() - 1) {
            res *= list.get(temp);
            temp++;
        }
        newarray[i] = res;
        list.add(array[i]);
        res = 1;
    }

输出:[24,120,60,40,30]

左旅行->右和保持保存产品。称之为过去。- > O (n) 旅行右->左保持产品。称之为未来。- > O (n) 结果[i] =过去[i-1] *将来[i+1] -> O(n) 过去[-1]= 1;和未来(n + 1) = 1;

O(n)

这是我的代码:

int multiply(int a[],int n,int nextproduct,int i)
{
    int prevproduct=1;
    if(i>=n)
        return prevproduct;
    prevproduct=multiply(a,n,nextproduct*a[i],i+1);
    printf(" i=%d > %d\n",i,prevproduct*nextproduct);
    return prevproduct*a[i];
}

int main()
{
    int a[]={2,4,1,3,5};
    multiply(a,5,1,0);
    return 0;
}

使用EcmaScript 2015编码

'use strict'

/*
Write a function that, given an array of n integers, returns an array of all possible products using exactly (n - 1) of those integers.
*/
/*
Correct behavior:
- the output array will have the same length as the input array, ie. one result array for each skipped element
- to compare result arrays properly, the arrays need to be sorted
- if array lemgth is zero, result is empty array
- if array length is 1, result is a single-element array of 1

input array: [1, 2, 3]
1*2 = 2
1*3 = 3
2*3 = 6
result: [2, 3, 6]
*/
class Test {
  setInput(i) {
    this.input = i
    return this
  }
  setExpected(e) {
    this.expected = e.sort()
    return this
  }
}

class FunctionTester {
  constructor() {
    this.tests = [
      new Test().setInput([1, 2, 3]).setExpected([6, 3, 2]),
      new Test().setInput([2, 3, 4, 5, 6]).setExpected([3 * 4 * 5 * 6, 2 * 4 * 5 * 6, 2 * 3 * 5 * 6, 2 * 3 * 4 * 6, 2 * 3 * 4 * 5]),
    ]
  }

  test(f) {
    console.log('function:', f.name)
    this.tests.forEach((test, index) => {
      var heading = 'Test #' + index + ':'
      var actual = f(test.input)
      var failure = this._check(actual, test)

      if (!failure) console.log(heading, 'input:', test.input, 'output:', actual)
      else console.error(heading, failure)

      return !failure
    })
  }

  testChain(f) {
    this.test(f)
    return this
  }

  _check(actual, test) {
      if (!Array.isArray(actual)) return 'BAD: actual not array'
      if (actual.length !== test.expected.length) return 'BAD: actual length is ' + actual.length + ' expected: ' + test.expected.length
      if (!actual.every(this._isNumber)) return 'BAD: some actual values are not of type number'
      if (!actual.sort().every(isSame)) return 'BAD: arrays not the same: [' + actual.join(', ') + '] and [' + test.expected.join(', ') + ']'

      function isSame(value, index) {
        return value === test.expected[index]
      }
  }

  _isNumber(v) {
    return typeof v === 'number'
  }
}

/*
Efficient: use two iterations of an aggregate product
We need two iterations, because one aggregate goes from last-to-first
The first iteration populates the array with products of indices higher than the skipped index
The second iteration calculates products of indices lower than the skipped index and multiplies the two aggregates

input array:
1 2 3
   2*3
1*    3
1*2

input array:
2 3 4 5 6
    (3 * 4 * 5 * 6)
(2) *     4 * 5 * 6
(2 * 3) *     5 * 6
(2 * 3 * 4) *     (6)
(2 * 3 * 4 * 5)

big O: (n - 2) + (n - 2)+ (n - 2) = 3n - 6 => o(3n)
*/
function multiplier2(ns) {
  var result = []

  if (ns.length > 1) {
    var lastIndex = ns.length - 1
    var aggregate

    // for the first iteration, there is nothing to do for the last element
    var index = lastIndex
    for (var i = 0; i < lastIndex; i++) {
      if (!i) aggregate = ns[index]
      else aggregate *= ns[index]
      result[--index] = aggregate
    }

    // for second iteration, there is nothing to do for element 0
    // aggregate does not require multiplication for element 1
    // no multiplication is required for the last element
    for (var i = 1; i <= lastIndex; i++) {
      if (i === 1) aggregate = ns[0]
      else aggregate *= ns[i - 1]
      if (i !== lastIndex) result[i] *= aggregate
      else result[i] = aggregate
    }
  } else if (ns.length === 1) result[0] = 1

  return result
}

/*
Create the list of products by iterating over the input array

the for loop is iterated once for each input element: that is n
for every n, we make (n - 1) multiplications, that becomes n (n-1)
O(n^2)
*/
function multiplier(ns) {
  var result = []

  for (var i = 0; i < ns.length; i++) {
    result.push(ns.reduce((reduce, value, index) =>
      !i && index === 1 ? value // edge case: we should skip element 0 and it's the first invocation: ignore reduce
      : index !== i ? reduce * value // multiply if it is not the element that should be skipped
      : reduce))
  }

  return result
}

/*
Multiply by clone the array and remove one of the integers

O(n^2) and expensive array manipulation
*/
function multiplier0(ns) {
  var result = []

  for (var i = 0; i < ns.length; i++) {
    var ns1 = ns.slice() // clone ns array
    ns1.splice(i, 1) // remove element i
    result.push(ns1.reduce((reduce, value) => reduce * value))
  }

  return result
}

new FunctionTester().testChain(multiplier0).testChain(multiplier).testChain(multiplier2)

使用Node.js v4.4.5运行:

Node—harmony integerarrays.js

function: multiplier0
Test #0: input: [ 1, 2, 3 ] output: [ 2, 3, 6 ]
Test #1: input: [ 2, 3, 4, 5, 6 ] output: [ 120, 144, 180, 240, 360 ]
function: multiplier
Test #0: input: [ 1, 2, 3 ] output: [ 2, 3, 6 ]
Test #1: input: [ 2, 3, 4, 5, 6 ] output: [ 120, 144, 180, 240, 360 ]
function: multiplier2
Test #0: input: [ 1, 2, 3 ] output: [ 2, 3, 6 ]
Test #1: input: [ 2, 3, 4, 5, 6 ] output: [ 120, 144, 180, 240, 360 ]