Учитывая массив чисел, возвращаемый массив продуктов всех других чисел (никакое подразделение)

Меня задали этот вопрос в собеседовании, и я хотел бы знать, как другие решат его. Я являюсь самым довольным 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) не используя подразделение.

180
задан Mat 3 May 2013 в 23:24
поделиться

12 ответов

Объяснение метода 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) в пространстве, вы можете сделать это (что менее понятно IMHO)

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];
}
244
ответ дан 23 November 2019 в 06:12
поделиться

Это 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]
2
ответ дан 23 November 2019 в 06:12
поделиться

Хитрость:

Используйте следующее:

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 вместо i, но это способ реши это.

1
ответ дан 23 November 2019 в 06:12
поделиться

Вот моя попытка решить эту проблему на 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] * .. число [j + 1] . Часть i слева - это логика «префикса», а часть j справа - логика «суффикса».


Рекурсивный однострочный

Джасмит дал (красивое!) Рекурсивное решение; Я превратил его в этот (отвратительный!) Однострочник 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]"
16
ответ дан 23 November 2019 в 06:12
поделиться

Перевод решения Майкла Андерсона на Haskell:

otherProducts xs = zipWith (*) below above

     where below = scanl (*) 1 $ init xs

           above = tail $ scanr (*) 1 xs
14
ответ дан 23 November 2019 в 06:12
поделиться

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));
6
ответ дан 23 November 2019 в 06:12
поделиться

Что ж, это решение можно считать решением C / C ++. { {1}} Допустим, у нас есть массив "a", содержащий 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];
  }
0
ответ дан 23 November 2019 в 06: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;
}
49
ответ дан 23 November 2019 в 06: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]))
13
ответ дан 23 November 2019 в 06:12
поделиться

Также существует 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.

1
ответ дан 23 November 2019 в 06:12
поделиться

Еще одно решение, использующее деление. с двойным обходом. Умножьте все элементы, а затем начните делить их на каждый элемент.

0
ответ дан 23 November 2019 в 06:12
поделиться
    int[] arr1 = { 1, 2, 3, 4, 5 };
    int[] product = new int[arr1.Length];              

    for (int i = 0; i < arr1.Length; i++)
    {
        for (int j = 0; j < product.Length; j++)
        {
            if (i != j)
            {
                product[j] = product[j] == 0 ? arr1[i] : product[j] * arr1[i];
            }
        }
    }
-1
ответ дан 23 November 2019 в 06:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: