【算法学习】剑指 Offer II 042. 最近请求次数(java / c / c++ / python / go / rust)

简介: 写一个 RecentCounter 类来计算特定时间范围内最近的请求。请实现 RecentCounter 类: RecentCounter() 初始化计数器,请求数为 0 。 int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。保证 每次对 ping 的调用都使用比之前更大的 t 值。

剑指 Offer II 042. 最近请求次数:

写一个 RecentCounter 类来计算特定时间范围内最近的请求。

请实现 RecentCounter 类:

  • RecentCounter() 初始化计数器,请求数为 0 。
  • int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。

保证 每次对 ping 的调用都使用比之前更大的 t 值。

样例 1

输入:
    inputs = ["RecentCounter", "ping", "ping", "ping", "ping"]
    inputs = [[], [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 <= 109
  • 保证每次对 ping 调用所使用的 t 值都 严格递增
  • 至多调用 ping 方法 104

分析

  • 二当家的觉得这道算法题相对来说还是自由度比较高的。
  • 由于有提示中第二条的规定,每次 ping 调用所使用的t值都严格递增,所以可以使用队列,如果已经和最新的t相差超过3000,就可以清除了,按顺序出队列即可,队列中元素最多也只有3000个,也就是每次 ping 最差就是不超过3000次出队列,而队列留下的值恰好就是 ping 方法返回值。
  • 由于有提示中第三条的规定,ping 的最高调用次数已经知道,如果使用可随机访问的数据结构,还可以使用二分查找,时间复杂度上会比按顺序出队列好一些,但是如果 ping 的次数是不一定的,还是用队列好。

题解

java

没有使用自带的二分查找,因为查不到是负数,不是我们想要的值。

class RecentCounter {
    private int[] q;
    private int head;
    private int tail;

    public RecentCounter() {
        q = new int[10000];
    }

    public int ping(int t) {
        head = binarySearch(q, head, tail, t - 3000);
        q[tail++] = t;
        return tail - head;
    }

    private int binarySearch(int[] a, int fromIndex, int toIndex, int key) {
        int low  = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid    = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return low;  // key not found.
    }
}

c

typedef struct {
    int *q;
    int head;
    int tail;
} RecentCounter;


RecentCounter *recentCounterCreate() {
    RecentCounter *recentCounter = (RecentCounter *) malloc(sizeof(RecentCounter));
    recentCounter->q = (int *) malloc(sizeof(int) * 10000);
    recentCounter->head = 0;
    recentCounter->tail = 0;
    return recentCounter;
}

int binarySearch(int *a, int fromIndex, int toIndex, int key) {
    int low = fromIndex;
    int high = toIndex - 1;

    while (low <= high) {
        int mid = (low + high) >> 1;
        int midVal = a[mid];

        if (midVal < key)
            low = mid + 1;
        else if (midVal > key)
            high = mid - 1;
        else
            return mid; // key found
    }
    return low;  // key not found.
}

int recentCounterPing(RecentCounter *obj, int t) {
    obj->head = binarySearch(obj->q, obj->head, obj->tail, t - 3000);
    obj->q[obj->tail++] = t;
    return obj->tail - obj->head;
}

void recentCounterFree(RecentCounter *obj) {
    free(obj->q);
    free(obj);
}

c++

class RecentCounter {
private:
    vector<int> q;
    int head;
public:
    RecentCounter() {
        head = 0;
    }

    int binarySearch(vector<int>& a, int fromIndex, int toIndex, int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }
        return low;  // key not found.
    }

    int ping(int t) {
        head = binarySearch(q, head, q.size(), t - 3000);
        q.push_back(t);
        return q.size() - head;
    }
};

python

python这次是最简洁的了,主要就是有可以直接用的二分查找。

class RecentCounter:

    def __init__(self):
        self.q = []
        self.head = 0

    def ping(self, t: int) -> int:
        self.head = bisect.bisect_left(self.q, t - 3000, self.head)
        self.q.append(t)
        return len(self.q) - self.head

go

type RecentCounter struct {
    q    []int
    head int
}

func Constructor() RecentCounter {
    return RecentCounter{[]int{}, 0}
}

func (this *RecentCounter) Ping(t int) int {
    this.head = binarySearch(this.q, this.head, len(this.q), t - 3000)
    this.q = append(this.q, t)
    return len(this.q) - this.head
}

func binarySearch(a []int, fromIndex int, toIndex int, key int) int {
    low := fromIndex
    high := toIndex - 1

    for low <= high {
        mid := (low + high) >> 1
        midVal := a[mid]

        if midVal < key {
            low = mid + 1
        } else if midVal > key {
            high = mid - 1
        } else {
            return mid // key found
        }
    }
    return low // key not found.
}

rust

struct RecentCounter {
  q: Vec<i32>,
  head: i32,
}


/**
 * `&self` means the method takes an immutable reference.
 * If you need a mutable reference, change it to `&mut self` instead.
 */
impl RecentCounter {
  fn new() -> Self {
    RecentCounter { q: Vec::new(), head: 0 }
  }

  fn ping(&mut self, t: i32) -> i32 {
    self.head = RecentCounter::binarySearch(&self.q, self.head, self.q.len() as i32, t - 3000);
    self.q.push(t);
    self.q.len() as i32 - self.head
  }

  fn binarySearch(a: &Vec<i32>, fromIndex: i32, toIndex: i32, key: i32) -> i32 {
    let mut low = fromIndex;
    let mut high = toIndex - 1;

    while low <= high {
      let mid = (low + high) >> 1;
      let midVal = a[mid as usize];

      if midVal < key {
        low = mid + 1;
      } else if midVal > key {
        high = mid - 1;
      } else {
        return mid; // key found
      }
    }
    return low;  // key not found.
  }
}

在这里插入图片描述


原题传送门:https://leetcode-cn.com/problems/H8086Q/


非常感谢你阅读本文~
放弃不难,但坚持一定很酷~
希望我们大家都能每天进步一点点~
本文由 二当家的白帽子:https://developer.aliyun.com/profile/sqd6avc7qgj7y 博客原创~

相关文章
|
8月前
|
算法 搜索推荐 JavaScript
基于python智能推荐算法的全屋定制系统
本研究聚焦基于智能推荐算法的全屋定制平台网站设计,旨在解决消费者在个性化定制中面临的选择难题。通过整合Django、Vue、Python与MySQL等技术,构建集家装设计、材料推荐、家具搭配于一体的一站式智能服务平台,提升用户体验与行业数字化水平。
|
8月前
|
存储 Oracle Java
java零基础学习者入门课程
本课程为Java零基础入门教程,涵盖环境搭建、变量、运算符、条件循环、数组及面向对象基础,每讲配示例代码与实践建议,助你循序渐进掌握核心知识,轻松迈入Java编程世界。
697 0
|
8月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
8月前
|
负载均衡 Java API
grpc-java 架构学习指南
本指南系统解析 grpc-java 架构,涵盖分层设计、核心流程与源码结构,结合实战路径与调试技巧,助你从入门到精通,掌握高性能 RPC 开发精髓。
837 8
|
8月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
435 1
|
8月前
|
存储 算法 编译器
算法入门:剑指offer改编题目:查找总价格为目标值的两个商品
给定递增数组和目标值target,找出两数之和等于target的两个数字。利用双指针法,left从头、right从尾向中间逼近,根据和与target的大小关系调整指针,时间复杂度O(n),空间复杂度O(1)。找不到时返回{-1,-1}。
|
9月前
|
Java
Java基础学习day08-作业
本作业涵盖Java中Lambda表达式的应用,包括Runnable与Comparator接口的简化实现、自定义函数式接口NumberProcessor进行加减乘及最大值操作,以及通过IntProcessor处理整数数组,实现遍历、平方和奇偶判断等功能,强化函数式编程实践。
145 5
|
9月前
|
Java API 容器
Java基础学习day08-2
本节讲解Java方法引用与常用API,包括静态、实例、特定类型方法及构造器引用的格式与使用场景,并结合代码示例深入解析。同时介绍String和ArrayList的核心方法及其实际应用。
244 1
|
9月前
|
Java 程序员
Java基础学习day08
本节讲解Java中的代码块(静态与实例)及其作用,深入介绍内部类(成员、静态、局部及匿名)的定义与使用,并引入函数式编程思想,重点阐述Lambda表达式及其在简化匿名内部类中的应用。
222 5
|
9月前
|
存储 算法 调度
【复现】【遗传算法】考虑储能和可再生能源消纳责任制的售电公司购售电策略(Python代码实现)
【复现】【遗传算法】考虑储能和可再生能源消纳责任制的售电公司购售电策略(Python代码实现)
442 26