代码之家  ›  专栏  ›  技术社区  ›  polygenelubricants

给定一个数字数组,返回所有其他数字的乘积数组(无除法)

  •  170
  • polygenelubricants  · 技术社区  · 15 年前

    我是在一次面试中被问到这个问题的,我想知道其他人会如何解决这个问题。我最喜欢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]
    

    你必须穿上 O(N) 不用除法。

    41 回复  |  直到 6 年前
        1
  •  230
  •   Community CDub    8 年前

    对…的解释 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,  }
    

    这两种方法都可以在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];
    }
    

    如果你在太空中也需要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];
    }
    
        2
  •  48
  •   smac89    12 年前

    这里是一个小的递归函数(在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;
    }
    
        3
  •  15
  •   Community CDub    8 年前

    这是我尝试用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[i-1] pj = nums[N-1] * nums[N-2] *.. nums[j+1] . 这个 i 左边的部分是“前缀”逻辑,而 j 右边的部分是“后缀”逻辑。


    递归一行

    Jasmeet 给了一个(漂亮的!)递归解决方案;我把它变成了这个(可怕的!)爪哇一班轮。它确实 就地修改 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]"
    
        4
  •  14
  •   smac89    12 年前

    将迈克尔安德森的解决方案翻译成哈斯克尔:

    otherProducts xs = zipWith (*) below above
    
         where below = scanl (*) 1 $ init xs
    
               above = tail $ scanr (*) 1 xs
    
        5
  •  12
  •   smac89    12 年前

    鬼鬼祟祟地规避“不分裂”规则:

    sum = 0.0
    for i in range(a):
      sum += log(a[i])
    
    for i in range(a):
      output[i] = exp(sum - log(a[i]))
    
        6
  •  10
  •   raoadnan    12 年前

    这里是一个简单明了的解决方案,具有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]);
        }
    
        7
  •  5
  •   wilhelmtell    15 年前

    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));
    
        8
  •  5
  •   user3177227    10 年前
    1. 向左-向右移动并继续保存产品。算了吧。gt;o(n)
    2. 向右行驶->向左保留产品。称之为未来。-gt;o(n)
    3. 结果[i]=过去[i-1]*未来[i+1]->o(n)
    4. 过去的[-1]=1;未来的[n+1]=1;

    o(n)

        9
  •  3
  •   Paul R    9 年前

    这是我在现代C++中的解决方案。它利用了 std::transform 而且很容易记住。

    Online code (wandbox).

    #include<algorithm>
    #include<iostream>
    #include<vector>
    
    using namespace std;
    
    vector<int>& multiply_up(vector<int>& v){
        v.insert(v.begin(),1);
        transform(v.begin()+1, v.end()
                 ,v.begin()
                 ,v.begin()+1
                 ,[](auto const& a, auto const& b) { return b*a; }
                 );
        v.pop_back();
        return v;
    }
    
    int main() {
        vector<int> v = {1,2,3,4,5};
        auto vr = v;
    
        reverse(vr.begin(),vr.end());
        multiply_up(v);
        multiply_up(vr);
        reverse(vr.begin(),vr.end());
    
        transform(v.begin(),v.end()
                 ,vr.begin()
                 ,v.begin()
                 ,[](auto const& a, auto const& b) { return b*a; }
                 );
    
        for(auto& i: v) cout << i << " "; 
    }
    
        10
  •  2
  •   Lars    15 年前

    这是o(n^2),但f是如此美丽:

    List.fold (fun seed i -> List.mapi (fun j x -> if i=j+1 then x else x*i) seed) 
              [1;1;1;1;1]
              [1..5]
    
        11
  •  1
  •   Daniel Migowski    15 年前

    阿德里安·索斯:

    使用以下选项:

    public int[] calc(int[] params) {
    
    int[] left = new int[n-1]
    in[] right = new int[n-1]
    
    int fac1 = 1;
    int fac2 = 1;
    for( int i=0; i<n; i++ ) {
        fac1 = fac1 * params[i];
        fac2 = fac2 * params[n-i];
        left[i] = fac1;
        right[i] = fac2; 
    }
    fac = 1;
    
    int[] results = new int[n];
    for( int i=0; i<n; i++ ) {
        results[i] = left[i] * right[i];
    }
    

    是的,我肯定我错过了一些I-1而不是我,但这是解决问题的方法。

        12
  •  1
  •   kolistivra    15 年前

    还有一个o(n^(3/2)) 非最佳 解决方案。不过,这很有趣。

    首先对大小为n^0.5的每个部分乘法进行预处理(这是在o(n)时间复杂度中完成的)。然后,可以在2*o(n^0.5)时间内计算每个数字的其他值'-倍数(为什么?因为只需要将其他((n^0.5)-1)数字的最后一个元素相乘,然后将结果与属于当前数字组的((n^0.5)-1)数字相乘。对每个数字这样做,可以得到o(n^(3/2))时间。

    例子:

    4 6 7 2 3 1 9 5 8

    部分结果: 4×6×7=168 2×3×1=6 9×5×8=360

    要计算3的值,需要将其他组的值168*360乘以2*1。

        13
  •  1
  •   Kareem    12 年前
    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5 };
        int[] result = { 1, 1, 1, 1, 1 };
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < i; j++) {
                result[i] *= arr[j];
    
            }
            for (int k = arr.length - 1; k > i; k--) {
                result[i] *= arr[k];
            }
        }
        for (int i : result) {
            System.out.println(i);
        }
    }
    

    我想出了这个解决方案,我发现它很清楚你怎么想!?

        14
  •  1
  •   wildplasser    12 年前

    预先计算每个元素左边和右边数字的乘积。 对于每个元素,期望值都是其邻居产品的乘积。

    #include <stdio.h>
    
    unsigned array[5] = { 1,2,3,4,5};
    
    int main(void)
    {
    unsigned idx;
    
    unsigned left[5]
            , right[5];
    left[0] = 1;
    right[4] = 1;
    
            /* calculate products of numbers to the left of [idx] */
    for (idx=1; idx < 5; idx++) {
            left[idx] = left[idx-1] * array[idx-1];
            }
    
            /* calculate products of numbers to the right of [idx] */
    for (idx=4; idx-- > 0; ) {
            right[idx] = right[idx+1] * array[idx+1];
            }
    
    for (idx=0; idx <5 ; idx++) {
            printf("[%u] Product(%u*%u) = %u\n"
                    , idx, left[idx] , right[idx]  , left[idx] * right[idx]  );
            }
    
    return 0;
    }
    

    结果:

    $ ./a.out
    [0] Product(1*120) = 120
    [1] Product(1*60) = 60
    [2] Product(2*20) = 40
    [3] Product(6*5) = 30
    [4] Product(24*1) = 24
    

    (更新:现在我再仔细看一下,它使用的方法与上面的michael anderson、daniel migowski和polygeneloilutes相同)

        15
  •  1
  •   Nitin    12 年前
    def productify(arr, prod, i):
        if i < len(arr):
                prod.append(arr[i - 1] * prod[i - 1]) if i > 0 else prod.append(1)
                retval = productify(arr, prod, i + 1)
                prod[i] *= retval
                return retval * arr[i]
        return 1
    

    arr=[1,2,3,4,5] PROD= 生产(arr,prod,0) 打印戳

        16
  •  1
  •   Billz    11 年前

    下面是scala中的代码:

    val list1 = List(1, 2, 3, 4, 5)
    for (elem <- list1) println(list1.filter(_ != elem) reduceLeft(_*_))
    

    这将打印出以下内容:

    120
    60
    40
    30
    24
    

    程序将过滤掉当前元素(!=elem);并用reduceleft方法乘以新列表。我认为如果您使用scala视图或迭代器进行延迟计算,那么这将是o(n)。

        17
  •  1
  •   junkgui    8 年前

    基于billz的回答——抱歉,我不能发表评论,但这里有一个scala版本,它可以正确处理列表中的重复项,可能是o(n):

    val list1 = List(1, 7, 3, 3, 4, 4)
    val view = list1.view.zipWithIndex map { x => list1.view.patch(x._2, Nil, 1).reduceLeft(_*_)}
    view.force
    

    返回:

    List(1008, 144, 336, 336, 252, 252)
    
        18
  •  1
  •   soulus    7 年前

    在这里添加我的javascript解决方案,因为我没有发现任何人建议这样做。 什么是除法,除了计算从另一个数中提取一个数的次数之外?我计算了整个数组的乘积,然后遍历每个元素,将当前元素减至零:

    //No division operation allowed
    // keep substracting divisor from dividend, until dividend is zero or less than divisor
    function calculateProducsExceptCurrent_NoDivision(input){
      var res = [];
      var totalProduct = 1;
      //calculate the total product
      for(var i = 0; i < input.length; i++){
        totalProduct = totalProduct * input[i];
      }
      //populate the result array by "dividing" each value
      for(var i = 0; i < input.length; i++){
        var timesSubstracted = 0;
        var divisor = input[i];
        var dividend = totalProduct;
        while(divisor <= dividend){
          dividend = dividend - divisor;
          timesSubstracted++;
        }
        res.push(timesSubstracted);
      }
      return res;
    }
    
        19
  •  1
  •   RodrigoCampos    7 年前

    我习惯于C:

        public int[] ProductExceptSelf(int[] nums)
        {
            int[] returnArray = new int[nums.Length];
            List<int> auxList = new List<int>();
            int multTotal = 0;
    
            // If no zeros are contained in the array you only have to calculate it once
            if(!nums.Contains(0))
            {
                multTotal = nums.ToList().Aggregate((a, b) => a * b);
    
                for (int i = 0; i < nums.Length; i++)
                {
                    returnArray[i] = multTotal / nums[i];
                }
            }
            else
            {
                for (int i = 0; i < nums.Length; i++)
                {
                    auxList = nums.ToList();
                    auxList.RemoveAt(i);
                    if (!auxList.Contains(0))
                    {
                        returnArray[i] = auxList.Aggregate((a, b) => a * b);
                    }
                    else
                    {
                        returnArray[i] = 0;
                    }
                }
            }            
    
            return returnArray;
        }
    
        20
  •  0
  •   Vijay    15 年前

    这个解决方案可以被认为是C/C++。 假设我们有一个包含n个元素的数组“a” 像[n]一样,伪代码如下。

    for(j=0;j<n;j++)
      { 
        prod[j]=1;
    
        for (i=0;i<n;i++)
        {   
            if(i==j)
            continue;  
            else
            prod[j]=prod[j]*a[i];
      }
    
        21
  •  0
  •   Alam    15 年前

    还有一个解决方案,使用除法。两次穿越。 将所有元素相乘,然后开始除以每个元素。

        22
  •  0
  •   Fysx    14 年前
    {-
    使用sqrt(n)子集的递归解。在o(n)中运行。
    
    递归地计算大小为sqrt(n)的sqrt(n)子集上的解。
    然后在每个子集的乘积和上递归。
    然后,对于每个子集中的每个元素,它用
    所有其他产品的乘积和。
    然后展平所有子集。
    
    运行时的递归为t(n)=sqrt(n)*t(sqrt(n))+t(sqrt(n))+n
    
    假设t(n)≤cn在o(n)中。
    
    t(n)=sqrt(n)*t(sqrt(n))+t(sqrt(n))+n
    小于等于sqrt(n)*c*sqrt(n)+c*sqrt(n)+n
    
    
        23
  •  0
  •   Anantha Krishnan    14 年前

    这是我的代码:

    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;
    }
    
        24
  •  0
  •   Ian Newson    13 年前

    下面是一个稍微有用的例子,使用c:

                Func<long>[] backwards = new Func<long>[input.Length];
                Func<long>[] forwards = new Func<long>[input.Length];
    
                for (int i = 0; i < input.Length; ++i)
                {
                    var localIndex = i;
                    backwards[i] = () => (localIndex > 0 ? backwards[localIndex - 1]() : 1) * input[localIndex];
                    forwards[i] = () => (localIndex < input.Length - 1 ? forwards[localIndex + 1]() : 1) * input[localIndex];
                }
    
                var output = new long[input.Length];
                for (int i = 0; i < input.Length; ++i)
                {
                    if (0 == i)
                    {
                        output[i] = forwards[i + 1]();
                    }
                    else if (input.Length - 1 == i)
                    {
                        output[i] = backwards[i - 1]();
                    }
                    else
                    {
                        output[i] = forwards[i + 1]() * backwards[i - 1]();
                    }
                }
    

    我不是 完全地 肯定这是o(n),因为创建的函数是半递归的,但是我的测试似乎表明它是o(n)及时的。

        25
  •  0
  •   user3552947    11 年前

    //这是Java中的递归解决方案 //从主产品(A,1,0)调用如下;

    public static double product(double[] a, double fwdprod, int index){
        double revprod = 1;
        if (index < a.length){
            revprod = product2(a, fwdprod*a[index], index+1);
            double cur = a[index];
            a[index] = fwdprod * revprod;
            revprod *= cur;
        }
        return revprod;
    }
    
        26
  •  0
  •   Aaron Hall    10 年前

    O(N)运行时的整洁解决方案:

    1. 对于每个元素,计算在此之前发生的所有元素的乘积,并将其存储在数组“pre”中。
    2. 对于每个元素,计算该元素之后出现的所有元素的乘积,并将其存储在数组“post”中
    3. 创建最后一个数组“result”,对于元素i,

      result[i] = pre[i-1]*post[i+1];
      
        27
  •  0
  •   Farid Movsumov    10 年前
    function solution($array)
    {
        $result = [];
        foreach($array as $key => $value){
            $copyOfOriginalArray = $array;
            unset($copyOfOriginalArray[$key]);
            $result[$key] = multiplyAllElemets($copyOfOriginalArray);
        }
        return $result;
    }
    
    /**
     * multiplies all elements of array
     * @param $array
     * @return int
     */
    function multiplyAllElemets($array){
        $result = 1;
        foreach($array as $element){
            $result *= $element;
        }
        return $result;
    }
    
    $array = [1, 9, 2, 7];
    
    print_r(solution($array));
    
        28
  •  0
  •   SiddP    8 年前

    下面是另一个简单的概念,它解决了 O(N) .

            int[] arr = new int[] {1, 2, 3, 4, 5};
            int[] outArray = new int[arr.length]; 
            for(int i=0;i<arr.length;i++){
                int res=Arrays.stream(arr).reduce(1, (a, b) -> a * b);
                outArray[i] = res/arr[i];
            }
            System.out.println(Arrays.toString(outArray));
    
        29
  •  0
  •   Quinn    8 年前

    我们可以排除 nums[j] (何处) j != i )首先从列表中,然后得到其余部分的乘积;下面是 python way 要解决这个难题:

    def products(nums):
        return [ reduce(lambda x,y: x * y, nums[:i] + nums[i+1:]) for i in range(len(nums)) ]
    print products([1, 2, 3, 4, 5])
    
    [out]
    [120, 60, 40, 30, 24]
    
        30
  •  0
  •   Arefe    7 年前

    我有个解决办法 O(n) 空间与 O(n^2) 时间复杂性如下:

    public static int[] findEachElementAsProduct1(final int[] arr) {
    
            int len = arr.length;
    
    //        int[] product = new int[len];
    //        Arrays.fill(product, 1);
    
            int[] product = IntStream.generate(() -> 1).limit(len).toArray();
    
    
            for (int i = 0; i < len; i++) {
    
                for (int j = 0; j < len; j++) {
    
                    if (i == j) {
                        continue;
                    }
    
                    product[i] *= arr[j];
                }
            }
    
            return product;
        }