分散数组中的重复项

时间:2013-07-27 15:16:22

标签: performance algorithm

来源:Google面试问题

编写例程以确保输入中的相同元素在输出中最大分布?

基本上,我们需要以这样的方式放置相同的元素,即 TOTAL 传播尽可能最大。

示例:

Input: {1,1,2,3,2,3}

Possible Output: {1,2,3,1,2,3}  

Total dispersion = Difference between position of 1's + 2's + 3's = 4-1 + 5-2 + 6-3 = 9 .

一点都不肯定,如果有一个最佳的多项式时间算法可用于此。此外,除此之外,没有为其他问题提供其他细节。

我的想法是,计算输入中每​​个元素的频率,然后将它们一次排列在输出中,每个不同的元素,直到所有频率都耗尽。

我不确定我的方法。

任何方法/想法的人。

4 个答案:

答案 0 :(得分:4)

我相信这个简单的算法可行:

  • 计算每个不同元素的出现次数。
  • 制作新名单
  • 将多次出现的所有元素的一个实例添加到列表 (每个组中的顺序无关紧要)
  • 将所有唯一元素的一个实例添加到列表
  • 将多次出现的所有元素的一个实例添加到列表中
  • 将列表中出现两次以上的所有元素的一个实例添加到列表中
  • 将列表中多于一个的所有元素的一个实例添加到列表中
  • ...

现在,这将直观地没有给出好的传播:
对于{1, 1, 1, 1, 2, 3, 4} ==> {1, 2, 3, 4, 1, 1, 1}
对于{1, 1, 1, 2, 2, 2, 3, 4} ==> {1, 2, 3, 4, 1, 2, 1, 2}

但是,我认为这是您提供的评分功能可以获得的最好的传播。 由于色散分数计算距离的总和而不是距离的平方和,因此只要在其他地方有较大的间隙进行补偿,就可以将多个重复点靠近在一起。

对于平方距离总和得分,问题变得更难。 也许面试问题取决于候选人是否认识到评分功能的弱点?

答案 1 :(得分:1)

在perl

@a=(9,9,9,2,2,2,1,1,1);

然后创建列表中不同数字计数的哈希表,如频率表

map { $x{$_}++ } @a;

然后重复遍历找到的所有键,按照已知的顺序键,并将相应数量的单个数字添加到输出列表,直到所有键都用完为止

@r=();
$g=1; 
while( $g == 1 ) { 
   $g=0;
   for my $n (sort keys %x) 
      {
      if ($x{$n}>1) {
                    push @r, $n;
                    $x{$n}--;
                    $g=1
                    }
      } 
}

我确信这可以适用于任何支持哈希表的编程语言

答案 2 :(得分:0)

Vorsprung和HugoRune建议的算法的python代码:

from collections import Counter, defaultdict

def max_spread(data):
    cnt = Counter()
    for i in data: cnt[i] += 1

    res, num  = [], list(cnt)
    while len(cnt) > 0:
        for i in num:
            if num[i] > 0:
                res.append(i)
                cnt[i] -= 1
            if cnt[i] == 0: del cnt[i]

    return res

def calc_spread(data):
    d = defaultdict()
    for i, v in enumerate(data):
        d.setdefault(v, []).append(i)

    return sum([max(x) - min(x) for _, x in d.items()])

答案 3 :(得分:0)

HugoRune's answer利用了不寻常的评分功能,但我们实际上可以做得更好:假设有不同的非唯一值,那么 only 就需要了最佳解决方案是输出中的前d个值必须以任何顺序包含这些值,同样输出中的最后d值必须包含 any (即可能是不同的)订单。 (这意味着所有唯一数字出现在每个非唯一数字的第一个和最后一个实例之间。)

非唯一数字的第一个副本的相对顺序无关紧要,同样也不是它们最后副本的相对顺序。假设值1和2都出现多次输入,并且我们已经建立了一个候选解决方案,遵守我在第一段中给出的条件,该条件在位置i处具有1的第一副本,在位置j处具有2的第一副本。一世。现在假设我们交换这两个元素。元素1已被推到右侧j - i位置,因此其得分贡献将下降j - i。但是元素2已被推到左侧j - i位置,因此其得分贡献将增加j - i。这些取消,总分不变。

现在,任何元素的排列都可以通过以下方式交换元素来实现:将位置1中的元素与位于位置1的元素交换,然后对位置2执行相同操作, 等等。在第i步之后,置换的前i个元素是正确的。我们知道每个交换都会保持评分函数不变,并且排列只是一系列交换,因此每个排列也会使评分函数保持不变!这适用于输出数组两端的d元素。

当存在3个或更多个数字副本时,只有第一个和最后一个副本的位置有助于该数字的距离。中间的位置并不重要。我将两个d元素块之间的元素称为“中心”元素。它们由独特的元素组成,以及至少出现3次的所有非独特元素的一些副本。和以前一样,很容易看出这些“中心”元素的任何排列都对应于一系列交换,并且任何这样的交换都会使整体得分保持不变(实际上它比以前更简单,因为交换两个中心元素不会甚至改变这些元素中任何一个的得分贡献。)

这导致一个简单的O(nlog n)算法(如果你在第一步使用桶排序,则为O(n))从长度为n的输入数组X生成解决方案数组Y:

  1. 对输入数组X进行排序。
  2. 使用单次传递X来计算不同的非唯一元素的数量。叫这个d。
  3. 将i,j和k设为0。
  4. 虽然我< N:
    • 如果X [i + 1] == X [i],我们有一个非唯一元素:
      • 设置Y [j] = Y [n-j-1] = X [i]。
      • 增加i两次,并增加j一次。
      • 当X [i] == X [i-1]时:
        • 设置Y [d + k] = X [i]。
        • 增加i和k。
    • 否则我们有一个独特的元素:
      • 设置Y [d + k] = X [i]。
      • 增加i和k。