Java&C++题解与拓展——leetcode1606.找到处理最多请求的服务器【TreeSet、PriorityQueue、set、priority_queue学习与使用】

每日一题做题记录,参考官方和三叶的题解

题目要求

在这里插入图片描述

思路一:有序集合+优先队列

模拟就完了,用哈希表统计每个server处理的请求数,因为数据范围在 [ 1 , 1 0 5 ] [1,10^5] [1,105],所以可以开一个静态数组cnt替代哈希表,同时维护一个 m a x max max值表示cnt中的最大数,也就是处理的最多请求数。

【下面开始高能数据结构】
用优先队列维护忙碌池,一个包含编号和请求结束时间的二元组 ( i d x , e n d T i m e ) (idx, endTime) (idx,endTime),那么请求到来时,只要对比到来时间和 e n d T i m e endTime endTime即可判断是否能够处理。

由于请求分配规则是优先取大于等于 i % k i \% k i%k的最小值,若取不到则取大于等于 0 0 0的最小值。因此空闲池最好是支持二分的有序集合,则使用基于红黑树的TreeSet结构。
【这段属于是懂了但没完全懂,二分……是为了啥呢】

Java

class Solution {
    static int N = 100001; //数据范围
    static int[] cnt = new int[N]; //统计每个server处理的请求数
    public List<Integer> busiestServers(int k, int[] arrival, int[] load) {
        Arrays.fill(cnt, 0);
        int max = 0;
        Map<Integer, Integer> map = new HashMap<>();
        PriorityQueue<int[]> busy = new PriorityQueue<>((a,b) -> a[1] - b[1]); //优先队列(小顶堆)(编号, 结束时间)
        TreeSet<Integer> free = new TreeSet<>(); //红黑树
        for(int i = 0; i < k; i++)
            free.add(i);
        for(int i = 0; i < arrival.length; i++) {
            int sta = arrival[i], end = sta + load[i]; //请求开始与结束时间
            while(!busy.isEmpty() && busy.peek()[1] <= sta) //上一请求结束时间 < 下一请求开启时间
                free.add(busy.poll()[0]);
            Integer u = free.ceiling(i % k); //处理该请求的server编号
            if(u == null)
                u = free.ceiling(0);
            //取不到
            if(u == null)
                continue; //丢弃该请求
            //取到了,更新空闲池和忙碌池
            free.remove(u);
            busy.add(new int[]{u, end});
            max = Math.max(max, ++cnt[u]);
        }
        List<Integer> res = new ArrayList<>();
        for(int i = 0; i < k; i++)
            if(cnt[i] == max) //筛选结果
                res.add(i);
        return res;
    }
}
  • 时间复杂度: O ( ( k + n ) log ⁡ k ) O((k+n)\log k) O((k+n)logk),初始将server存入 f r e e free free复杂度为 O ( k log ⁡ k ) O(k\log k) O(klogk),每个请求取server时调用ceiling(最多两次)的复杂度为 O ( log ⁡ k ) O(\log k) O(logk),共 n n n个请求即 O ( n log ⁡ k ) O(n\log k) O(nlogk)
  • 空间复杂度: O ( k ) O(k) O(k),每个容器最大都是 k k k

TreeSet

  • 学习参考链接
  • 一个有序的Set集合,基于TreeMap实现,支持自然排序或根据Comparator排序。
  • 它基本操作比之前学的HashSet慢,但是有序且易于导航(即内部实现了SortedSet和NavigableSet接口)。
  • 在此用于实现一个红黑树(自平衡二叉查找树),就只用了很简单的ceiling
方法功能
add()添加元素
remove()删除元素
ceiling(key)返回大于等于key的最小元素

PriorityQueue

  • 学习参考链接
  • 无界的基于优先级的队列,支持自然排序或根据Comparator排序,头最小尾最大。(无界所以线程不安全)
  • 此处初始化时是用lambda定义pair排序逻辑,按上面代码里会实现小顶堆(堆顶为最小值),要改变的话括号里改为(a, b) -> b - a)即可。
方法功能
add()添加元素
peek()返回头元素(最小值)
poll()删除并返回头元素(最小值)

C++

class Solution {
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
        vector<int> cnt(k); //统计每个server处理的请求数
        set<int> free;
        for(int i = 0; i < k; i++)
            free.insert(i);
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> busy; //小顶堆,(编号, 结束时间)
        
        for(int i = 0; i < arrival.size(); i++) {
            int sta = arrival[i], end = sta + load[i]; //请求开始与结束时间
            while(!busy.empty() && busy.top().first <= sta) { //上一请求结束时间 < 下一请求开启时间
                free.insert(busy.top().second);
                busy.pop();
            }
            auto u = free.lower_bound(i % k); //处理该请求的server编号
            if(u == free.end())
                u = free.begin();
            //取不到
            if(u == free.end())
                continue; //丢弃该请求
            //取到了,更新空闲池和忙碌池
            cnt[*u]++;
            busy.emplace(end, *u);
            free.erase(u);
        }
        //取最大请求处理数并筛选结果
        int max = *max_element(cnt.begin(), cnt.end());
        vector<int> res;
        for(int i = 0; i < k; i++)
            if(cnt[i] == max)
                res.push_back(i);
        return res;
    }
};
  • 时间复杂度: O ( ( k + n ) log ⁡ k ) O((k+n)\log k) O((k+n)logk)
  • 空间复杂度: O ( k ) O(k) O(k)

set

  • 学习参考链接
  • 关联式容器,键与值需相等,元素需各不相同,会自动根据键值大小进行排序。
  • 因返回值均为双向迭代器,不能与空值NULL作比较,则判断是否指向end()作为替代。
方法功能
insert()插入新元素
lower_bound(key)返回指向大于等于key的最小元素的双向迭代器
begin()返回指向容器中的第一个元素的双向迭代器
end()返回指向容器中的最后一个元素的双向迭代器
erase(key)删除容器中的key元素

priority_queue

方法功能
greater<>使最小元素排在前面,队头为最小
top()返回第一个元素的引用
pop()删除第一个元素
emplace()可理解为添加新元素

思路二:双优先队列

两个池都用优先队列实现,其他一样,代码就不搞了。
时空复杂度应该也是一样的,有差异的话就是把server存入 f r e e free free的地方可能有问题。

总结

思路就是模拟,难在数据结构的处理上。


欢迎指正与讨论!
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值