Algorithm 从不断更新的列表中获取最大数量

Algorithm 从不断更新的列表中获取最大数量,algorithm,heap,Algorithm,Heap,几天前,我在一次系统设计面试中遇到了这个问题。我省略了一些无关的部分来关注问题的核心部分。事情是这样的 假设我们有一组k,v对,键是字符串,值是整数。我们可以假设有一组固定的键(例如k1、k2、…、kn)。有一些代理将这些k,v对连续地推入系统,就像一个流。我们需要做的就是将当前值添加到所有传入对的旧值中 让我们举个例子。在时间t0,假设我们有以下k-v对 k1: 100 k3: 200 在时间t1,有两个传入对k2:50,k3:150。因此,在t1,系统的状态为: k1: 100 k2: 5

几天前,我在一次系统设计面试中遇到了这个问题。我省略了一些无关的部分来关注问题的核心部分。事情是这样的

假设我们有一组k,v对,键是字符串,值是整数。我们可以假设有一组固定的键(例如k1、k2、…、kn)。有一些代理将这些k,v对连续地推入系统,就像一个流。我们需要做的就是将当前值添加到所有传入对的旧值中

让我们举个例子。在时间
t0
,假设我们有以下k-v对

k1: 100
k3: 200
在时间
t1
,有两个传入对<代码>k2:50,
k3:150
。因此,在
t1
,系统的状态为:

k1: 100
k2: 50
k3: 350
目标是在周期间隔内给出具有最大值的键。我想不出有哪种算法能比max heapify提供更好的运行时间。我想构建一个最大堆,然后在每次新数据出现时更新它。对于每次更新,
heapify()
将花费最多
log(n)
时间。在每次调用时,我们都可以返回堆的根。但是还有比这个更好的解决方案吗?

这取决于(1)是否所有的更新都是单调的(2)取决于您的计算模型

如果值只会增加(单调更新),那么显然,您可以在恒定时间内保持内存中迄今为止存在的所有值的最大值

否则,如果值是小整数,则可以使用a将运行时间提高到
O(log log M)
,其中
M
是最大值


如果只允许比较,那么
Theta(logn)
是最好的选择,因为这种结构可以自适应地用于排序,而排序
n
元素需要
O(nlogn)
比较。给定一个未排序的数组,将每个元素插入不同的键下。查询最大值,将其键设置为负无穷大(或小于最小元素的某个值),然后重复此操作以按降序顺序读取元素。

将最大值和相关键保留在内存中。每次处理传入的键值对时,将已处理键值的新值与最大值进行比较,如果有新的最大值,则进行更新。

概念验证Perl实现。显然,调试语句不应该计入时间

#!/usr/bin/perl -T

$maxv = undef;
%maxk = ();
%pairs = ();

sub updatekeys {
    my %newpairs = @_;
    warn "updating\n";
    while ( my ($k,$v) = each %newpairs ) {
        warn "testing $k:$v\n";
        my $newmax = $pairs{$k} += $v;
        if ( $newmax == $maxv ) {
            warn "appending $k\n";
            $maxk{$k}++;
        }
        elsif ( $newmax > $maxv ) {
            warn "new max ($newmax); overwriting $k\n";
            $maxv = $newmax;
            %maxk = ( $k=>1 );
        }
    }
    warn sprintf "max=$maxv; k=( %s ); pairs=( %s )\n",
        ( join ',', sort keys %maxk ),
        ( join " ", map {"${_}:$pairs{$_}"} sort keys %pairs );

}

updatekeys ( k1=>100, k2=>200 );
updatekeys ( k2=>50, k3=>150 );

如果v可以为负值,这将不起作用。

如果有多个v是最大的,你想要什么答案?我认为在这种情况下,我们应该返回所有具有最大值的元素。在我看来,这可以在与变化次数成线性关系的时间内解决。v总是非负值吗?是的。它们总是>=0谢谢。这些小整数的合理上限是多少?@Bitswazsky理论上,不管你想要什么——这就是变量
M
的作用。实际上,
2^2^x
x
中是一个超快速增长的函数,因此,无论您处理的是何种缓存层次结构,其实际情况都比理论运行时间重要得多。为什么不在内存中保留最大值和相关键,并在处理新的键值对时对其进行比较和更新?@Dave如果当前最大值被较小的值覆盖怎么办?@davidesenstat这不只是加法吗?覆盖发生在哪里?在看到您的答案后,我正在经历一场生存危机:'(。在实际问题中,有许多并发调用此
populateMap()
+
updateMax()
调用。但我想,如果进行适当的同步,它会工作得很好。