如Linear time majority algorithm?的答案所示,可以在线性时间和log(n)
空间中计算大部分元素。
有人表示,看到这种算法的每个人都认为这是一种很酷的技术。但这个想法是否会推广到新算法?
这个算法的隐藏力似乎在于保持一个扮演复杂角色的计数器 - 例如“(到目前为止多数元素的数量) - (到目前为止第二多数的计数)”。是否存在基于相同想法的其他算法?
答案 0 :(得分:6)
算法的要点是,如果你有一个多数元素,那么你可以将它的每一个匹配与一个“另一个”元素匹配,然后你有一些“备用”。
所以,我们只有一个计数器来计算我们的客座答案的“备用”次数。 如果它达到0,那么从我们“选择”“当前”元素作为客户主要元素到“当前”位置时,它不是子序列的多数元素。 此外,由于我们的“guest”元素与所考虑的子序列中的所有其他元素匹配,因此在所考虑的子序列中没有主要元素。
现在,因为:
通过矛盾可以看出,如果存在一个主要元素,那么当计数器永远不会为零时,我们就会得到整个序列的后缀。
现在:在新的O(1)大小O(n)时间算法中可以利用的想法是什么?
对我来说,只要你需要在一系列元素上计算属性P
,就可以应用这种技术:
seq[n, m]
不成立,seq[n, m+1]
展开到Q(seq[n, m+1])
P(seq[n, m])
成立,P(seq[n, j])
可以在P(seq[j, m])
和Q(seq[n, j])
的O(1)时间和空间内计算在我们的案例中,P
是我们“当选”主要元素的“备用”事件,Q
是“P
为零”。
如果你以这种方式看待事物,longest common subsequence会利用同样的想法(不知道它的“冷静因素”;))
答案 1 :(得分:4)
当然,这可能与原始问题非常相似,您可能正在寻找“不同”的算法。
这是一个可能不同的例子。
给出一个算法,该算法将检测是否形成了一串圆括号('('和')')。
我认为标准解决方案是维持一个柜台。
旁注:
对于声称不能是恒定空间等的答案,请询问他们的计算模型。例如,在WORD RAM模型中,假设整数/数组索引等为O(1)。
许多人错误地混合和匹配模型。例如,他们很乐意将n个整数的输入数组设为O(n),将数组索引设为O(1)空间,而将计数器视为Omega(log n)等,这是无意义的。如果他们想要考虑位的大小,那么输入本身就是Omega(n log n)等。
答案 2 :(得分:2)
对于想要了解此算法的功能及其原理的人:look at my detailed answer。
这里我将描述该算法的自然扩展(或概括)。因此,在标准多数投票算法中,您必须找到在流中至少n/2
次出现的元素,其中n
是流的大小。您可以执行此操作这在O(n)
时间内(在O(log(n))
空间中有一个很小的常数,更糟糕的情况,并且极不可能。
广义算法允许您查找k
最常见的项目,其中每次在原始流中出现的时间至少为n/(k+1)
次。请注意,如果k = 1,你最终得到了原来的问题。
此问题的解决方案与原始问题非常类似,除了代替一个计数器和一个可能的元素,您需要维护k个计数器和k个可能的元素。现在逻辑以类似的方式进行。迭代遍历数组,如果元素在可能的元素中,则增加它的计数器,如果其中一个计数器为零 - 用新元素替换此计数器的元素。否则只需减小值。
与原始多数投票算法一样,您需要保证拥有这些k多数元素,否则您必须对数组进行另一次传递以验证您之前找到的可能元素是否正确。这是我的python尝试(没有进行彻底的测试)。
from collections import defaultdict
def majority_element_general(arr, k=1):
counter, i = defaultdict(int), 0
while len(counter) < k and i < len(arr):
counter[arr[i]] += 1
i += 1
for i in arr[i:]:
if i in counter:
counter[i] += 1
elif len(counter) < k:
counter[i] = 1
else:
fields_to_remove = []
for el in counter:
if counter[el] > 1:
counter[el] -= 1
else:
fields_to_remove.append(el)
for el in fields_to_remove:
del counter[el]
potential_elements = counter.keys()
# might want to check that they are really frequent.
return potential_elements