Design Hit Counter
要点:因为是second granularity,所以可以用以秒为单位的circular buffer方法。这题简单在只需要count过去300秒的,增加难度可以count过去秒,分钟,小时。
- 2个时间点都有可能更新超时的统计:query和hit
- 一种简单方法是分开计timestamp和hit:每次timestamp%300的slot里的ts和当前时间比较:因为可以用记录的ts来区分当前slot是否过期,query可以不更新只统计。同样,hit只要更新当前slot:+1(有效)or 1(过期)
- 如果不记录timestamp,需要记录lastSec(实际就是上次队头):而这次curSec更新为新head
- why [lastSec+1, curSec] is cleaned up? 因为curSec是新的circular buffer的队头,根据题意,队列的head和tail是相连的(or tail是head的下一个)。新的head更新前就把上一次队尾开始的元素清空(也就是超时的部分)
- 扩展:lastMiniute的更新?[lastSec-60+1, curSec-60]:在这两个时间之间要清除:无论curSec和lastSec差>60 or <60:之前lastSec和curSec之间已经清零(注意更新minute要在seconds清零之后),所以实际减去的只是lastSec-60开始到curSec-60没清零的部分,保留的是curSec-60+1之后这部分:注意curSec-60+1是过去1分钟的最后一秒
- 特殊情况:curSec-lastSec>300:不用一个个清零了。curSec==lastSec情况不用特殊考虑,因为是从lastSec+1开始清零
- 本题count更新和扩展中的hour更新一个意思
https://repl.it/C9qu/3
# Design a hit counter which counts the number of hits received in the past 5 minutes.
# Each function accepts a timestamp parameter (in seconds granularity) and you may assume that calls are being made to the system in chronological order (ie, the timestamp is monotonically increasing). You may assume that the earliest timestamp starts at 1.
# It is possible that several hits arrive roughly at the same time.
# Example:
# HitCounter counter = new HitCounter();
# // hit at timestamp 1.
# counter.hit(1);
# // hit at timestamp 2.
# counter.hit(2);
# // hit at timestamp 3.
# counter.hit(3);
# // get hits at timestamp 4, should return 3.
# counter.getHits(4);
# // hit at timestamp 300.
# counter.hit(300);
# // get hits at timestamp 300, should return 4.
# counter.getHits(300);
# // get hits at timestamp 301, should return 3.
# counter.getHits(301);
# Follow up:
# What if the number of hits per second could be very large? Does your design scale?
class HitCounter(object):
def __init__(self):
"""
Initialize your data structure here.
"""
self.lastSec = 0
self.seconds = [0]*300
self.count = 0
def _cleanup(self, timestamp):
if timestamp-self.lastSec>300:
self.seconds = [0]*300
self.count = 0
else:
for i in xrange(self.lastSec+1, timestamp+1):
self.count-=self.seconds[i%300]
self.seconds[i%300]=0
def hit(self, timestamp):
"""
Record a hit.
@param timestamp - The current timestamp (in seconds granularity).
:type timestamp: int
:rtype: void
"""
self._cleanup(timestamp)
self.count+=1
self.seconds[timestamp%300]+=1
self.lastSec = timestamp
def getHits(self, timestamp):
"""
Return the number of hits in the past 5 minutes.
@param timestamp - The current timestamp (in seconds granularity).
:type timestamp: int
:rtype: int
"""
self._cleanup(timestamp)
self.lastSec = timestamp
return self.count
# Your HitCounter object will be instantiated and called as such:
# obj = HitCounter()
# obj.hit(timestamp)
# param_2 = obj.getHits(timestamp)