找到数组中最常见的条目

时间:2008-11-10 17:02:43

标签: algorithm language-agnostic time-complexity

您将获得一个32位无符号整数数组,其长度最多为2 32 ,其中包含数组中一半以上条目的属性等于N,对于某些32位无符号整数N.找到N只查看数组中的每个数字一次并使用最多2 kB的内存。

您的解决方案必须具有确定性,并保证找到N.

8 个答案:

答案 0 :(得分:58)

为每个位保留一个整数,并为数组中的每个整数适当增加此集合。

最后,一些位的计数将高于数组长度的一半 - 这些位确定为N.当然,计数将高于N出现的次数,但这不是物。重要的是,任何不属于N 的位都不能出现超过一半的次数(因为N有超过一半的条目),并且任何属于N 的位必须< / em>发生的次数超过一半(因为每次发生N时都会发生,以及任何额外的事件)。

(目前没有代码 - 即将失去网络访问权限。希望上述内容已经足够清楚了。)

答案 1 :(得分:41)

Boyer and Moore's "Linear Time Majority Vote Algorithm" - 沿阵列向下,保持你当前的答案。

答案 2 :(得分:7)

只需两个变量就可以做到这一点。

public uint MostCommon(UInt32[] numberList)
{
    uint suspect = 0;
    int suspicionStrength = -1; 
    foreach (uint number in numberList)
    {
        if (number==suspect)
        {
            suspicionStrength++;
        }
        else
        {
            suspicionStrength--;
        }

        if (suspicionStrength<=0)
        {
            suspect = number;
        }
    }
    return suspect;
}

将第一个数字设为可疑号码,然后继续循环浏览列表。如果数字匹配,将怀疑强度提高一个;如果不匹配,请将怀疑强度降低一个。如果怀疑强度达到0,则当前数字成为可疑数字。这将找到最常见的数字,只有一个超过该组50%的数字。如果suspicionStrength大于列表长度的一半,则拒绝添加检查的冲动 - 它总是会导致更多的总比较。

P.S。我没有测试过这段代码 - 自己冒险使用它。

答案 3 :(得分:4)

Jon的算法的伪代码(记事本C ++ :-)):

int lNumbers = (size_of(arrNumbers)/size_of(arrNumbers[0]);

for (int i = 0; i < lNumbers; i++)
  for (int bi = 0; bi < 32; bi++)
    arrBits[i] = arrBits[i] + (arrNumbers[i] & (1 << bi)) == (1 << bi) ? 1 : 0;

int N = 0;

for (int bc = 0; bc < 32; bc++)
  if (arrBits[bc] > lNumbers/2)
    N = N | (1 << bc);

答案 4 :(得分:2)

请注意,如果序列a0, a1, . . . , an−1包含一个领导者,那么在删除一对之后 不同值的元素,剩下的序列仍然具有相同的领导者。的确,如果我们 删除两个不同的元素,然后只有其中一个可以成为领导者。领导者 新序列超过n/2 − 1 = (n−2)/2 倍。因此,它仍然是领导者 新的n − 2元素序列。

这是一个Python实现,具有O(n)时间复杂度:

def goldenLeader(A):
    n = len(A)
    size = 0
    for k in xrange(n):
        if (size == 0):
            size += 1
            value = A[k]
        else:
            if (value != A[k]):
                size -= 1
            else:
                size += 1
    candidate = -1
    if (size > 0):
        candidate = value
    leader = -1
    count = 0
    for k in xrange(n):
        if (A[k] == candidate):
            count += 1
    if (count > n // 2):
        leader = candidate
    return leader

答案 5 :(得分:2)

这是流式算法中的一个标准问题(你有一个巨大的(可能是无限的)数据流),你必须从这个流计算一些统计数据,一次通过这个流。

显然,您可以使用散列或排序来处理它,但是如果有可能无限的流,您显然会耗尽内存。所以你必须在这里做一些聪明的事。

多数元素是发生超过数组大小一半的元素。这意味着多数元素的出现次数超过了所有其他元素的组合,或者如果你计算多数元素的次数,并且减去所有其他元素的数量,你将得到一个正数。

因此,如果你计算一些元素的数量,并减去所有其他元素的数量并得到数字0 - 那么你的原始元素就不能成为多数元素。这个如果是正确算法的基础:

有两个变量,计数器和可能的元素。迭代流,如果计数器为0 - 你覆盖可能的元素并初始化计数器,如果数字与可能的元素相同 - 增加计数器,否则减少它。 Python代码:

def majority_element(arr):
    counter, possible_element = 0, None
    for i in arr:
        if counter == 0:
            possible_element, counter = i, 1
        elif i == possible_element:
            counter += 1
        else:
            counter -= 1

    return possible_element

很明显,算法O(n)O(n)之前具有非常小的常数(如3)。此外,空间复杂度似乎为O(1),因为我们只初始化了三个变量。问题是这些变量之一是一个可能长到n的计数器(当数组由相同的数字组成时)。要存储您需要n空格的O(log (n))号码。从理论的角度来看 它是O(n)时间和O(log(n))空间。 从实际开始,你可以在longint中输入2 ^ 128个数字,并且数组中的元素数量难以想象。

另请注意,只有存在多数元素时,算法才有效。如果这样的元素不存在,它仍然会返回一些数字,这肯定是错误的。 (很容易修改算法来判断多数元素是否存在)

历史频道:这个算法是1982年由Boore,Moore在某处发明的,并称为Boyer–Moore majority vote algorithm

答案 6 :(得分:0)

我对此算法有一些回忆,它可能会也可能不会遵循2K规则。它可能需要用堆栈等重写,以避免因函数调用而破坏内存限制,但这可能是不必要的,因为它只有这种调用的对数。无论如何,我对大学的模糊回忆或对此的递归解决方案涉及分而治之,秘诀是当你将组分成两半时,至少有一半的一半仍然有一半以上的值等于最大值。除法时的基本规则是返回两个候选顶值,其中一个是最高值,另一个是其他值(可能是也可能不是第二位)。我忘记了算法本身。

答案 7 :(得分:0)

buti-oxa / Jason Hernandez答案的正确性证明,假设Jason的答案与buti-oxa的答案相同,并且两者都按照所描述的算法的方式工作:

如果选择最高值,我们将调整后的怀疑强度定义为等于怀疑强度,或者如果未选择最高值,则将调整后的怀疑强度定义为-suspicion strength。每次选择正确的数字时,当前调整的怀疑强度增加1.每次选错号码时,它会下降1或增加1,具体取决于当前是否选择了错误的数字。因此,最小可能的结束调整后的怀疑强度等于[最高值]的数量 - [其他值]的数量