Algorithm 问题解决面试问题(数组)

Algorithm 问题解决面试问题(数组),algorithm,Algorithm,有一个整数数组,比如说3,5,7,9。您应该创建另一个数组并填充它,以便第二个数组的第0个位置应该是第一个数组中所有数字的乘积,不包括第0个位置的数字,这意味着它应该是5x7x9(不包括3),第二个数组索引1处的数字将是3x7x9(不包括5)的乘积 我想到的第一个答案是有2个for循环,这将导致O(n2)的时间复杂度。后来我想: 将第一个数组(3x5x7x9)中的所有数字相乘,在填充第二个数组时,我将此乘积除以该位置的数字。如果我填充第0个位置,则除以3;如果我填充第1个位置,则除以5,依此类推

有一个整数数组,比如说3,5,7,9。您应该创建另一个数组并填充它,以便第二个数组的第0个位置应该是第一个数组中所有数字的乘积,不包括第0个位置的数字,这意味着它应该是5x7x9(不包括3),第二个数组索引1处的数字将是3x7x9(不包括5)的乘积

我想到的第一个答案是有2个for循环,这将导致O(n2)的时间复杂度。后来我想:

将第一个数组(3x5x7x9)中的所有数字相乘,在填充第二个数组时,我将此乘积除以该位置的数字。如果我填充第0个位置,则除以3;如果我填充第1个位置,则除以5,依此类推。这将使复杂性从O(n2)降低到O(2n)

但采访者表示,不允许分裂。除了在某种数据结构中存储不同的可能倍数并在填充时使用它之外,我想不出其他任何东西。我放弃了,但当被问到答案时,他说他将保留两个正向和反向倍数数组。当被问及解决方案的空间复杂性问题时,他说可以对其进行优化。

我想我可以用O(n log n)来完成

存储前半个数字和后半个数字的乘积

同时存储第一季度、第二季度、第三季度和第四季度的产品

还应储存前八分之一、后八分之一。。。第八

等等

你可以通过计算每一对的乘积,然后是每一对的乘积,依此类推,从头开始构建这个模型

这里的额外数据总量是O(n),需要计算O(n)(易于显示)

现在,为了计算(比如)元素0的输出,取下半部分的乘积,第二季度的乘积(即第一季度的下半部分),第八季度的下半部分的乘积,等等,然后将它们相乘。有logn这样的数字,所以这个操作是logn

要计算元素k的乘积,请将k写入二进制并翻转其位。然后高位告诉你从哪一半开始;下一位告诉您下一步使用剩余一半中的哪一半;下一位告诉您下一步使用剩余季度的哪一半;等等

因此,任何输出元素都可以在logn时间内计算。对n个输出元素中的每一个执行此操作,您将得到O(n logn)

[编辑]

当然,“向前和向后倍数”方法也能起作用。我想我应该更仔细地阅读这个问题。:-)

[编辑2]

至于面试官(和大卫)的解决方案,你实际上不需要构建一个额外的数组。。。假设您可以编辑输出数组中的值

首先,构造输出数组B,使得B[i]=A[0]A[1]…*A[i-1]和B[0]=1。您可以增量执行此操作,因此这是线性时间:

B[0] = 1;
for (i=1 ; i < n ; ++i) {
    B[i] = B[i-1] * A[i];
}
这解决了O(n)中的问题,而无需构建额外的阵列

  • 使用
    A[i]
    是元素1到元素i的乘积

  • 将元素i到n(输入大小)的乘积存储到数组中
    B
    ,其中
    B[i]
    为元素i到元素n的乘积

  • 当需要结果[i]时,使用A[i-1]*B[i+1]。角落案例是 通过这种方式,只需使用B[1]和A[n-2](其中A[n-1]是最后一个) 元素)


  • 我不确定问题是关于空间还是关于解决方案本身。因为每个人都在提供解决方案,这让我觉得他们不理解面试官的解决方案,所以这里有一个解释:

    我们维护两个阵列。第一个是原始数组数的运行乘积。因此,位于
    i
    位置的元素将是原始数组中第一个
    i
    元素的乘积(没有latex,但
    i
    th条目的值为
    pi{j=0到i}j
    )。第二个数组只是与之相反的方向,因此
    i
    th条目将有值:
    pi{j=N-i到N}j

    为了构造所需的最终数组,我们只需沿着两个数组中的每一个运行并乘以条目。因此,最后一个数组中的
    i
    th值将是所有条目的乘积,即
    0
    i-1
    乘以
    i+1
    N
    的乘积,这是第一个数组的
    i-1
    条目和第二个数组的
    i+1
    条目的乘积


    总时间和空间
    O(n)

    我相信他的意思是,给定一个数组A={A_1,…,A_n},他将创建两个新数组:

    F_A={A_1,A_1*A_2,…,A_1*…*A_n}

    可通过在数组上向前迭代时保持累积乘积在线性时间内构造,以及

    B_A={A_1*..*A_n,…,A_n*A_(n-1),A_n}

    它可以在线性时间内构造,方法是在数组上以相反方向迭代时保持累积乘积

    现在,要在结果数组中填充索引i,只需将F_A[i-1]乘以B_A[i+1]:


    F_A[i-1]*B_A[i+1]=[A_1*…*A_(i-1)]*[A_(i+1)*…*A_n]

    关于省略对数呢。不是除法而是减法

    但是,如果您可以修改第一个数组,您可以执行以下操作

    //pop arr2
    r=1
    for(i=len-1;i>=0;i--)
    {
       r=r*arr1[i]
       arr2[i]=r
    }
    
    //modify arr1
    r=1
    for(i=0;i<len;i++)
    {
       r=r*arr1[i]
       arr1[i]=r
    }
    
    //fix arr2
    arr2[0]=arr2[1];
    for(i=1;i<len-1;i--)
    {
         arr2[i]=arr2[i+1]*arr1[i-1];
    }
    arr2[len-1]=arr1[len-2];
    
    //pop arr2
    r=1
    对于(i=len-1;i>=0;i--)
    {
    r=r*arr1[i]
    arr2[i]=r
    }
    //修改arr1
    r=1
    
    对于(i=0;iOne)一个小小的技术诡辩:O(2n)正是O(n)。也许他的意思是在某种意义上它是最优的,因为这个问题的空间复杂度是
    O(n)
    ,而这个解决方案又增加了一个
    O(n)
    ,总的来说仍然是
    O(n)
    。@Charlie知道你的意思……他是说O(2n)和O(3n)是一样的
    //pop arr2
    r=1
    for(i=len-1;i>=0;i--)
    {
       r=r*arr1[i]
       arr2[i]=r
    }
    
    //modify arr1
    r=1
    for(i=0;i<len;i++)
    {
       r=r*arr1[i]
       arr1[i]=r
    }
    
    //fix arr2
    arr2[0]=arr2[1];
    for(i=1;i<len-1;i--)
    {
         arr2[i]=arr2[i+1]*arr1[i-1];
    }
    arr2[len-1]=arr1[len-2];