「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
题目
最近的请求次数
写一个 RecentCounter 类来计算特定时间范围内最近的请求。
请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。 int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。 保证 每次对 ping 的调用都使用比之前更大的 t 值。
思路:
头脑风暴的第一反应,是申明一个数组用来存储所有请求,然后每次ping的时候,往数组里push请求的时间t,并且遍历整个数组,看大于t的请求有多少个。虽然这个解法不怎么优雅,但也算能解决问题。
代码如下:
var RecentCounter = function() {
this.items = [];
};
/**
* @param {number} t
* @return {number}
*/
RecentCounter.prototype.ping = function(t) {
this.items.push(t);
let time = t-3000;
let count = 0;
for(let i=0;i<this.items.length;i++){
if(this.items[i]>=time){
count++;
}
}
return count;
};
队列解法
有了暴力解法的基础,我们其实可以转换一下思想,首先我们其实没有必要维护所有的请求,因为我们需要的只是[t-3000,t]范围内的请求,然后我们遍历的判断可以由大于time则计数变为小于time则去除,这样剩下来的就都是满足[t-3000,t]这个条件的。
其实这就是一个队列的思想,我们维护一个队列,这个队列里存储着[t-3000,t]的请求,每推进来一个请求数据,我们就去判断队列的头元素是否在队列条件内,不在就踢出队列,那么我们最终返回队列的长度即是所有在[t-3000,t]内的元素。
代码如下:
var RecentCounter = function() {
this.items = []
};
/**
* @param {number} t
* @return {number}
*/
RecentCounter.prototype.ping = function(t) {
this.items.push(t);
let time = t-3000;
while(this.items[0] < time){
this.items.shift();
}
return this.items.length;
};
但这个写法其实有点问题,就是每次shift数组的时候,后面的元素都会向前移动,多次执行shift操作,则会移动多次。我们其实可以优化一下,引入headIndex来标记队列头元素,每次判断headIndex是否满足[i-3000,i],不满足则+1,最后返回数组length-headindex的值即为队列长度。
代码如下:
var RecentCounter = function() {
this.items = [];
this.headIndex = 0;
};
/**
* @param {number} t
* @return {number}
*/
RecentCounter.prototype.ping = function(t) {
this.items.push(t);
let time = t-3000;
while(this.items[this.headIndex] < time){
this.headIndex ++;
}
return this.items.length - this.headIndex;
};