有序集合 1[set][map][priority_queue]

21 篇文章 0 订阅

1 有序集合

泛指set, map, pirority_queue等能够按照key进行排序,然后使用lower_bound和upper_bound来进行log(n)复杂度查询的基础数据结构
(注意priority_queue仅能够在堆顶进行操作)
示例如下:

        set<int, std::less<int>> spareServers;
<被作者省略>
            // request distribute, find the server
            int tarServer = -1;
            auto itr = spareServers.lower_bound(arrivalIdx % k);
            // cout << "trying to find: " << arrivalIdx % k << endl;
            if(itr != spareServers.end()) {
                tarServer = *itr;
                // cout << "find tar1: " << tarServer << endl;
            } else { // search from the start just like search like a cycle
                tarServer = *spareServers.lower_bound(0);
                // cout << "find tar2: " << tarServer << endl;
            }

2 具体示例

0363maxSumSubMatrix 最大子区域和

1 题目:

https://leetcode-cn.com/problems/max-sum-of-rectangle-no-larger-than-k/

2 解题思路:

  • 1 普通思路:一维前缀和,按行计算前缀和,使用m x n的矩阵存储,然后计算矩形区域,则只需要O(m)的计算复杂度
  • 2 优化:二维前缀和,pre[i][j]存储的是0,0到i,j处的矩形区域和,那么计算方式为:

preMat2[i+1][j+1] = preMat2[i][j+1] + preMat2[i+1][j] - preMat2[i][j] + matrix[i][j];

  • 3 使用二位前缀和去计算子区域的和,搜索子区域的方式:
    • 3.1 对于每个子区域的上下边界搜索左右边界,上下左边界搜索复杂度为o(n^2), 而后搜索左右边界,对于每一个右边界O(n),搜索左边界,什么样的左边界?left满足sum(up, down, 0, right) - k <= sum(up, down, 0, left), 也就是在[0, right]的范围内找到最小的大于sum(up, down, 0, right) - k的left,在遍历的过程中使用set存所有的sum(up, down, 0, right), 在这个set里面找left,使用lower_bound函数,只需要log(n)复杂度,于是总复杂度为 o(m^2 * nlog(n))
    • 3.2 上述核心思想: 本来需要找right和left的对子,但是现在借助k,找left就变成在right的历史中搜索即可
      • 3.2.1 原来思想:sum(up, down, 0, right) - sum(up, down, 0, left) <= k
      • 3.2.2 新的思想:sum(up, down, 0, right) - k <= sum(up, down, 0, left)
class Solution {
public:
    static constexpr int matLen = 102;
    vector<vector<int>> preMat; // 2d prefix sum
    int m = -1;
    int n = -1;
    int maxSumSubmatrix(vector<vector<int>>& matrix, int k) {
        // cal 2d prefix sum
        m = matrix.size();
        n = matrix[0].size();
        preMat.resize(m+1, vector<int>(n+1, 0));
        for(int i = 0; i < m; ++i){
            for(int j = 0; j < n; ++j){
                preMat[i+1][j+1] = preMat[i][j+1] + preMat[i+1][j] - preMat[i][j] + matrix[i][j];
                // cout << preMat[i+1][j+1] << endl;
            }
        }

        // for each up and down, find max l,r
        int res = INT_MIN;
        for(int d = 0; d < m; ++d) {
            for(int u = d; u >= 0; --u) {
                set<int> lSet = {0};
                for(int r = 0; r < n; ++r) {
                    int sumRight = sumRegion(u, d, 0, r);
                    set<int>::iterator lbPtr = lSet.lower_bound(sumRight - k);
                    lSet.insert(sumRight);
                    if(lbPtr != lSet.end()) {
                        res = max(res, sumRight - *lbPtr);
                    }
                }
            }
        }

        return res;
    }

    int sumRegion(int up, int down, int left, int right) {
        return preMat[down+1][right+1] - preMat[down+1][left] - preMat[up][right+1] - preMat[up][left];
    }
};

0699fallingSquares 掉落的方块

1 题目:

https://leetcode-cn.com/problems/falling-squares/

