数组中最常见的元素/在O(n)时间和O(1)空间中确定性地找到相对多数?

时间:2012-08-02 16:18:54

标签: algorithm data-structures

例如,数组的答案:

1,11,3,95,23,8,1

将为1,因为所有其他元素仅出现一次,而1出现两次。

我在stackoverflow上看到的许多类似于这个问题的问题要求找到绝对多数(答案在长度为n的数组中至少发生n / 2),或者使用排序或a回答问题哈希表。前者不是我要求的,后者要么太慢(O(n log n)用于排序)或者使用太多内存(O(n)用于哈希表)。

这样的算法是否存在?如果没有,是否有证据显示为什么不可能?包括一个来源会很好。

4 个答案:

答案 0 :(得分:1)

使用此处的想法:

How can we find a repeated number in array in O(n) time and O(1) space complexity

并应用类似于counting sort的技术。也就是说,创建N个bin(大小为N的数组),其中N是您期望遇到的最大整数。这仍然是O(1)空间。然后,在O(n)时间内遍历原始数组,当遇到值 i 时,将索引 i 的结果数组递增1.然后,迭代结果数组(再次为O(1)时间),找到最大的单个值。该值的索引将是原始列表中最常见的值。

答案 1 :(得分:1)

这不是一个完整的答案,但它应该有助于阐明为什么这个问题很困难。

考虑我们想要设计一种算法,它可以扫描数组(按某种顺序)以找到最常见的元素。在我们的算法运行期间,允许保留一些数据结构S。让我们看看S中有多少信息,因此我们可以将它包含在O(1)内存中。

假设我们的算法已经处理了数组的第一个k元素。现在S可以告诉我们a[0..k]范围内最常见的元素。但是,假设我们知道k+1'st元素,那么我们也会知道范围a[0..k+1]中最常见的元素。如果不能,如果nk+1,我们的算法将无效。更一般地说,鉴于元素a[k..m]S的知识,我们知道a[0..m]中最常见的元素。

我们可以使用上述参数从S中提取信息。假设我们正在处理[0,u]范围内的整数(如果原始数组占用空间O(n),则必须有一些范围)。如果原始最常见的元素是5,那么我们会添加0,直到最常见的元素发生变化。如果这需要c个零,则a[0..k]必须包含c5以上0个。重复这个论点,我们得到了很多线性方程式,我们可以解决这些方程式来准确地告诉[0,u]中每个元素a[0..k]的确切次数。

这告诉我们,任何执行扫描的数据结构都可以存储所有看到元素的计数(以某种压缩方式)。如果您对数学感兴趣,那么在看到n数字之后存储的数字是log(n+u-1 choose n),这是将n无法区分的项目划分为u的方式数量的日志可区分箱。这超过log(u^n/n!) >= nlogu-nlogn

结论:任何只进行一次数组传递的算法都必须使用尽可能多的内存来存储到目前为止看到的所有计数。如果nu相比较小,则相当于存储n个内存字。

(好吧,我们也可能会覆盖现有的数据而不是额外的内存。)

这里还有很多值得探讨的地方。例如。多次传递如何影响上述参数。但是我认为我应该在这一点上停止:),但是对于我来说,任何线性时间算法(具有较大的u)都可以使O(1)额外的内存消失

答案 2 :(得分:1)

如果你想拥有固定的空间来找到最常见的元素,你需要拥有一个元素的最大位数。如果你没有,那么大输入数组可能有更大的输入数字,这样表示数字的位数大于固定空间来存储结果。

假设k是您支持的最大数字的长度。如果你试图天真地创建一个2^k桶数组来计算每个数字的出现次数(计数器排序),你可以收到一个由相同数字组成的数组,在这种情况下你的算法最终需要log(n)存储金额的空间。[*]

如果我们查看更简单的问题版本 - 确定输入中是否还有1个或0,我认为您需要要执行此操作的堆栈(存储10所引用的数量),因此即使我们将输入长度限制为{{1},也不可能使用常量空间比特大小。

您的问题更为一般(k = 1,但仍然是固定的),并且还需要非恒定的空间,因此问题无法解决。

[*]如果假设计数器具有k > 1空间复杂度,那么你可以采用计数器排序方法,尽管这样做你已经在输入数组的最大大小上设置了上限(可能接受也可能不接受):就O(1)而言,数组输入元素的最大位数以及k计数器中最大位数最多可以有c个元素(其中一个计数器会在下一个元素上溢出)。要解决此问题,您可以添加2^k * 2^c时间步骤来递减计数器,以便在处理每个元素后,如果所有计数器都是非O(1),则最小值始终为0,从而使他们相对而不是绝对。这需要0时间,因为如果所有都非零,则只需要O(1)计数器递减O(2^k) = O(1),如果您在每个元素上执行它。虽然该算法现在可以处理一些任意大的输入,但是任何具有子数组的输入数组,使得两个值1a使得使用计数器策略的b将失败一些投入。实际上,依赖于count(a) - count(b) > 2^c = max(counter)空间复杂度计数器方法的结果是,所有以O(1)个相同元素开头的数组都无法通过此算法处理。

答案 3 :(得分:-1)

这是我阅读数组中最常见元素的脚本

<?php

class TestClass {

    public $keyVal;
    public $keyPlace = 0;

    //put your code here
    public function maxused_num($array) {
        $temp = array();
        $tempval = array();
        $r = 0;
        for ($i = 0; $i <= count($array) - 1; $i++) {
            $r = 0;
            for ($j = 0; $j <= count($array) - 1; $j++) {
                if ($array[$i] == $array[$j]) {
                    $r = $r + 1;
                }
            }
            $tempval[$i] = $r;
            $temp[$i] = $array[$i];
        }
        //fetch max value
        $max = 0;
        for ($i = 0; $i <= count($tempval) - 1; $i++) {
            if ($tempval[$i] > $max) {
                $max = $tempval[$i];
            }
        }
        //get value 
        for ($i = 0; $i <= count($tempval) - 1; $i++) {
            if ($tempval[$i] == $max) {
                $this->keyVal = $tempval[$i];
                $this->keyPlace = $i;
                break;
            }
        }

        // 1.place holder on array $this->keyPlace;
        // 2.number of reapeats $this->keyVal;
        return $array[$this->keyPlace];
    }

}

$catch = new TestClass();
$array = array(1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 1, 2, 3, 1, 1, 2, 5, 7, 1, 9, 0, 11, 22, 1, 1, 22, 22, 35, 66, 1, 1, 1);
echo $catch->maxused_num($array);