这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战
[933] - 最近请求次数
题目:
写一个RecentCounter类来计算特定时间范围内最近的请求。
请你实现 RecentCounter 类:
RecentCounter() 初始化计数器,请求数为 0 。 int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。 保证 每次对 ping 的调用都使用比之前更大的 t 值。
实例:
输入:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]
解释:
RecentCounter recentCounter = new RecentCounter();``
recentCounter.ping(1); // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100); // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001); // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002); // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3
实现方案1: 遍历数组 (暴利破解)
解题思路:
- 将每次请求的数,存放到数组中 (并记录最后一位的下标)
- 每次从数组尾部进行遍历,对大于等于 【t - 3000】 ,进行统计次数,进行累加
class RecentCounter {
List<Integer> list = new ArrayList<Integer>(10000);
public RecentCounter() {
}
public int ping1(int t) {
list.add(t);
int end = list.size() - 1;
int count = 0;
while (list.get(end) >= t - 3000) {
count++;
if (--end < 0) {
break;
}
}
return count;
}
}
实现方案2: 优化 - 遍历数组
优化思路:
- 确认3000 毫秒内 最多请求数 ?——> 用去减少数组的存储空间
- t 请求次 + 前3000次 = 3001次
- 但 数组需要设置成 3002 次 (因为是先写入数据,在删除)
- 统计是是否需要进行遍历所有的节点,来计算出数组的的次数? ——> 减少不必要的循环
- 每次添加时,循环遍历数组,将数组内不符合范围内的数据给删除
- 最后 列表中只剩下符合条件的请求次数,直接获取
size()
即可
class RecentCounter {
List<Integer> list = new ArrayList<Integer>(3002);
public RecentCounter() {
}
public int ping1(int t) {
list.add(t);
while (list.get(0) < t - 3000) {
list.remove(0);
}
return list.size();
}
}
实现方案3: 队列解法
3-1: 自定义队列 (底层使用链表)
解题思路:
- 使用队列的数据结构,先进先出的原则
- 通过链表实现队列
- 定义属性: 对头:
head
、对尾:tail
、长度:size
- 定义方法: 添加节点:
add()
、移除节点:poll()
、 队列长度:size()
- 内部定义:
Node
类 ,封装每次入队的请求,和指向下一个节点的指针
- 每次请求向队列尾部追加节点
- 循环检查列头数据是否合法,不合法则移除节点
- 返回队列长度
class RecentCounter {
Queue<Integer> q = new LinkedList<Integer>();
public RecentCounter() {
}
public int ping(int t) {
q.add(t);
while (q.head.getVal() < t - 3000) {
q.poll();
}
return q.size();
}
class Queue {
Node head;
Node tail;
int size = 0;
Queue() {
}
public void add(int val) {
if (head == null) {
head = new Node(val);
tail = head;
} else {
Node last = new Node(val);
tail.next = last;
tail = last;
}
size++;
}
public int poll() {
int headVal = head.val;
Node next = head.next;
head.next = null;
head = next;
if (next == null) {
tail = null;
}
size--;
return headVal;
}
public int size() {
return this.size;
}
class Node {
int val;
Node next;
Node(int index) {
this.val = index;
}
int getVal() {
return val;
}
}
}
}
3-2: 使用Java提供的队列
解题逻辑与 自定义逻辑一致。 其实Java 原生就提供的 链表 和 队列的实现 没有必要太多的自定义:
class RecentCounter {
Queue<Integer> q = new LinkedList<Integer>();
public RecentCounter() {
}
public int ping(int t) {
q.add(t);
while (q.peek() < t - 3000) {
q.poll();
}
return q.size();
}
}