2 解题思路:

  • 1 普通思路:使用一维数组模拟每个位置方块高度,复杂度为o(N^2),由于N很大,所以会超时
  • 2 优化:使用坐标压缩,将所有方块的边界存储在2000以内(因为一共就1000个方块),然后更新高度就按照2000以内去更新即可
class Solution {
public:
    vector<int> fallingSquares(vector<vector<int>>& positions) {
        // coordinate compression
        set<int, std::less<int>> coords;
        for(vector<int>& coord : positions) {
            coords.insert(coord[0]);
            coords.insert(coord[0] + coord[1] - 1);
        }

        unordered_map<int, int> borderToIdx;
        int t = 0;
        for(auto& i : coords) {
            borderToIdx[i] = t++;
        }

        // cal heights
        vector<int> heights(t);
        vector<int> res;
        int curHeightest = INT_MIN;
        for(vector<int>& square : positions) {
            int l = borderToIdx[square[0]];
            int r = borderToIdx[square[0] + square[1] - 1];
            int h = square[1];
            int updatedHeight = update(l, r, h, heights);
            curHeightest = max(curHeightest, updatedHeight);
            res.emplace_back(curHeightest);
        }
        return res;
    }

    // update and return updated height of [l, r]
    int update(int l, int r, int h, vector<int>& heights) {
        int oldHeight = INT_MIN;
        for(int i = l; i <= r; ++i) {
            oldHeight = max(oldHeight, heights[i]);
        }
        int newHeight = INT_MIN;
        for(int i = l; i <= r; ++i) {
            heights[i] = oldHeight + h;
        }
        return oldHeight + h;
    } 
};

0715 Range 模块 rangeModule

1 题目:

https://leetcode-cn.com/problems/range-module/

2 解题思路:

  • 1 普通思路:使用map记录这些离散的区间
    • 1.1 添加、删除: 遍历map,看map的每一个区间和当前区间的关系,这样处理最为合适
    • 1.2 查询:使用upper_bound查询到第一个(左侧)比目标的left小的区间,然后看目标的left和right是否在map对应的区间内部
class RangeModule {
public:
    map<int, int, std::less<int>> intervals;
    RangeModule() {
        
    }
    
    void addRange(int left, int right) {
        if(intervals.size() == 0) {
            intervals[left] = right;
            print();
            return ;
        }
        for(auto it = intervals.begin(); it != intervals.end(); ++it) {
            if(it->first >= left) {
                if(it->first > right) {
                    continue;
                }
                while(it != intervals.end() && it->second <= right) {
                    it = intervals.erase(it);
                }
                if(it == intervals.end()){
                    intervals[left] = right;

                } else {
                    if(it->first <= right) {
                        int newRight = it->second;
                        intervals.erase(it);
                        intervals[left] = newRight;
                    }else {
                        intervals[left] = right;
                    }
                }
                print();
                return;
            } else { // it->first < left
                if(it->second < left) {
                    continue;
                }
                int newLeft = it->first;
                while(it != intervals.end() && it->second <= right) {
                    it = intervals.erase(it);
                }
                if(it == intervals.end()){
                    intervals[newLeft] = right;
                } else {
                    if(it->first <= right) {
                        int newRight = it->second;
                        intervals.erase(it);
                        intervals[newLeft] = newRight;
                    }else {
                        intervals[newLeft] = right;
                    }
                }
                print();
                return;
            }
        }
        intervals[left] = right;
        print();
    }
    
    bool queryRange(int left, int right) {
        if(intervals.empty()) {
            return false;
        }

        auto lBig = intervals.upper_bound(left);
        if(lBig != intervals.begin()) {
            --lBig;
            return  lBig->second >= right;
        } else {
            return false;
        }
        return false;
    }
    
