我遇到了这个问题,仍在尝试解决此问题:
您要记录网站的点击次数。
实现两个功能, log_hit()在记录匹配时被调用,并且 get_hits_in_last_five_minutes()返回命中总数 在最后五分钟内 假定所有时间戳按递增顺序排列。
我的想法(尝试解决问题):
数据结构:数组。
我的逻辑:
调用log_hit()时,我们基本上将时间存储在数组中的ms中(Date()。getTime()以ms为单位),并将命中的特定时间存储在哈希图中。
function Counter() {
this.map = {};
this.store = [];
}
Counter.prototype.log_hit = function() {
const d = new Date().getTime()/1000;
this.store.push(d);
this.map[d] = this.map[d] ? this.map[d]++ : 1;
}
Counter.prototype.get_hits_in_last_five_minutes = function() {
const fiveMin = 60 * 60; //seconds.
const initalPointer = new Date().getTime() / 1000 - fiveMin;
// Somehow retrieve the value and return it back
}
但是,如果我想将解决方案扩展一个小时或其他粒度,我认为这不是解决问题的最佳方法。
我将如何解决此类问题?
答案 0 :(得分:0)
使用队列是解决此问题的正确方法。
var HitCounter = function() {
this.queue = [];
};
/**
* Record a hit.
@param timestamp - The current timestamp (in seconds granularity).
* @param {number} timestamp
* @return {void}
*/
HitCounter.prototype.hit = function(timestamp) {
this.queue.push(timestamp);
};
/**
* Return the number of hits in the past 5 minutes.
@param timestamp - The current timestamp (in seconds granularity).
* @param {number} timestamp
* @return {number}
*/
HitCounter.prototype.getHits = function(timestamp) {
while(this.queue.length && this.queue[0] <= timestamp-300) {
this.queue.shift();
}
return this.queue.length;
};
const counter = new HitCounter();
counter.hit(1);
counter.hit(2);
counter.hit(3);
counter.getHits(4);
counter.hit(300);
counter.getHits(300);
console.log(counter.getHits(301)); // should output 3.
答案 1 :(得分:0)
我们需要利用这一事实-
假设所有时间戳按递增顺序排列。
算法:
t
的时间get_hits_in_last_five_minutes()
的最后一个有效上限索引。 Upper bound
所用的限制是我们可以使用点击数判断否的极限。最近5分钟的通话次数。这是必需的,因为问题说明中会显示 get_hits_in_last_five_minutes(),该函数会返回最近五分钟的总点击数。因此,从技术上讲,这意味着它将用作API
来检查在参数传递时间t
前5分钟之前进行了多少次呼叫。 不能保证,为此方法传递的时间t
始终是计数器中最后插入的时间戳。因此,我们需要搜索数组的上限,即直到存储在数组中的命中值可以算作对我们的答案有效的索引为止。
第二,我们从0
到upper_bound
进行二进制搜索,以获取t
前最后5分钟内的所有有效匹配。
空间复杂度: O(n),其中n
是否。已记录的点击数。
O(log(n))
搜索上限。O(log(n))
获取最近5分钟内记录的实际匹配。O(log(n)) + O(log(n)) = 2 * O(log(n)) = O(log(n))
注意:在存储和搜索时,我将时间t
转换为秒。
代码:
function Counter() {
this.hits = [];
this.hits_size = 0;
}
Counter.prototype.log_hit = function(t) {
this.hits[this.hits_size++] = t * 60;
}
Counter.prototype.get_hits_in_last_five_minutes = function(t) {
if (this.hits_size < 2) return 0;
t *= 60;
var upper_bound = this.getUpperBound(t);
this.last_call_type = 2;
var low = 0,
high = upper_bound;
while (low <= high) {
var mid = low + parseInt((high - low) / 2);
if (this.hits[mid] > t - 300) high = mid - 1;
else if (this.hits[mid] < t - 300) low = mid + 1;
else return upper_bound - mid + 1;
}
return upper_bound - low + 1;
}
Counter.prototype.getUpperBound = function(t) {
var low = 0,
high = t > this.hits[this.hits_size - 1] ? this.hits_size - 1 : this.hits_size - 2;
var ans = 0;
while (low <= high) {
var mid = low + parseInt((high - low) / 2);
if (this.hits[mid] >= t) {
high = mid - 1;
} else {
ans = mid;
low = mid + 1;
}
}
if (high < 0) return -1;
return ans;
}
console.log("*****Counter 1******");
var c1 = new Counter();
c1.log_hit(1);
console.log("Registered, 1 = " + c1.get_hits_in_last_five_minutes(1));
c1.log_hit(2);
console.log("Registered, 2 = " + c1.get_hits_in_last_five_minutes(2));
c1.log_hit(3);
console.log("Registered, 3 = " + c1.get_hits_in_last_five_minutes(3));
c1.log_hit(4);
console.log("Registered, 4 = " + c1.get_hits_in_last_five_minutes(4));
c1.log_hit(5);
console.log("Registered, 5 = " + c1.get_hits_in_last_five_minutes(5));
c1.log_hit(6);
console.log("Registered, 6 = " + c1.get_hits_in_last_five_minutes(6));
c1.log_hit(7);
console.log("Registered, 7 = " + c1.get_hits_in_last_five_minutes(7));
c1.log_hit(8);
console.log("Registered, 8 = " + c1.get_hits_in_last_five_minutes(8));
c1.log_hit(9);
console.log("Registered, 9 = " + c1.get_hits_in_last_five_minutes(9));
c1.log_hit(10);
console.log("Registered, 10 = " + c1.get_hits_in_last_five_minutes(10));
console.log("*****Counter 2******");
var c2 = new Counter();
c2.log_hit(2);
console.log("Registered, 2 = " + c2.get_hits_in_last_five_minutes(2));
c2.log_hit(7);
console.log("Registered, 7 = " + c2.get_hits_in_last_five_minutes(7));
c2.log_hit(8);
console.log("Registered, 8 = " + c2.get_hits_in_last_five_minutes(8));
c2.log_hit(9);
console.log("Registered, 9 = " + c2.get_hits_in_last_five_minutes(9));
c2.log_hit(10);
console.log("Registered, 10 = " + c2.get_hits_in_last_five_minutes(10));
c2.log_hit(11);
console.log("Registered, 11 = " + c2.get_hits_in_last_five_minutes(11));
c2.log_hit(12);
console.log("Registered, 12 = " + c2.get_hits_in_last_five_minutes(12));
c2.log_hit(17);
console.log("Registered, 17 = " + c2.get_hits_in_last_five_minutes(17));
console.log("Unregistered, 18 = " + c2.get_hits_in_last_five_minutes(18));
c2.log_hit(19);
console.log("Registered, 19 = " + c2.get_hits_in_last_five_minutes(19));
console.log("Unregistered, 20 = " + c2.get_hits_in_last_five_minutes(20));
c2.log_hit(21);
console.log("Registered, 21 = " + c2.get_hits_in_last_five_minutes(21));
console.log("Unregistered, 6 = " + c2.get_hits_in_last_five_minutes(6));
console.log("Unregistered, 500 = " + c2.get_hits_in_last_five_minutes(500));
console.log("Unregistered, 15 = " + c2.get_hits_in_last_five_minutes(15));
console.log("Registered, 17 = " + c2.get_hits_in_last_five_minutes(17));
为什么要执行二进制搜索?
t - 300
下的所有这些值的计数。这样,每次通话可能是O(1)
。t - 300
下。如果增加,例如t - 9000
,那么我们的向后迭代也会增加直到9000
,并且如果get_hits_in_last_five_minutes()
的调用次数恰好是10000或更多,则循环的复杂性增加了整体复杂性。因此,可能是10000次致电
get_hits_in_last_five_minutes()
* 9000
如果我们使用上述算法,它将是
10000次调用
get_hits_in_last_five_minutes()
* log(n)
如果通话永无止境(无限)怎么办?
这取决于我们选择使用get_hits_in_last_five_minutes()
方法的方式。
如果传递给t
的呼叫所花费的时间get_hits_in_last_five_minutes()
始终处于非递减状态,那么我们可以从存储中截断/删除匹配。
t - 300
的存储中获取最大值的索引。之后,我们只需执行array slice并将this.hits_size
重置为新长度即可。
log_hit()
方法执行此操作,而不是get_hits_in_last_five_minutes()
,因为时间t
传递给它不一定是我们注册匹配的一部分。 array slice
可能会增加一点复杂度,因为它返回原始数组的浅表副本,并且复杂度为O(N)
,其中N
是end - start
。参见array slice complexity。为了避免这种情况,我们可以创建一个链表,并在其中存储数据,并使用地图存储节点。这样,我们可以重置列表的头部并进行修剪/截断O(1)
。我们也需要维护一个被截断的节点数,因为我们无法使用新值重置地图。 如果传递给您网站t
的呼叫的时间get_hits_in_last_five_minutes()
是任意随机的,那么我们将无法截断任何内容,因为我们需要所有数据来给出答案。在这种情况下,可能会将数据存储在数据库中。好的部分是,您可以从数据库查询您的答案,而无需在javascript中进行计算。