亚马逊面试问题

问题描述

| 给定每个大小为K的N个数组。.对N个数组中的K个元素中的每一个进行排序,这些N * K个元素中的每个元素都是唯一的。从N个元素的选定子集中,从N个数组中的每一个中选择一个元素。减去最小和最大元素。现在,这个 差异应该是最小可能的。。希望问题是清楚的:) :) 样品:
N=3,K=3

N=1 : 6,16,67
N=2 : 11,17,68
N=3 : 10,15,100
如果选择了16、17、15,那么我们得到的最小差为 17-15 = 2。     

解决方法

我可以想到O(N * K * N)(由zivo正确指出后编辑,现在不是一个好的解决方案:()解决方案。 1.以N指针初始指向N个数组中的每个数组的初始元素。
6,16,67
^ 
11,17,68
^
10,15,100
^ 
2.找出当前指针O(k)(6和11)中的最高和最低元素,并找出它们之间的差。(5) 3.将指向该数组中最低元素的指针加1。
 6,67
    ^ 
 11,68
 ^
 10,100 (difference:5)
 ^ 
4.继续重复步骤2和3,并存储最小差异。
 6,100 (difference:5)
    ^ 


 6,68
    ^
 10,100 (difference:2)
    ^ 
以上将是必需的解决方案。
 6,100 (difference:84)
       ^ 

 6,67
        ^ 
 11,100 (difference:83)
       ^ 
等等...... 编辑: 通过使用堆可以降低其复杂性(如Uri所建议)。我想到了,但遇到了一个问题:每次从堆中提取一个元素时,都必须找出其数组编号,以增加该数组的相应指针。查找数组编号的有效方法肯定可以将复杂度降低到O(K * N log(K * N))。一种幼稚的方法是使用这样的数据结构
Struct
{
    int element;
    int arraynumer;
}
并像这样重建初始数据
 6|0,16|0,67|0

 11|1,17|1,68|1

 10|2,15|2,100|2
首先保留第一列的当前最大值,然后将指向的元素插入堆中。现在每次提取一个元素时,都可以找到其数组编号,该数组中的指针增加,可以将新指向的元素与当前的max进行比较,并且可以相应地调整max指针。     ,因此,这里有一个分两步解决此问题的算法: 第一步是将所有数组合并为一个排序后的数组,如下所示: Combined_val []-包含所有数字 Combined_ind []-保留此数字最初属于哪个数组的索引 此步骤可以在O(K * N * log(N))中轻松完成,但我认为您也可以做得更好(也许不能,您可以查找merge sort的变体,因为它们的步骤与此类似) 现在第二步: 放置代码而不是进行解释比较容易,所以这里是pseduocode:
int count[N] = { 0 }
int head = 0;
int diffcnt = 0;
// mindiff is initialized to overall maximum value - overall minimum value
int mindiff = combined_val[N * K - 1] - combined_val[0];
for (int i = 0; i < N * K; i++) 
{
  count[combined_ind[i]]++;

  if (count[combined_ind[i]] == 1) {
    // diffcnt counts how many arrays have at least one element between
    // indexes of \"head\" and \"i\". Once diffcnt reaches N it will stay N and
    // not increase anymore
    diffcnt++;
  } else {
    while (count[combined_ind[head]] > 1) {
      // We try to move head index as forward as possible while keeping diffcnt constant.
      // i.e. if count[combined_ind[head]] is 1,then if we would move head forward
      // diffcnt would decrease,that is something we dont want to do.
      count[combined_ind[head]]--;
      head++;
    }
  }

  if (diffcnt == N) {
    // i.e. we got at least one element from all arrays
    if (combined_val[i] - combined_val[head] < mindiff) {
      mindiff = combined_val[i] - combined_val[head];
      // if you want to save actual numbers too,you can save this (i.e. i and head
      // and then extract data from that)
    }
  }
}
结果令人不解。 第二步的运行时间为O(N * K)。这是因为\“ head \”索引最多只能移动N * K倍。因此,内循环不会使它变成二次方,而是线性的。 因此,算法的总运行时间为O(N * K * log(N)),但这是由于合并步骤的缘故,如果您可以提出更好的合并步骤,则可以将其降低到O(N * K)。     ,这个问题是针对经理 您有3个开发人员(N1),3个测试人员(N2)和3个DBA(N3) 选择可以成功运行项目的分歧较小的团队。
int[n] result;// where result[i] keeps the element from bucket N_i

int[n] latest;//where latest[i] keeps the latest element visited from bucket N_i

Iterate elements in (N_1 + N_2 + N_3) in sorted order
{
    Keep track of latest element visited from each bucket N_i by updating \'latest\' array;

    if boundary(latest) < boundary(result)
    {
       result = latest;
    }
}

int boundary(int[] array)
{
   return Max(array) - Min(array);
}
    ,我有O(K * N * log(K)),典型的执行要少得多。目前尚无更好的选择。我将首先说明更容易描述的内容(执行时间更长): 对于第一个数组中的每个元素f(循环遍历K个元素) 对于每个数组,从第二个数组开始(循环到N-1个数组) 在数组上执行二进制搜索,然后找到最接近f的元素。这是您的元素(Log(K)) 如果为每个阵列添加一个新的楼层索引,则可以优化该算法。执行二进制搜索时,在\'Floor \'到\'K-1 \'之间搜索。 最初,楼层索引为0,对于第一个元素,您将搜索整个数组。一旦找到最接近\'f \'的元素,就用该元素的索引更新地板索引。更糟的情况是相同的(如果第一个数组的最大元素小于任何其他最小值,则Floor可能不会更新),但平均情况会有所改善。     ,可接受答案的正确性证明(终端解决方案) 假设该算法找到一个不是最优解(R)的序列A = 。 考虑R中的索引j,以使项目R [j]是R中算法检查的第一个项目,并将其替换为该行中的下一个项目。 令A \'表示该阶段(替换之前)的候选解决方案。由于R [j] = A \'[j]是A \'的最小值,所以它也是R的最小值。 现在,考虑R的最大值R [m]。如果A \'[m]
    choose the element in 2nd array that is closest to the element in 1st array
    current_array = 2;
    do
    {
        choose the element in current_array+1 that is closest to the element in current_array
        current_array++;
    } while(current_array < n);
复杂度:O(k ^ 2 * n)     ,这是我如何解决此问题的逻辑,请记住,我们需要从N个数组中各选择一个元素(以计算最小值)
// if we take the above values as an example!
// then the idea would be to sort all three arrays while keeping another
// array to keep the reference to their sets (1 or 2 or 3,could be 
// extended to n sets)      
1   3   2   3   1   2   1   2   3    // this is the array that holds the set index
6   10  11  15  16  17  67  68  100  // this is the sorted combined array.
           |           |   
    5            2          33       // this is the computed least minimum,// the rule is to make sure the indexes of the values 
                                     // we are comparing are different (to make sure we are 
// comparing elements from different sets),then for example
// the first element of that example is index:1|value:6 we hold 
// that value 6 (that is the value we will be using to compute the least minimum,// then we go to the edge of the comparison which would be the second different index,// we skip index:3|value:10 (we remove it from the array) we compare index:2|value:11 
// to index:1|value:6 we obtain 5 which would go to a variable named leastMinimum = 5,// now we remove the indexes and values we already used,// and redo the same steps.
步骤1:
1   3   2   3   1   2   1   2   3
6   10  11  15  16  17  67  68  100
           |   
5            
leastMinumum = 5
第2步:
3   1   2   1   2   3
15  16  17  67  68  100
           |   
 2          
leastMinimum = min(2,leastMinumum) // which is equal 2
第三步:
1   2   3
67  68  100

    33
leastMinimum = min(33,leastMinumum) // which is equal to old leastMinumum which is 2
现在:假设我们有同一数组中的元素彼此非常接近(这次k = 2,这意味着我们只有3个带有两个值的集合):
// After sorting the n arrays we will have the below indexes array and values array
1   1   2   3   2   3
6   7   8   12  15  16
*       *   *

* we skip second index of 1|7 and we take the least minimum of 1|6 and 3|12 (index:2|value:8 will be removed as it is not at the edges,we pick the minimum and maximum of the unique index subset of n elements)
1   3         
6   12
 =6
* second step we remove the values we already used,so the array become like below:

1   2   3
7   15  16
*   *   * 
7 - 16
= 9
注意: 消耗更多内存的另一种方法是创建N个子数组,从中我们将比较最大-最小 因此,从下面的排序值数组及其对应的索引数组中,我们提取了其他三个子数组:
1   3   2   3   1   2   1   2   3
6   10  11  15  16  17  67  68  100
第一个数组:
1   3   2 
6   10  11
11-6 = 5 第二个数组:
3   1   2
15  15  17
17-15 = 2 第三阵列:
1   2   3
67  68  100
100-67 = 33