有一个数组(大小为N),其元素重复次数超过N / 2次,并且数组中元素的其余部分也可以重复,但只重复一个元素超过N / 2次。找到号码。
我可以想到几种方法:
无法想到更好的解决方案,必须有。
答案 0 :(得分:60)
有一个很好的算法可以解决这个问题,它只使用恒定的外部空间(O(1))两次通过(总时间O(N))。我有这个算法的实现,以及包括正确性证明的评论, available here
算法背后的直觉实际上非常漂亮。假设你有一个满屋子的人,每个人都拿着一个阵列元素。每当两个人发现彼此都没有拿着与另一个相同的数组元素时,他们两个坐下。最终,在最后,如果有人站着,他们有可能占多数,你可以检查那个元素。只要一个元素出现频率至少为N / 2,就可以保证这种方法总能找到多数元素。
要实际实现该算法,您需要对数组进行线性扫描,并跟踪当前关于多数元素的猜测,以及到目前为止您看到它的次数。最初,此猜测未定义且重复次数为零。当您走过数组时,如果当前元素与您的猜测相符,则递增计数器。如果当前元素与您的猜测不匹配,则递减计数器。如果计数器达到零,则将其重置为您遇到的下一个元素。您可以将此实现视为上述“站在房间里”算法的具体实现。每当两个人遇到不同的元素时,他们就会取消(丢掉柜台)。每当两个人拥有相同的元素时,他们就不会互相交流。
有关完整的正确性证明,引用原始论文(Boyer和Moore的更着名的Boyer-Moore字符串匹配算法)以及C ++中的实现,请查看上面的链接。
答案 1 :(得分:13)
这是多数元素问题。 对于这个问题,存在单程,恒定空间算法。 这是一个用python编写的简短算法:
import random
items = [1, 2, 3, 4, 5, 5, 5, 5, 5 ]
# shuffle the items
random.shuffle(items)
print("shuffled items: ", items)
majority_elem = items[0]
count = 1
for i in range(1,len(items)):
if items[i] == majority_elem:
count += 1
else:
count -= 1
if count == 0:
majority_elem = items[i]
count = 1
print("majority element : %d" % majority_elem )
我们使用变量majority_elem来跟踪多数元素和计数器(计数)
最初我们将数组的第一个元素设置为多数元素。
我们浏览数组
如果当前元素==多数元素:增量计数
否则:{减量计数。如果count变为零,则设置count = 1并设置majority_element = current元素。 }
这个问题有一个变化,而不是数组,可能会有一个非常大的序列,我们不知道手头的长度。如果是这种情况,排序或分区是没有用的。
参考文献:
答案 2 :(得分:5)
如果一个元素重复超过N / 2次,那么它必须是中位数。 many algorithms允许您有效地找到它。
答案 3 :(得分:5)
你熟悉quicksort吗?它有一个名为'partition'的函数,给定一个值,将数组划分为一个区域,其中所有值都大于值(pivot)在一侧,而小于该值的所有值都在另一侧。请注意,这不是一种排序,只是一种分离。 N / 2计数项目将在两个部分中较大的一部分。您可以递归地应用此技术在O(n)时间内查找元素。
维基百科:快速排序,或基于分区的一般选择算法
答案 4 :(得分:5)
在你的第二种方法中,你基本上是选择中值元素。查看用于查找数字列表中位数的算法。特别是,selection algorithm可以正常工作并在O(n)中计算。
Hoare的选择算法与快速排序非常相似,只是它不是递归两个分区,而只是递归一个分区(包含第k个元素的分区)。
在C ++中,标准库以std::nth_element
的形式提供选择算法,保证O(n)平均复杂度。您可以使用此查找中位数。
int a[8] = {5, 1, 1, 1, 2, 1, 3, 1};
int median = *std::nth_element(a, a + 4, a + 8);
请注意,std::nth_element
也会对数组进行部分排序。
答案 5 :(得分:4)
无需排序。您可以简单地使用中值选择算法来确定第n / 2个元素。 Quickselect在O(n)预期时间内运行。中位数的中位数在O(n)中运行。
答案 6 :(得分:3)
使用任何排序算法对数组进行排序。重复超过一半时间的元素将始终是中间元素。复杂性将是nlog(n)。