如何以小于线性的时间对位数组中的位进行分区

时间:2010-03-17 07:08:07

标签: c++

这是我最近面临的一个面试问题。


给定一个1和0的数组,找到一种方法来对位in place进行分区,以便将0组合在一起,并将1组合在一起。 1是在0之前还是0在1之前是无关紧要的。

示例输入为101010101,输出为111110000000011111

在不到线性的时间内解决问题。

使问题更简单。输入是一个整数数组,每个元素为1或0.输出是相同的整数数组,整数分区很好。


对我来说,如果可以在 O (N)中解决这个问题,这是一个简单的问题。我的方法是使用两个指针,从数组的两端开始。增加和减少每个指针;如果它没有指向正确的整数,则交换两个。

    int * start = array;
    int * end = array + length - 1;

    while (start < end) {
        // Assume 0 always at the end
        if (*end == 0) {
            --end; 
            continue;
        }

        // Assume 1 always at the beginning
        if (*start == 1) {
            ++start; 
            continue;
        }

        swap(*start, *end);
    }

然而,采访坚持认为存在一个亚线性解决方案。这让我苦苦思索,但仍未得到答案。

有人可以帮助解决这个面试问题吗?

更新:看到SO中的回复说明问题无法在次线性时间内解决,我可以确认我原来的想法,即不存在子线性的解决方案。

面试官有可能发挥作用吗?

9 个答案:

答案 0 :(得分:8)

我没有看到解决方案如何比线性时间更快。

想象一下所有1的数组。任何解决方案都需要在声明它已经被分区之前检查该数组中的每一位。检查每一位都需要线性时间。

答案 1 :(得分:8)

这是不可能的。在不到线性时间内完成它意味着您不会查看每个数组元素(如二进制搜索)。但是,由于无法在不查看数组的情况下知道数组的任何元素,因此必须至少查看一次数组元素。

你可以使用查找表来加快速度,但是O(n / 8)仍然是O(n),所以面试官错了或你误解了这个问题。

答案 2 :(得分:3)

如果有足够的内存,可以在线性时间内更快,可以在O(1)中完成

使用位掩码作为映射到分区位掩码的向量中的索引。

使用您的示例,在索引341(101010101)处存储值496(111110000)。

答案 3 :(得分:2)

也许混淆来自“不到线性时间”。例如,该解决方案计算位数,使得掩码包含许多位。它只计算位,而有不计数的on-bits:

// from http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
unsigned count_bits(unsigned pX)
{
    unsigned result;
    for (result = 0; v; ++result)
    {
        pX &= pX - 1;
    }

    return result;
}

unsigned n = /* the number */;

// r contains 000...111, with number of 1's equal to number of 1's in v
unsigned r = 1 << count_bits(n); 

即使这最小化了要计数的位数,它仍然是线性的。因此,如果这是“次线性”的含义,那就去吧。

但如果它们真的意味着对数或常数的次线性,我看不到任何方法。你可以想象为每个值制作一个查找表,但是:/

答案 4 :(得分:2)

从技术上讲,您可以将阵列的每个元素发送到单独的处理器,然后在不到线性的时间内完成。如果你有N个处理器,你甚至可以在O(1)时间内完成它!

答案 5 :(得分:1)

正如其他人所说,我不相信这可以在不到线性的时间内完成。对于线性时间解决方案,您可以使用STL算法代替您自己的循环:

int a1[8] = {1,0,1,0,1,0,1,0};
std::fill(std::remove(a1, a1+8, 0), a1+8, 0);

答案 6 :(得分:0)

嗯..它可以做'少于线性'的时间(厚脸皮的方法)。

if(n % 2)
{
   // Arrange all 1's to the right and DON'T check the right-most bit, because it's 1
}else{
   // Arrange all 0's to the right and DON'T check the right-most bit, because it's 0.
}

因此,从技术上讲,你将这些位分组的时间小于线性时间:P

答案 7 :(得分:0)

对我而言,最可能的解释是:

  • 这些位应该是int而不是数组,在这种情况下,您可以使用http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan或8位(或更多)查找表。

    < / LI>
  • 他们使用“次线性”来表示“少于n次操作”而不是小于-O(n)。但即使这似乎也不可能出于下面列出的相同原因。

  • 问题中还有另一个误传

  • 否则问题是错误的,因为必须检查数组的所有元素以确定答案,这至少是'n'操作。

首先列出0或1,并且对比特而不是bool的引用让我认为第一个选项是有意的,即使只处理一个单词,它也没有太大的区别。我很想知道面试官的实际想法。

答案 8 :(得分:0)

只有当您假设并行性增长速度比问题大小慢时,才能在并行处理器之间拆分此工作成本N / M(或O(N))。在过去十年左右的时间里,并行(通过GPU)的增长速度比典型的问题规模增长得更快,而且这种趋势在未来几年仍将持续。对于广泛的问题,假设“无限并行”或更准确地说“并行性大于任何预期的问题大小”是有益的,因为GPU和云计算的进步随着时间的推移提供了这样的事情。

假设无限并行 ,这个问题可以在O(logN)时间内解决,因为加上所有0和1位所需的加法运算符是关联的,并且因此至少需要logN时间步骤才能完成。