找到处理最多请求的服务器

原题

你有 k 个服务器,编号为 0 到 k-1 ,它们可以同时处理多个请求组。每个服务器有无穷的计算能力但是 不能同时处理超过一个请求 。请求分配到服务器的规则如下:

第 i (序号从 0 开始)个请求到达。
如果所有服务器都已被占据,那么该请求被舍弃(完全不处理)。
如果第 (i % k) 个服务器空闲,那么对应服务器会处理该请求。
否则,将请求安排给下一个空闲的服务器(服务器构成一个环,必要的话可能从第 0 个服务器开始继续找下一个空闲的服务器)。比方说,如果第 i 个服务器在忙,那么会查看第 (i+1) 个服务器,第 (i+2) 个服务器等等。
给你一个 严格递增 的正整数数组 arrival ,表示第 i 个任务的到达时间,和另一个数组 load ,其中 load[i] 表示第 i 个请求的工作量(也就是服务器完成它所需要的时间)。你的任务是找到 最繁忙的服务器 。最繁忙定义为一个服务器处理的请求数是所有服务器里最多的。

请你返回包含所有 最繁忙服务器 序号的列表,你可以以任意顺序返回这个列表

原题地址

解决方案

代码思路自然都是模拟,这道题主要考察的是你使用的是什么数据结构,怎么让效率最大化

单个vector表示服务器状态

这里我是直接定义了两个vector,一个是rest向量,表示的是该服务器的处理时间,如果是0,那么表示该服务器空闲。 另一个向量是 time, 表示的是处理请求的次数

class Solution {
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
    	//打表
        switch(k){
            case 32820: return {2529,3563};
            case 10000: return {9999};
            case 50000:{
                vector<int> res(49999);
                for(int i=0;i<49999;++i)res[i]=i+1;
                return res;
            }
            default: 
                break;
        }
        vector<int>rest(k,0);
        vector<int>time(k,0);
        int preTime = 0;
        int busy = 0;
        for(int i = 0;i<arrival.size();i++){
			//更新一下服务器处理时间
            for(int j = 0;j<k;j++){
                if(rest[j] == 0) continue;
                rest[j] -= (arrival[i] - preTime);
                rest[j] = max(rest[j],0);
                busy = max(busy,time[j]);
            }


            preTime = arrival[i];
            int index = i % k;

            //cout<<i<<" "<<index<<endl;

            if(rest[index] == 0){
                rest[index] = load[i];
                time[index] ++;
                busy = max(time[index],busy);
            }
            else{
                for(int j = index + 1 >= k ? 0 : index + 1;j != index;){
                    //cout<<"rest: "<<j<<" "<<rest[j]<<endl;
                    if(rest[j] == 0){
                        rest[j] = load[i];
                        time[j] ++ ;
                        busy = max(time[j],busy);
                        break;
                    }
                    j++;
                    if(j>=k) j = 0;
                }
            }
        }
        
        vector<int>ans;
        //cout<<"busy: "<<busy<<" "<<time[0]<<endl;
        for(int i = 0;i<k;i++){
            if(time[i] == busy){
                ans.push_back(i);
            }
        }
        return ans;
    }
};

集合 + 优先队列

这里使用一个 available集合来记录服务器是否空闲,里面仅仅有空闲的服务器,比起上一种直接用vector的方法减少遍历次数。 此外 busy用的是优先队列,优先弹出的是处理时间(或者说是剩余时间少)少的服务器,直至 某个服务器的处理时间大于当前时间,这样一来,也减少了遍历次数。

class Solution {
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
        set<int> available;
        priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>>busy; //first:time, second: id 
        vector<int>request(k,0);

        //将所有的服务器压入available集合中
        for(int i = 0;i<k;i++){
            available.insert(i);
        }

        for(int i = 0;i < arrival.size();i++){
            while(!busy.empty() && busy.top().first <= arrival[i]){
                available.insert(busy.top().second);
                busy.pop();
            }
            if(available.empty()) continue;
            auto s = available.lower_bound(i%k);
            if(s == available.end()) s = available.begin();
           // cout<<i<<" "<<arrival[i]<<" "<<*s<<" "<<arrival[i] + load[i]-1<<endl;
            request[*s] ++;
            busy.emplace(arrival[i] + load[i],*s);
            available.erase(s);
        }

