移动最大变体

时间:2016-12-31 14:03:05

标签: algorithm max moving-average

昨天,在技术面试中我被问到以下问题。

想象一下,你在为新闻机构工作。在每个离散的时间点t,故事都会破裂。有些故事比其他故事更有趣。这种“热度”表示为自然数h,更多数字代表更热门的新闻报道。

鉴于Sn个故事的流k,您的工作就是找到每个t >= k最新a个故事中最热门的故事。

到目前为止,非常好:这是移动的最大问题(也称为滑动窗口最大问题),并且有一个linear-time algorithm可以解决它。

现在问题越来越难了。当然,与较新的故事相比,较旧的故事通常不那么热门。让最新故事的年龄max(0, min(h, k - a))为零,让任何其他故事的年龄大于其后续故事的年龄。然后将故事的“改善的热度”定义为n = 13, k = 4 S indices: 0 1 2 3 4 5 6 7 8 9 10 S values: 1 3 1 7 1 3 9 3 1 3 1 mov max hot indices: 3 3 3 6 6 6 6 9 mov max hot values: 7 7 7 9 9 9 9 3 mov max imp-hot indices: 3 3 5 6 7 7 9 9 mov max imp-hot values: 4 3 3 4 3 3 3 3

以下是一个例子:

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");

我完全失去了这个问题。我想在计算最大值之前为每个元素添加索引,但这可以让你得到每个步骤时故事的热度减少1的答案,无论它是否达到了热度限制。

你能找到一个针对这个问题的算法,其子二次(理想情况是:线性)运行时间?

2 个答案:

答案 0 :(得分:1)

我将描述一个线性时间解决方案,解决涉及双端队列(deque)的原始问题,然后将其扩展到改进的热度而不会失去渐近效率。

原始问题:在窗口中保留包含(1)比其他所有故事更新或更热的故事的双端队列(2)。在任何时候,队列中最热门的故事都在前面。在从后面弹出每个故事直到找到一个更热门的故事之后,新故事被推到了双端队列的背面。随着时间的推移,故事会从前面弹出。

例如:

S indices:   0   1   2   3   4   5   6   7   8   9  10
S values:    1   3   1   7   1   3   9   3   1   3   1

deque: (front) [] (back)
push (0, 1)
deque: [(0, 1)]
pop (0, 1) because it's not hotter than (1, 3)
push (1, 3)
deque: [(1, 3)]
push (2, 1)
deque: [(1, 3), (2, 1)]
pop (2, 1) and then (1, 3) because they're not hotter than (3, 7)
push (3, 7)
deque: [(3, 7)]
push (4, 1)
deque: [(3, 7), (4, 1)]
pop (4, 1) because it's not hotter than (5, 3)
push (5, 3)
deque: [(3, 7), (5, 3)]
pop (5, 3) and then (3, 7) because they're not hotter than (6, 9)
push (6, 9)
deque: [(6, 9)]
push (7, 3)
deque: [(6, 9), (7, 3)]
push (8, 1)
deque: [(6, 9), (7, 3), (8, 1)]
pop (8, 1) and (7, 3) because they're not hotter than (9, 3)
push (9, 3)
deque: [(6, 9), (9, 3)]
push (10, 1)
pop (6, 9) because it exited the window
deque: [(9, 3), (10, 1)]

为了解决新问题,我们修改了处理老化故事的方法。当它们从窗户滑出时,我们不会弹出故事,只要它的改善的热度变得小于或等于它的热度,我们就会弹出前面的故事。在确定头条新闻时,只需要考虑最近出现的故事。

在Python中:

import collections

Elem = collections.namedtuple('Elem', ('hot', 't'))


def winmaximphot(hots, k):
    q = collections.deque()
    oldtop = 0
    for t, hot in enumerate(hots):
        while q and q[-1].hot <= hot:
            del q[-1]
        q.append(Elem(hot, t))
        while q and q[0].hot >= k - (t - q[0].t) > 0:
            oldtop = k - (t - q[0].t)
            del q[0]
        if t + 1 >= k:
            yield max(oldtop, q[0].hot) if q else oldtop
        oldtop = max(0, oldtop - 1)


print(list(winmaximphot([1, 3, 1, 7, 1, 3, 9, 3, 1, 3, 1], 4)))

答案 1 :(得分:0)

理念如下:对于每个突发新闻,它将在k-h步之后击败所有之前的新闻。这意味着对于k==30和新闻热度h==28,此新闻将比之前的所有新闻更热,经过两个步骤。 让下一个新闻成为最热门的所有时刻。在步骤i,我们获得当前新闻将超过所有先前等于i+k-h的新闻时刻。 所以我们将有{news_date | news_beats_all_previous_ones_date}这样的对象序列,它按news_beats_all_previous_ones_date的顺序递增:

{i1 | i1+k-h} {i3 | i3+k-h} {i4 | i4+k-h} {i7 | i7+k-h} {i8 | i8+k-h}

在当前步骤,我们得到i9+k-h,我们将它添加到此列表的末尾,删除所有更大的值(因为序列增加这很容易)。 一旦第一个元素news_beats_all_previous_ones_date变为相等的当前日期(i),此新闻将成为滑动窗口查询的答案,我们会从序列中删除此项目。 因此,您需要一种能够添加到最后的数据结构,并从开始和结束时删除。这是Deque。解决方案的时间复杂度为O(n)