    void removeRange(int left, int right) {
        // for(auto it = intervals.begin(); it != intervals.end(); ++it) {
        //     for(int i = 0)
        // }

        auto lBig = intervals.lower_bound(left);
        int newLeft = 0;
        if(lBig != intervals.begin()) {
                // cout << "d1.5" << endl;
            --lBig;
            if(right < lBig->second) {
                intervals[right] = lBig->second;
                intervals[lBig->first] = left;
                print();
                return ;
            } else {
                // cout << "d2" << endl;
                if(lBig->second > left) {
                    // cout << "d2.5" << endl;
                    intervals[lBig->first] = left;
                }
            }
            ++lBig;

            while (lBig != intervals.end() && lBig->second < right) {
                lBig = intervals.erase(lBig);
            }
            if(lBig != intervals.end()) {
                if(lBig->first < right) {
                    int secondRight = lBig->second;
                    intervals.erase(lBig);
                    intervals[right] = secondRight;
                }
            }
        } else {
            while (lBig != intervals.end() && lBig->second < right) {
                lBig = intervals.erase(lBig);
            }
            if(lBig != intervals.end()) {
                if(lBig->first < right) {
                    int secondRight = lBig->second;
                    intervals.erase(lBig);
                    intervals[right] = secondRight;
                }
            }
        }

        print();
        
    }

    void print() {
        // cout << "-----st-----" << endl;
        // for(auto it = intervals.begin(); it != intervals.end(); ++it) {
        //     cout << it->first << " " << it->second << endl;
        // }
        // cout << "-----ed-----" << endl;
    }
};

/**
 * Your RangeModule object will be instantiated and called as such:
 * RangeModule* obj = new RangeModule();
 * obj->addRange(left,right);
 * bool param_2 = obj->queryRange(left,right);
 * obj->removeRange(left,right);
 */

0850. 矩形面积 II

1 题目:

https://leetcode-cn.com/problems/rectangle-area-ii/solution

2 解题思路:

  • 1 参考官方思路的扫描线:https://leetcode-cn.com/problems/rectangle-area-ii/solution/ju-xing-mian-ji-ii-by-leetcode/
  • 2 总结过程:
    • 2.1 将一个矩形看成x1,x2,y1,ST; x1,x2,y2,ED;的两条线
    • 2.2 而后用active记录还没有遇到ED的那些矩形的第一个扫描线集合,每回新来了一根线,则将active中的所有线和当前线计算对应面面积加起来
    • 2.3 当active中遇到了矩形的ED,则将active中对应的矩形的ST删除
class Solution {
public:
    static constexpr long long bigPrime = 1000000007;
    int rectangleArea(vector<vector<int>>& rectangles) {
        // lines
        int ST = 0;
        int ED = 1;

        vector<vector<int>> lines;
        for(auto& vec : rectangles) {
            lines.emplace_back(vector<int>{vec[0], vec[2], vec[1], ST});
            lines.emplace_back(vector<int>{vec[0], vec[2], vec[3], ED});
        }

        // sort the lines by the y
        sort(lines.begin(), lines.end(), 
            [](vector<int>& a, vector<int>& b) {
                return a[2] < b[2];
        });

        // scan the lines
        vector<vector<int>> actives;
        long long res = 0;
        int lastY = lines[0][2];
        for(auto& line : lines) {
            int curY = line[2], type = line[3], x1 = line[0], x2 = line[1];
            int width = 0;
            int actX = -1;
            
            // actives: those opend lines, sorted by y
            for(auto& act : actives) {
                // cout << "act: " << act[0] << " " << act[1] << endl;
                actX = max(actX, act[0]);
                width += max(act[1] - actX, 0);
                actX = max(actX, act[1]);
            }

            res += static_cast<long long>(width) * (curY - lastY) % bigPrime;
            // cout << "w: " << width << endl;

            // add new actives
            if(type == ST) {
                actives.emplace_back(vector<int>{x1, x2, curY, type});
                // cout << "insert x1,2: " << x1 << " " << x2 << endl;
                // sort the opend lines of the started points
                sort(actives.begin(), actives.end(), 
                    [](vector<int>& line1, vector<int>& line2) {
                        return line1[0] < line2[0];
                    });
            } else {
                // find the active and rm it
                for(int i = 0; i < actives.size(); ++i) {
                    if(actives[i][0] == x1 && actives[i][1] == x2) {
                        actives.erase(actives.begin() + i);
                        // cout << "earse x1,2: " << x1 << " " << x2 << endl;
                        break;
                    }
                }
            }

            lastY = curY;
        }

        return res % bigPrime;

    }