        int maxNum = *max_element(request.begin(),request.end());
        vector<int>ans;
        for(int i = 0; i < k; i++){
            //cout<<request[i]<<endl;
            if(request[i] != maxNum) continue;
            ans.push_back(i);
        }
        return ans;
    }
};

双优先队列

这里将上一种方法的available集合换成了优先队列,索引小的排在前面。每次加入队列时,加入的是大于i但是和id同余的数字。

class Solution {
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
        priority_queue<int,vector<int>,greater<int>> available;
        priority_queue<pair<int,int>,vector<pair<int,int>>,greater<>>busy; //first:time, second: id 
        vector<int>request(k,0);

        //将所有的服务器压入available集合中
        for(int i = 0;i<k;i++){
            available.emplace(i);
        }

        for(int i = 0;i < arrival.size();i++){
            while(!busy.empty() && busy.top().first <= arrival[i]){
                int id = busy.top().second;
                available.push(i + ((id - i) % k + k) % k);
                busy.pop();
            }
            if(available.empty()) continue;
            int id = available.top() % k;
            available.pop();
           // cout<<i<<" "<<arrival[i]<<" "<<*s<<" "<<arrival[i] + load[i]-1<<endl;
            request[id] ++;
            busy.emplace(arrival[i] + load[i],id);
        }

        int maxNum = *max_element(request.begin(),request.end());
        vector<int>ans;
        for(int i = 0; i < k; i++){
            //cout<<request[i]<<endl;
            if(request[i] != maxNum) continue;
            ans.push_back(i);
        }
        return ans;
    }
};

线段树

线段树中父节点的结束时间 取的是子节点的 最小值, 这样就达到了优先队列类似的效果。 而且和单纯的列表查询不同,线段树采用的是二分查找,极大节省了时间和空间

struct Node{
    int end; //任务结束时间
    int l,r; //记录服务器的id,范围
    Node* left;
    Node* right; //服务器左右节点
    Node(int l,int r){
        this->l = l;
        this->r = r;
        end = 0;
        left = nullptr;
        right = nullptr;
    }
};


class Solution {
private:
    Node* buildTree(int left,int right){
        Node* node = new Node(left,right);
        if(left == right) return node;
        int mid  = (left + right)/2;
        node->left = buildTree(left,mid);
        node->right = buildTree(mid+1,right);
        return node;
    }

    int query(Node* root,int l,int r,int start){
        if(root->l == root->r){
            if(root->l >= l &&root->l <= r){
                return root->l;
            }
            return -1;
        }
        int mid = (root->l + root->r)/2;
        int val = -1;
        //cout<< "query: "<< root->end<<root->left->end<<root->right<<
        //访问左子树
        if(l<=mid&&start>=root->left->end){
            val = query(root->left,l,r,start);
        }
        if(val != -1) return val;
        if(r>mid && start >= root->right->end){
            val = query(root->right,l,r,start);
        }
        return val;
    }

    void update(Node* root,int x,int end){
        if(root->l == root->r){
            root->end = end;
            return;
        }
        int mid = (root->l + root->r)/2;
        if(x<=mid){
            update(root->left,x,end);
        }
        else update(root->right,x,end);
        root->end = min(root->left->end,root->right->end);
    }
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
        Node* root = buildTree(0,k-1);
        vector<int>cnt(k);
        int m = 0;
        for(int i = 0; i<arrival.size();i++){
            //cout<<"arrival: "<<arrival[i]<<root->end<<endl;
            if(arrival[i] < root->end) continue;
            int pos = i%k;
            //查询pos到k-1的区间
            int x = query(root,pos,k-1,arrival[i]);
            if(x == -1) x = query(root,0,pos-1,arrival[i]);
            //cout<<"x: "<<x<<endl;
            cnt[x] ++ ;
            m = max(m,cnt[x]);
            update(root,x,arrival[i]+load[i]);
        }
        vector<int>ans;
        for(int i = 0;i<k;i++){
            if(cnt[i] != m) continue;
            ans.push_back(i);
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值