Leetcode刷题系列:最近请求次数

507 阅读2分钟

这是我参与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

933-提示.jpg

实现方案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();
    }
}


gitlab 地址

案例地址