    vector<vector<int>> getSkyLine(vector<vector<int>>& buildings) {
        function<bool(pair<int, int>&, pair<int, int>&)> cmp = 
        [](pair<int, int>& a, pair<int, int>& b) {
            return a.second < b.second;
        };

        // <rightBound, height>
        priority_queue<pair<int, int>, vector<pair<int, int>>, function<bool(pair<int, int>&, pair<int, int>&)> > queue(cmp);

        vector<int> bounds;
        vector<vector<int>> res;
        int n = buildings.size();
        for(int i = 0; i < n; ++i) {
            bounds.emplace_back(buildings[i][0]);
            bounds.emplace_back(buildings[i][1]);
        }

        sort(bounds.begin(), bounds.end(), std::less<int>());
        // std::cout << "c1" << endl;
        
        int j = 0;
        for(auto& curBound : bounds) {
            // std::cout << "c "  << j << endl;
            // push buildings lefter than curBound until one righter meet
            while(j < buildings.size() && curBound >= buildings[j][0]) {
                queue.push(make_pair(buildings[j][1], buildings[j][2]));
                ++j;
            }
            // std::cout << "c "  << "adf" << endl;

            // pop out those rec unrelevant
            while(!queue.empty() && curBound >= queue.top().first) {
                queue.pop();
            }

            int curBoundHeight = queue.empty() ? 0 : queue.top().second;

            // if(res.size() == 0 || res.back()[1] != curBoundHeight) {
            //     res.emplace_back(vector<int>{curBound, curBoundHeight});
            // }
            if(res.size() == 0 || res.back()[1] != curBoundHeight) {
                res.emplace_back(vector<int>{curBound, curBoundHeight});
            }
        }

        return res;
    }

    void print(vector<vector<int>>& vec) {
        for(auto& v : vec) {
            for(auto& i : v) {
                cout << i << " ";
            }
            cout << endl;
        }

    }
};

0895maxFreqStack 最大频率栈

1 题目:

https://leetcode-cn.com/problems/maximum-frequency-stack/

2 解题思路:map/hash栈

  • 1 参考官方思路的扫描线:https://leetcode-cn.com/problems/maximum-frequency-stack/solution/zui-da-pin-lu-zhan-by-leetcode/
  • 2 总结过程:
    • 2.1 首先用map维持每一个变量的频率
      • 2.1.1 维持方法: push中对应的key加一,pop对应的key减一
    • 2.2 如何获得当前最大频率?
      • 2.2.1 在每个变量push和pop的时候我们可以获得对应的key的频率,那么所有的key的频率变动都是在push和pop执行完成之后,那么只需要用一个变量maxFreq维持最大频率即可
    • 2.3 知道最大频率,如何获得当前最大频率的变量?
      • 2.3.1 使用map<频率,频率对应的数的集合>来记录即可,然后用2.2中使用maxFreq获取最大频率对应的数字的集合,那如何从这个集合中获取在栈顶的数据呢?map<频率,频率对应的数的集合>中 频率对应的数的集合 使用stack去存就好了,因为stack的栈顶总是存着最新来的数据
    • 2.4 一个实例: 3,3,3都是push,那么在频率为1,2,3的栈中,很自然的都有一个3,自然体现在哪里呢?现在pop一下,频率为3对应的stack为空,然后最大频率变为2,然后2里面同样是3
class FreqStack {
public:
    int opNum;
    map<int, int> opNumMap;
    map<int, int> freqMap;
    map<int, vector<int>*> groupByFreq;
    int maxFreq;
    FreqStack() {

    }
    
    void push(int val) {
        // cout << "pushing st: with max freq: " << maxFreq << endl;
        freqMap[val]++;
        if(groupByFreq[freqMap[val]] == nullptr) {
            groupByFreq[freqMap[val]] = new vector<int>();
        }
        groupByFreq[freqMap[val]]->emplace_back(val);
        maxFreq = max(maxFreq, freqMap[val]);
        // cout << "pushing done: " << val << "with maxFreq: " << maxFreq << endl;
    }

    int pop() {
        // cout << "pop st with maxFreq: " << maxFreq << endl;
        // find biggest frequency and most newly element to rm
        int popRes = groupByFreq[maxFreq]->back();
        groupByFreq[maxFreq]->pop_back();
        if(groupByFreq[maxFreq]->size() == 0) {
            delete groupByFreq[maxFreq];
            groupByFreq[maxFreq] = nullptr;
            --maxFreq;
        }
        // cout << "pop ed" << popRes << " with maxFreq: " << maxFreq << endl;
        freqMap[popRes]--;
        return popRes;
    }
};

/**
 * Your FreqStack object will be instantiated and called as such:
 * FreqStack* obj = new FreqStack();
 * obj->push(val);
 * int param_2 = obj->pop();
 */

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

1 题目:

https://leetcode-cn.com/problems/find-servers-that-handled-most-number-of-requests/

2 解题思路:

  • 1 自然的思路:对于每一个到来的请求,我们将服务器分为两部分,一部分是空闲服务器,一部分是繁忙服务器
    • 1.1 如何快速找到空闲服务器?我们用set记录空闲服务器即可,两次使用lower_bound完成cycle查询
    • 1.2 如何记录繁忙服务器?在每一个请求到来以及处理完成,都可能出现繁忙服务器的改动和空闲服务器的改动,于是采用map<到期时间,相应服务器列表>来存储繁忙服务器
    • 1.3 如何处理请求处理完成时的服务器从繁忙变为空闲?
      • 1.3.1 采用小根堆存储当前所有繁忙服务器的到期时间,那么只需要在请求到来的时候,将所有比当前请求小的到期时间的繁忙服务器变为空闲即可
class Solution {
public:
    vector<int> busiestServers(int k, vector<int>& arrival, vector<int>& load) {
        // using small root heap to store the smallest avaliable server
        // auto cmp = [](const int& a, const int b) 
        set<int, std::less<int>> spareServers;
        map<int, vector<int>> busyServers;
        priority_queue<int, vector<int>, std::greater<int>> dues;
        vector<pair<int, int>> serverSumLoad;

        for(int i = 0; i < k; ++i) {
            spareServers.insert(i);
            serverSumLoad.push_back(make_pair(i,0));
        }

        // deal request
        int reqNum = arrival.size();
        int arrivalIdx = 0;
        for(int arrival : arrival) {
            // cout << "req: " << arrival << "load: " << load[arrivalIdx] << endl;
            // release servers
            while(!dues.empty() && dues.top() <= arrival) {
                int due = dues.top();
                dues.pop();
                vector<int>& toRelease = busyServers[due];
                for(auto i : toRelease) {
                    spareServers.insert(i);
                    // cout << "releasing " << i << endl;
                }
                // busyServers.erase(due);
            }

            // abandon the request
            if(spareServers.empty()) {
                ++ arrivalIdx;
                // cout << "abandon!" << endl;
                continue;
            }

            // request distribute, find the server
            int tarServer = -1;
            auto itr = spareServers.lower_bound(arrivalIdx % k);
            // cout << "trying to find: " << arrivalIdx % k << endl;
            if(itr != spareServers.end()) {
                tarServer = *itr;
                // cout << "find tar1: " << tarServer << endl;
            } else { // search from the start just like search like a cycle
                tarServer = *spareServers.lower_bound(0);
                // cout << "find tar2: " << tarServer << endl;
            }
            spareServers.erase(tarServer);
            
            // set the server to busy
            int due = arrival + load[arrivalIdx];
            if(busyServers.find(due) == busyServers.end()) {
                vector<int> tmpVec = {tarServer};
                busyServers[due] = tmpVec;
                dues.push(due);
                // cout << "init busy due : " << tarServer << " to " << due << endl;
            } else {
                busyServers[due].push_back(tarServer);
                // cout << "add busy due : " << tarServer << " to " << due << endl;
            }
            serverSumLoad[tarServer].second ++;

            ++ arrivalIdx;
        }

        sort(serverSumLoad.begin(), serverSumLoad.end(),[](
            const pair<int, int>& p1, const pair<int, int>& p2
        ) {
            return p1.second > p2.second;
        });
        
        // for(int ed = 0; ed < serverSumLoad.size(); ++ed) {
        //     cout << "server: " << serverSumLoad[ed].first << " > " << serverSumLoad[ed].second << endl;
        // }
        int ed = 1;
        vector<int> res = {serverSumLoad[0].first};
         
        
        for(; ed < serverSumLoad.size(); ++ed) {
            if(serverSumLoad[ed].second != serverSumLoad[0].second) {
                break;
            }
            res.emplace_back(serverSumLoad[ed].first);
        }
        
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值