优先队列

  • 优先队列(priority queue)
  • [1962. 移除石子使总数最小](https://leetcode.cn/problems/remove-stones-to-minimize-the-total/)
  • [1046. 最后一块石头的重量](https://leetcode-cn.com/problems/last-stone-weight/)
  • [215. 数组中的第K个最大元素](https://leetcode.cn/problems/kth-largest-element-in-an-array/)
  • [347. 前 K 个高频元素](https://leetcode.cn/problems/top-k-frequent-elements/)
  • [692. 前K个高频单词](https://leetcode.cn/problems/top-k-frequent-words/)
  • [2208. 将数组和减半的最少操作次数](https://leetcode-cn.com/problems/minimum-operations-to-halve-array-sum/)
  • [895. 最大频率栈](https://leetcode.cn/problems/maximum-frequency-stack/)
  • [★1642. 可以到达的最远建筑](https://leetcode.cn/problems/furthest-building-you-can-reach/)
  • [2336. 无限集中的最小数字](https://leetcode.cn/problems/smallest-number-in-infinite-set/)
  • [★2406. 将区间分为最少组数](https://leetcode.cn/problems/divide-intervals-into-minimum-number-of-groups/)
  • [1094. 拼车](https://leetcode.cn/problems/car-pooling/)
  • [★871. 最低加油次数](https://leetcode-cn.com/problems/minimum-number-of-refueling-stops/)
  • [1801. 积压订单中的订单总数](https://leetcode.cn/problems/number-of-orders-in-the-backlog/)
  • [1942. 最小未被占据椅子的编号](https://leetcode.cn/problems/the-number-of-the-smallest-unoccupied-chair/)
  • [264. 丑数 II](https://leetcode.cn/problems/ugly-number-ii/)
  • [239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
  • [373. 查找和最小的K对数字](https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums/)
  • [1696. 跳跃游戏 VI](https://leetcode.cn/problems/jump-game-vi/)
  • [1383. 最大的团队表现值](https://leetcode.cn/problems/maximum-performance-of-a-team/)
  • [★1705. 吃苹果的最大数目](https://leetcode-cn.com/problems/maximum-number-of-eaten-apples/)
  • [★1353. 最多可以参加的会议数目](https://leetcode-cn.com/problems/maximum-number-of-events-that-can-be-attended/)
  • [857. 雇佣 K 名工人的最低成本](https://leetcode.cn/problems/minimum-cost-to-hire-k-workers/)
  • [1425. 带限制的子序列和](https://leetcode.cn/problems/constrained-subsequence-sum/)
  • [630. 课程表 III](https://leetcode-cn.com/problems/course-schedule-iii/)
  • [218. 天际线问题](https://leetcode.cn/problems/the-skyline-problem/)
  • [2386. 找出数组的第 K 大和](https://leetcode.cn/problems/find-the-k-sum-of-an-array/)
  • [1054. 距离相等的条形码](https://leetcode-cn.com/problems/distant-barcodes/)
  • [632. 最小区间](https://leetcode.cn/problems/smallest-range-covering-elements-from-k-lists/)
  • [659. 分割数组为连续子序列](https://leetcode.cn/problems/split-array-into-consecutive-subsequences/)
  • [1488. 避免洪水泛滥](https://leetcode.cn/problems/avoid-flood-in-the-city/)
  • [1606. 找到处理最多请求的服务器](https://leetcode.cn/problems/find-servers-that-handled-most-number-of-requests/)
  • [1776. 车队 II](https://leetcode.cn/problems/car-fleet-ii/)
  • [1882. 使用服务器处理任务](https://leetcode.cn/problems/process-tasks-using-servers/)
  • [2233. K 次增加后的最大乘积](https://leetcode-cn.com/problems/maximum-product-after-k-increments/)
  • [2349. 设计数字容器系统](https://leetcode.cn/problems/design-a-number-container-system/)
  • [2353. 设计食物评分系统](https://leetcode.cn/problems/design-a-food-rating-system/)
  • [2402. 会议室 III](https://leetcode.cn/problems/meeting-rooms-iii/)
  • [2462. 雇佣 K 位工人的总代价](https://leetcode.cn/problems/total-cost-to-hire-k-workers/)
  • [1687. 从仓库到码头运输箱子](https://leetcode.cn/problems/delivering-boxes-from-storage-to-ports/)
  • [407. 接雨水 II](https://leetcode.cn/problems/trapping-rain-water-ii/)
  • [480. 滑动窗口中位数](https://leetcode.cn/problems/sliding-window-median/)
  • 1183. 矩阵中 1 的最大数量
  • [1851. 包含每个查询的最小区间](https://leetcode.cn/problems/minimum-interval-to-include-each-query/)
  • [743. 网络延迟时间](https://leetcode.cn/problems/network-delay-time/)
  • [2163. 删除元素后和的最小差值](https://leetcode.cn/problems/minimum-difference-in-sums-after-removal-of-elements/)
  • [855. 考场就座](https://leetcode.cn/problems/exam-room/)
  • [855. 考场就座](https://leetcode.cn/problems/exam-room/)
  • [1439. 有序矩阵中的第 k 个最小数组和](https://leetcode.cn/problems/find-the-kth-smallest-sum-of-a-matrix-with-sorted-rows/)
  • 1686. 石子游戏 VI
  • [1985. 找出数组中的第 K 大整数](https://leetcode.cn/problems/find-the-kth-largest-integer-in-the-array/)
  • [2290. 到达角落需要移除障碍物的最小数目](https://leetcode.cn/problems/minimum-obstacle-removal-to-reach-corner/)
  • 2454. 下一个更大元素 IV
  • [2503. 矩阵查询可获得的最大分数](https://leetcode.cn/problems/maximum-number-of-points-from-grid-queries/)
  • 23. 合并K个升序链表
  • 272. 最接近的二叉搜索树值 II
  • 355. 设计推特
  • 358. K 距离间隔重排字符串
  • 378. 有序矩阵中第 K 小的元素
  • 420. 强密码检验器
  • 451. 根据字符出现频率排序
  • 499. 迷宫 III
  • 502. IPO
  • 505. 迷宫 II
  • 675. 为高尔夫比赛砍树
  • 703. 数据流中的第 K 大元素
  • 767. 重构字符串
  • 882. 细分图中的可到达节点
  • 912. 排序数组
  • [1172. 餐盘栈](https://leetcode.cn/problems/dinner-plate-stacks/)
  • [1354. 多次求和构造目标数组](https://leetcode.cn/problems/construct-target-array-with-multiple-sums/)
  • [1368. 使网格图至少有一条有效路径的最小代价](https://leetcode.cn/problems/minimum-cost-to-make-at-least-one-valid-path-in-a-grid/)
  • [1388. 3n 块披萨](https://leetcode.cn/problems/pizza-with-3n-slices/)
  • [1424. 对角线遍历 II](https://leetcode.cn/problems/diagonal-traverse-ii/)
  • [1514. 概率最大的路径](https://leetcode.cn/problems/path-with-maximum-probability/)
  • [1675. 数组的最小偏移量](https://leetcode.cn/problems/minimize-deviation-in-array/)
  • [1738. 找出第 K 大的异或坐标值](https://leetcode.cn/problems/find-kth-largest-xor-coordinate-value/)
  • [1786. 从第一个节点出发到最后一个节点的受限路径数](https://leetcode.cn/problems/number-of-restricted-paths-from-first-to-last-node/)
  • [1792. 最大平均通过率](https://leetcode.cn/problems/maximum-average-pass-ratio/)
  • [1825. 求出 MK 平均值](https://leetcode.cn/problems/finding-mk-average/)
  • [1834. 单线程 CPU](https://leetcode.cn/problems/single-threaded-cpu/)
  • [1845. 座位预约管理系统](https://leetcode.cn/problems/seat-reservation-manager/)
  • [1878. 矩阵中最大的三个菱形和](https://leetcode.cn/problems/get-biggest-three-rhombus-sums-in-a-grid/)
  • [1912. 设计电影租借系统](https://leetcode.cn/problems/design-movie-rental-system/)
  • [1405. 最长快乐字符串](https://leetcode.cn/problems/longest-happy-string/)
  • [1499. 满足不等式的最大值](https://leetcode.cn/problems/max-value-of-equation/)
  • [1785. 构成特定和需要添加的最少元素](https://leetcode.cn/problems/minimum-elements-to-add-to-form-a-given-sum/)
  • [2034. 股票价格波动](https://leetcode.cn/problems/stock-price-fluctuation/)
  • [2054. 两个最好的不重叠活动](https://leetcode.cn/problems/two-best-non-overlapping-events/)
  • [2456. 最流行的视频创作者](https://leetcode.cn/problems/most-popular-video-creator/)
  • [2497. 图中最大星和](https://leetcode.cn/problems/maximum-star-sum-of-a-graph/)
  • [973. 最接近原点的 K 个点](https://leetcode.cn/problems/k-closest-points-to-origin/)
  • [1337. 矩阵中战斗力最弱的 K 行](https://leetcode.cn/problems/the-k-weakest-rows-in-a-matrix/)
  • [2333. 最小差值平方和](https://leetcode.cn/problems/minimum-sum-of-squared-difference/)
  • [2342. 数位和相等数对的最大和](https://leetcode.cn/problems/max-sum-of-a-pair-with-equal-sum-of-digits/)
  • [2343. 裁剪数字后查询第 K 小的数字](https://leetcode.cn/problems/query-kth-smallest-trimmed-number/)
  • [2344. 使数组可以被整除的最少删除次数](https://leetcode.cn/problems/minimum-deletions-to-make-array-divisible/)
  • [2424. 最长上传前缀](https://leetcode.cn/problems/longest-uploaded-prefix/)
  • [1263. 推箱子](https://leetcode.cn/problems/minimum-moves-to-move-a-box-to-their-target-location/)
  • [1753. 移除石子的最大得分](https://leetcode.cn/problems/maximum-score-from-removing-stones/)
  • [506. 相对名次](https://leetcode.cn/problems/relative-ranks/)
  • [1338. 数组大小减半](https://leetcode.cn/problems/reduce-array-size-to-the-half/)
  • [2099. 找到和最大的长度为 K 的子序列](https://leetcode.cn/problems/find-subsequence-of-length-k-with-the-largest-sum/)
  • [2335. 装满杯子需要的最短总时长](https://leetcode.cn/problems/minimum-amount-of-time-to-fill-cups/)
  • [2357. 使数组中所有元素都等于零](https://leetcode.cn/problems/make-array-zero-by-subtracting-equal-amounts/)
  • [621. 任务调度器](https://leetcode.cn/problems/task-scheduler/)
  • [658. 找到 K 个最接近的元素](https://leetcode.cn/problems/find-k-closest-elements/)
  • [295. 数据流的中位数](https://leetcode.cn/problems/find-median-from-data-stream/)
  • [2146. 价格范围内最高排名的 K 样物品](https://leetcode.cn/problems/k-highest-ranked-items-within-a-price-range/)
  • [2102. 序列顺序查询](https://leetcode.cn/problems/sequentially-ordinal-rank-tracker/)
  • [778. 水位上升的泳池中游泳](https://leetcode.cn/problems/swim-in-rising-water/)
  • [787. K 站中转内最便宜的航班](https://leetcode.cn/problems/cheapest-flights-within-k-stops/)
  • [786. 第 K 个最小的素数分数](https://leetcode.cn/problems/k-th-smallest-prime-fraction/)
  • [2398. 预算内的最多机器人数目](https://leetcode.cn/problems/maximum-number-of-robots-within-budget/)
  • [2182. 构造限制重复的字符串](https://leetcode.cn/problems/construct-string-with-repeat-limit/)
  • [2231. 按奇偶性交换后的最大数字](https://leetcode.cn/problems/largest-number-after-digit-swaps-by-parity/)
  • [2285. 道路的最大总重要性](https://leetcode.cn/problems/maximum-total-importance-of-roads/)
  • [1438. 绝对差不超过限制的最长连续子数组](https://leetcode.cn/problems/longest-continuous-subarray-with-absolute-diff-less-than-or-equal-to-limit/)
  • [1631. 最小体力消耗路径](https://leetcode.cn/problems/path-with-minimum-effort/)
  • [1648. 销售价值减少的颜色球](https://leetcode.cn/problems/sell-diminishing-valued-colored-balls/)
  • [862. 和至少为 K 的最短子数组](https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/)

优先队列(priority queue)

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out) 的行为特征。通常采用堆数据结构来实现。

1962. 移除石子使总数最小

class Solution {
    public int minStoneSum(int[] piles, int k) {
        PriorityQueue<Integer> q = new PriorityQueue<Integer>((a, b) -> b - a);
        for(int p : piles) q.offer(p);
        for(int i = 0; i < k; i++){
            q.offer((q.poll() + 1) / 2);
        }
        int ans = 0;
        for(int x : q) ans += x;
        return ans;
    }
}

1046. 最后一块石头的重量

class Solution:
    def lastStoneWeight(self, stones: List[int]) -> int:
        q = []
        for x in stones: heappush(q, -x)
        while q:
            a = -heappop(q)    
            if q: b = -heappop(q)
            else: return a
            if a > b:heappush(q, b - a)
        return 0
class Solution {
    public int lastStoneWeight(int[] stones) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for(int x : stones){ pq.offer(-x); }
        int a = 0, b = 0;
        while (!pq.isEmpty()){
            a = -pq.poll();
            if (pq.isEmpty()) return a;
            else b = -pq.poll();
            if (a > b) pq.offer(b - a);
        }
        return 0;
    }
}

215. 数组中的第K个最大元素

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        q = nums[:k]
        heapify(q)
        for x in nums[k:]:
            if x < q[0]: continue
            heappush(q, x)
            heappop(q)
        return q[0]
        
        nums.sort(reverse=True)
        return nums[k-1]
class Solution {
    public int findKthLargest(int[] nums, int k) {
        PriorityQueue<Integer> q = new PriorityQueue();
        for(int i = 0; i < k; i++) q.offer(nums[i]);
        for(int i = k, n = nums.length; i < n; i++){
            int x = nums[i];
            if(x <= q.peek()) continue;
            q.offer(x);
            q.poll();           
        }
        return q.peek();
    }
}

347. 前 K 个高频元素

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        return sorted(d := Counter(nums), key=lambda x:-d[x])[:k]
class Solution {
    public int[] topKFrequent(int[] nums, int k) {
        Map<Integer, Integer> map = new HashMap();
        for(int x : nums) map.merge(x, 1, Integer::sum);
        PriorityQueue<int[]> queue = new PriorityQueue<int[]>((a, b) -> a[1] - b[1]);
        for(Map.Entry<Integer, Integer> entry : map.entrySet()){
            queue.offer(new int[]{entry.getKey(), entry.getValue()});
            if(queue.size() > k) queue.poll();
        }
        int[] ret = new int[k];
        for (int i = 0; i < k; ++i) {            
            ret[i] = queue.poll()[0];
        }
        return ret;
    }
}

692. 前K个高频单词

public class Solution {
     public List<String> topKFrequent(String[] words, int k) {
        Map<String, Integer> map = new HashMap();
        for (String word : words) {
            map.merge(word, 1, Integer::sum);
        }
        PriorityQueue<String> q = new PriorityQueue<>((a, b) -> map.get(a).equals(map.get(b)) ? a.compareTo(b) : map.get(b) - map.get(a));
        for(String s : map.keySet()){
            q.offer(s); 
        }
        List<String> res = new ArrayList<String>(k);
        for(int i = 0; i < k; i++){
            res.add(q.poll());
        }        
        return res;
    }        
}

2208. 将数组和减半的最少操作次数

class Solution:
    def halveArray(self, nums: List[int]) -> int:       
        q, s = [-x for x in nums], sum(nums) / 2
        heapq.heapify(q)        
        for i in range(len(nums)):
            x = heapq.heappop(q) / 2
            heapq.heappush(q, x)
            s += x
            if s <= 0: return i + 1
class Solution {
    public int halveArray(int[] nums) {
        double sum = 0;
        PriorityQueue<Double> q = new PriorityQueue<>((o1,o2) -> Double.compare(o2, o1));
        for (int num : nums) {
            sum += num;
            q.add((double)num);
        }
        sum /= 2;
        for (int i = 1; i <= nums.length; i++){
            double tmp = q.poll() / 2;
            sum -= tmp;
            q.add(tmp);
            if (sum <= 0) return i;
        }
        return 0;
    }
}

895. 最大频率栈

# class FreqStack:
#     def __init__(self):
#         self.d = Counter()
#         self.q = []  # 每个元素都是一个栈

#     def push(self, val: int) -> None:
#         if self.d[val] == len(self.q):  # 这个元素的频率已经是目前最多的
#             self.q.append([val])  # 那么必须创建一个新栈
#         else:
#             self.q[self.d[val]].append(val)  # 否则就压入对应的栈
#         self.d[val] += 1  # 更新频率

#     def pop(self) -> int:
#         val = self.q[-1].pop()  # 弹出最右侧栈的栈顶
#         if len(self.q[-1]) == 0:  # 栈为空
#             self.q.pop()  # 删除
#         self.d[val] -= 1  # 更新频率
#         return val

class FreqStack:

    def __init__(self):
        self.d = defaultdict(int)
        self.q = []
        self.ts = 0

    def push(self, val: int) -> None:
        self.ts += 1
        self.d[val] += 1
        heappush(self.q, (-self.d[val], -self.ts, val))

    def pop(self) -> int:
        val = heappop(self.q)[2]
        self.d[val] -= 1
        return val
class FreqStack {
    Map<Integer, Integer> map = new HashMap<>();
    PriorityQueue<int[]> q = new PriorityQueue<int[]>((a, b) -> a[0] == b[0] ? b[1] - a[1] : b[0] - a[0]);
    int time = 0;
    
    public void push(int val) {
        map.merge(val, 1, Integer::sum);
        // 个数,时间戳,元素
        q.add(new int[]{map.get(val), time++, val});
    }
    
    public int pop() {
        int val = q.poll()[2];
        map.put(val, map.get(val) - 1);
        return val;
    }
}

class FreqStack {
    Map<Integer, Integer> d = new HashMap<>();
    // 栈记录频率相同元素
    List<Deque<Integer>> q = new ArrayList<>();
    
    public void push(int val) {
        d.merge(val, 1, Integer::sum);  
        int i = d.get(val) - 1;
        if (i == q.size()) // 这个元素的频率已经是目前最多的
            q.add(new ArrayDeque<>()); // 那么必须创建一个新栈
        q.get(i).push(val);  // 下标 i     
    }

    public int pop() {
        int i = q.size() - 1;
        int val = q.get(i).pop(); // 弹出最右侧栈的栈顶
        if (q.get(i).isEmpty()) // 栈为空
            q.remove(i); // 删除
        d.put(val, d.get(val) - 1); // 更新频率
        return val;
    }
}

★1642. 可以到达的最远建筑

class Solution:
    def furthestBuilding(self, heights: List[int], bricks: int, ladders: int) -> int:
        n, q = len(heights), []
        for i in range(1, n):
            delta = heights[i] - heights[i - 1]
            if delta <= 0: continue
            heappush(q, delta)
            # 如果优先队列已满,需要拿出一个其中的最小值,改为使用砖块
            if len(q) > ladders:
                bricks -= heappop(q)
                if bricks < 0: return i - 1
        return n - 1
class Solution {
    public int furthestBuilding(int[] heights, int bricks, int ladders) {
        int n = heights.length;
        PriorityQueue<Integer> q = new PriorityQueue();
        for(int i = 1; i < n; i++){
            int delta = heights[i] - heights[i - 1];            
            if(delta <= 0) continue;
            q.offer(delta);
            if(q.size() > ladders){
                bricks -= q.poll();
                if(bricks < 0) return i - 1;               
            } 
        }
        return n - 1;
    }
}

2336. 无限集中的最小数字

class SmallestInfiniteSet {
    Set<Integer> set = new HashSet();
    PriorityQueue<Integer> pq = new PriorityQueue<Integer>();
    int n = 1; // n 为非离散区间的起始位置
    public SmallestInfiniteSet() { }
    
    public int popSmallest() {
        if (set.isEmpty()) return n++;
        int res = pq.poll();
        set.remove(res);
        return res;
    }
    
    public void addBack(int num) {
        if (num < n && set.add(num)) pq.add(num);
    }
}

★2406. 将区间分为最少组数

class Solution:
    def minGroups(self, intervals: List[List[int]]) -> int:
        # 差分
        # d = defaultdict(int)
        # for l, r in intervals:
        #     d[l] += 1
        #     d[r + 1] -= 1
        # a = sorted(d.items())
        # ans = acc = 0
        # for _, v in a:
        #     acc += v
        #     ans = max(ans, acc)        
        # return ans

        # 最小堆
        intervals.sort()
        q = [] # 右边界
        for a, b in intervals:
            # 说明能接入后面,更新 最小值。
            if q and q[0] < a: heappop(q)
            heappush(q, b)        
        return len(q)

1094. 拼车

class Solution:
    def carPooling(self, trips: List[List[int]], capacity: int) -> bool:
        # 优先队列
        q = []
        trips.sort(key=lambda x : x[1])
        for x, f, t in trips:
            while q and q[0][0] <= f:               
                capacity += heappop(q)[1]
            capacity -= x
            if capacity < 0: return False
            heappush(q, [t, x])
        return True

        # 差分
        q = [0] * 1005
        for x, f, t in trips:
            q[f] += x
            q[t] -= x
        acc = 0
        for x in q:
            acc += x
            if acc > capacity: return False        
        return True
class Solution {
    public boolean carPooling(int[][] trips, int capacity) {
        // 优先队列 创建一个以下车顺序的小根堆
        PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> a[2] - b[2]);
        // 对上车顺序排序
        Arrays.sort(trips, Comparator.comparingInt(o -> o[1]));
        for(int[] t : trips){           
            capacity -= t[0]; // 上车                       
            while(!q.isEmpty() && q.peek()[2] <= t[1]) capacity += q.poll()[0]; // 下车
            if(capacity < 0) return false;
            q.offer(t);
        }
        return true;

        // 差分
        int[] q = new int[1001];
        for(int[] t:trips){
            q[t[1]] += t[0];
            q[t[2]] -= t[0];
        }
        int acc = 0;
        for(int x : q){
            acc += x;
            if(acc > capacity) return false;
        }
        return true;
    }
}

★871. 最低加油次数

class Solution:
    def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
        q, ans = [], 0
        # 方法一:先加油,后储备。
        stations.append((target, 0)) # 哨兵
        # 边走边把加油站一带上,可笑吧!
        for dis, oil in stations:
            # 油不够到当前站,前面需要加油
            while q and startFuel < dis: # startFuel 是累加的油量
                startFuel -= heappop(q)
                ans += 1
            if startFuel < dis: return -1
            heappush(q, -oil)
        return ans
        
        # 方法二:先储备,后加油。
        i = 0
        while startFuel < target: # 不到目标地一直循环
            # 遍历油站,当前油量能够到达 i 油站,储备油量。
            # 油够一直走,一直储备,直到油不够了。
            while i < n and stations[i][0] <= startFuel: 
                heappush(q, -stations[i][1])
                i += 1
            if not q: return -1
            startFuel -= heappop(q) # 开始加油
            ans += 1
        return ans
class Solution {
    public int minRefuelStops(int target, int startFuel, int[][] stations) {
        Queue<Integer> pq = new PriorityQueue<>(Collections.reverseOrder());
        int res = 0, n = stations.length;
        for (i = 0; startFuel < target; res++) {
            while (i < n && stations[i][0] <= startFuel)
                pq.offer(stations[i++][1]);
            if (pq.isEmpty()) return -1;
            startFuel += pq.poll();
        }
        return res;
    }
}

1801. 积压订单中的订单总数

class Solution:
    def getNumberOfBacklogOrders(self, orders: List[List[int]]) -> int:       
        buy, sell = [], [] # q 出单
        for p, a, t in orders:
            if not t: # 买单
                q, order = sell, buy
            else: # 卖单
                q, order = buy, sell             
           
            while a and q and (-q[0][0] >= p if t else q[0][0] <= p):
                cur = heappop(q)
                if cur[1] > a:
                    heappush(q, (cur[0], cur[1] - a))
                    a = 0
                else:
                    a -= cur[1]
            if a: # 入库
                heappush(order, (p if t else -p, a))
        ans, mod = 0, int(1e9)+7
        for _, a in q + order:
            ans += a
        return ans % mod
class Solution {
    public int getNumberOfBacklogOrders(int[][] orders) {
        PriorityQueue<int[]> buy = new PriorityQueue<int[]>((a, b) -> b[0] - a[0]);
        PriorityQueue<int[]> sell = new PriorityQueue<int[]>((a, b) -> a[0] - b[0]);
        for(int[] order : orders){
            int p = order[0], a = order[1], t = order[2];
            if(t == 0){ // 买单
                while(a > 0 && !sell.isEmpty() && p >= sell.peek()[0]){
                    int[] tmp = sell.poll();
                    if(tmp[1] > a){
                        sell.offer(new int[]{tmp[0], tmp[1] - a});
                        a = 0;                       
                    } else a -= tmp[1];
                } 
                if(a > 0) buy.offer(new int[]{p, a});
            } else { // 卖单
                while(a > 0 && !buy.isEmpty() && p <= buy.peek()[0]){
                    int[] tmp = buy.poll();
                    if(tmp[1] > a){
                        buy.offer(new int[]{tmp[0], tmp[1] - a});
                        a = 0;
                    } else a -= tmp[1];
                } 
                if(a > 0) sell.offer(new int[]{p, a});
            }
        }
        long ans = 0L;
        for(int[] order : buy) ans += order[1];
        for(int[] order : sell) ans += order[1];
        return (int)(ans % 1000000007);
    }
}

1942. 最小未被占据椅子的编号

class Solution:
    def smallestChair(self, times: List[List[int]], targetFriend: int) -> int:
        n = len(times)
        # 按每个人的开始排序,值为 person
        index = sorted(range(n), key=lambda i :(times[i][0], -times[i][1]))
        seat = [] # 空座位
        q = [] # (离开时间,位置)
        k = 0 # 当前位置
        for person in index:
            start, end = times[person]           
            while q and start >= q[0][0]:
                heappush(seat, heappop(q)[1])
            if seat: # 最小的
                pos = heappop(seat)
            else:
                pos = k
                k += 1   
            if targetFriend == person: return pos
            heappush(q, [end, pos])
class Solution {
    public int smallestChair(int[][] times, int targetFriend) {
        int n = times.length;
        Integer[] index = new Integer[n]; // 按每个人的开始排序,值为 person
        for(int i = 0; i < n; i++) index[i] = i;
        Arrays.sort(index, (a, b) -> times[a][0] - times[b][0]);
        PriorityQueue<Integer> seat = new PriorityQueue(); // 空座位
        PriorityQueue<int[]> q = new PriorityQueue<int[]>((a, b) -> a[0] - b[0]); // (离开时间,位置)
        int k = 0, pos; // 当前位置
        for(int person : index){
            int start = times[person][0], end = times[person][1];
            while(!q.isEmpty() && start >= q.peek()[0]){
                seat.offer(q.poll()[1]);
            }
            if(seat.isEmpty()) pos = k++;
            else pos = seat.poll(); // 最小的
            if(targetFriend == person) return pos;
            q.offer(new int[]{end, pos});
        }
        return -1;
    }
}

264. 丑数 II

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        factors = [2, 3, 5]
        seen = {1}
        q = [1]
        for i in range(n - 1):
            cur = heappop(q)
            for factor in factors:
                if (nxt := cur * factor) not in seen:
                    seen.add(nxt)
                    heappush(q, nxt)
        return heappop(q)

        a = b = c = 0
        dp = [1] * n        
        for i in range(1, n):
            dp[i] = min(dp[a] * 2, dp[b] * 3, dp[c] * 5)    
            if dp[i] == dp[a] * 2: a += 1
            if dp[i] == dp[b] * 3: b += 1
            if dp[i] == dp[c] * 5: c += 1 
        return dp[-1]
class Solution {
    public int nthUglyNumber(int n) {
        // PriorityQueue<Long> q = new PriorityQueue();
        // q.offer(1L);
        // int[] fact = {2, 3, 5};
        // Set<Long> vis = new HashSet();
        // for(int i = 1; i < n; i++){
        //     long cur = q.poll();           
        //     for(int f: fact){
        //         long next = f * cur;
        //         if(!vis.contains(next)){
        //             vis.add(next);
        //             q.offer(next);
        //         }
        //     }
        // }
        // return (int)(long)q.peek();
        
        int[] dp = new int[n];
        Arrays.fill(dp, 1);
        int a = 0, b = 0, c = 0;
        for(int i = 1; i < n; i++){
            dp[i] = Math.min(dp[a] * 2, Math.min(dp[b] * 3, dp[c] * 5));
            if(dp[i] == dp[a] * 2) a++;
            if(dp[i] == dp[b] * 3) b++;
            if(dp[i] == dp[c] * 5) c++;
        }
        return dp[n - 1];
    }
}

239. 滑动窗口最大值

heapq 库中的堆默认是最小堆

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: 
    	## 方法一:优先队列 / 最大堆
        q = [(-nums[i], i) for i in range(k)]
        heapq.heapify(q)
        res = [-q[0][0]]
        for i in range(k, len(nums)):
            heapq.heappush(q, (-nums[i], i))
            while q[0][1] <= i - k: heapq.heappop(q)
            res.append(-q[0][0])
        return res

    	## 方法二:单调队列
        q = deque()
        ans = []
        for i, x in enumerate(nums):
            while q and x >= nums[q[-1]]: q.pop()
            q.append(i)
            if i < k - 1: continue
            while q and q[0] <= i - k: q.popleft()
            ans.append(nums[q[0]])              
        return ans
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        // PriorityQueue<int[]> q = new PriorityQueue<int[]>((a, b) -> b[0] - a[0]);
        // int n = nums.length;
        // int[] ans = new int[n - k + 1];        
        // for(int i = 0; i < n; i++){
        //     q.offer(new int[]{nums[i], i});
        //     if(i < k - 1) continue;
        //     ans[i - k + 1] = q.peek()[0];
        //     while(!q.isEmpty() && q.peek()[1] <= i - k + 1) q.poll();            
        // }
        // return ans;                

        int n = nums.length, left = 0;
        int[] ans = new int[n - k + 1];
        Deque<Integer> q = new ArrayDeque<>();
        for(int i = 0; i < n; i++){
            int x = nums[i];

            // while(!q.isEmpty() && x > q.peekLast()) q.pollLast();
            // q.add(x);
            // if(i < k - 1) continue;
            // ans[left] = q.peek(); // left = i - k + 1
            // if(nums[left++] == q.peek()) q.poll(); // 按值比较

            while (!q.isEmpty() && nums[i] >= nums[q.peekLast()]) q.pollLast();
            q.add(i);
            if(i < k - 1) continue;
            ans[left] = nums[q.peek()];
            if(!q.isEmpty() && q.peek() <= left++) q.poll();
        }
        return ans;
    }
}

373. 查找和最小的K对数字

class Solution:
    def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
        m, n, res = len(nums1), len(nums2), []
        q = [(nums1[i] + nums2[0], i, 0) for i in range(min(k, m))] # 和只是用来排序的
        while q and len(res) < k:
            _, i, j = heappop(q)
            res.append([nums1[i], nums2[j]])
            if j + 1 < n:
                heappush(q, (nums1[i] + nums2[j + 1], i, j + 1))
        return res
class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<int[]> q = new PriorityQueue<>(k, (a, b)-> nums1[a[0]] + nums2[a[1]] - nums1[b[0]] - nums2[b[1]]);
        List<List<Integer>> ans = new ArrayList<>();
        int m = nums1.length, n = nums2.length;
        for(int i = 0; i < Math.min(m, k); i++){
            q.offer(new int[]{i, 0});
        }
        while(k-- > 0 && !q.isEmpty()){
            int i = q.peek()[0], j = q.poll()[1];            
            ans.add(List.of(nums1[i], nums2[j]));
            if (j + 1 < n) {
                q.offer(new int[]{i, j + 1});
            }
        }        
        return ans;
    }
}

1696. 跳跃游戏 VI

class Solution:
    def maxResult(self, nums: List[int], k: int) -> int:
        '''
        # 1:最大堆 
        n = len(nums)
        q = [] # dp
        heappush(q, (-nums[0], 0))
        res = nums[0]
        for i in range(1, n):
            while q and i - q[0][1] > k: heappop(q) # 清理
            res = -q[0][0] + nums[i]
            heappush(q, (-res, i))         
        return res
        '''
        # 2:单调队列
        n = len(nums)
        q = deque([(nums[0], 0)]) # 和,下标
        res = nums[0]
        for i in range(1, n):
            # 过滤,维护窗口
            while q and i - q[0][1] > k: q.popleft()
            res = q[0][0] + nums[i]   
            # 单调栈,维护最大值 
            while q and res >= q[-1][0]: q.pop()
            q.append((res, i))         
        return res
class Solution {
    public int maxResult(int[] nums, int k) {
        PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> b[0] - a[0]);
        q.offer(new int[]{nums[0], 0});
        int n = nums.length, ans = nums[0];
        for(int i = 1; i < n; i++){
            while(!q.isEmpty() && i - q.peek()[1] > k) q.poll();            
            ans = q.peek()[0] + nums[i];
            q.offer(new int[]{ans, i});
        }
        return ans;

        Deque<int[]> q = new ArrayDeque<>();
        q.add(new int[]{nums[0], 0});
        int n = nums.length, ans = nums[0];
        for(int i = 1; i < n; i++){
            while(!q.isEmpty() && i - q.peek()[1] > k) q.poll();
            ans = q.peek()[0] + nums[i];
            while(!q.isEmpty() && ans >= q.peekLast()[0]) q.pollLast();
            q.add(new int[]{ans, i});
        }
        return ans;
    }
}

1383. 最大的团队表现值

class Solution:
    def maxPerformance(self, n: int, speed: List[int], efficiency: List[int], k: int) -> int:
        # 「效率值」中的最小值的贡献值
        idx = sorted(range(n), key=lambda i : -efficiency[i])
        q = []
        ans = sum_ = 0
        for i, j in enumerate(idx):
            s, e = speed[j], efficiency[j]
            sum_ += s
            heappush(q, s)
            ans = max(ans, sum_ * e)
            if i > k - 2: sum_ -= heappop(q)
        return ans % 1000000007
class Solution {
    public int maxPerformance(int n, int[] speed, int[] efficiency, int k) {
        Integer[] idx = new Integer[n]; // 离线排序
        for(int i = 0; i < n; i++) idx[i] = i;
        Arrays.sort(idx, (a, b) -> efficiency[b] - efficiency[a]);
        int mod = 1000000007;
        long  ans = 0L, sum = 0L;
        PriorityQueue<Integer> q = new PriorityQueue();
        // 每一个 效率值 作为最小值的贡献值
        for(int i = 0; i < n; i++){
            int s = speed[idx[i]], e = efficiency[idx[i]];
            sum += s;
            q.offer(s);
            ans = Math.max(ans, sum * e);
            if(i > k - 2){ // k 个数后减一,保留 k - 1 个最大值。
                sum -= q.poll();
            }
        }
        return (int)(ans % mod);
    }
}

★1705. 吃苹果的最大数目

贪心 + 优先队列(堆)
「优先吃掉最快过期的苹果」会是最优,「小根堆」维护苹果过期的过程。

class Solution:
    def eatenApples(self, apples: List[int], days: List[int]) -> int:
        q, ans, n = [], 0, len(days)
        for i, (d, a) in enumerate(zip(days, apples)):
            # 1、入库
            heappush(q, [i + d, a]) # 有效期从第一天算起
            # 2、清库
            while q and (q[0][0] <= i or q[0][1] == 0): heappop(q)
            # 3、吃,快腐烂的
            if q: 
                q[0][1] -= 1 
                ans += 1
        # 4、吃库存
        while q:
            d, a = heappop(q)
            x = min(max(d - n, 0), a)
            ans += x
            n += x            
        return ans
class Solution {
    public int eatenApples(int[] apples, int[] days) {
        int ans = 0, n = days.length;
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
        // 方法一:n 天内和以后分开处理
        for(int i = 0; i < n; i++){
            // 1、入库
            pq.add(new int[]{i + days[i], apples[i]});
            // 2、清库
            while(!pq.isEmpty() && (pq.peek()[0] <= i || pq.peek()[1] == 0)) pq.poll();
            // 3、先吃快过期的苹果
            if(!pq.isEmpty()){
                pq.peek()[1]--;
                ans++;
            }
        }
        // 4、吃库存,从第 n 天开始
        while(!pq.isEmpty()){
            int[] arr = pq.poll();
            int cur= Math.min(Math.max(arr[0] - n, 0), arr[1]); // 最多能吃的天数
            ans += cur;
            n += cur;
        }

		// 方法二:n 天内和以后合并处理
        // int i = 0;
        // while (i < n || !q.isEmpty()) {
        //     // 处理过期的和吃完的,下架过期商品 
        //     while (!q.isEmpty() && (q.peek()[0] <= i || q.peek()[1] == 0)) q.poll();
        //     // 入库 注意与清库的顺序,后入库就不能入 0
        //     if (i < n && apples[i] > 0) q.offer(new int[]{i + days[i], apples[i]});
        //     // 吃快过期的,
        //     if (!q.isEmpty()) { q.peek()[1]--; ans++; } // 放心吃,不用考虑过期的问题
        //     i++;
        // }
        return ans;
    }
}

★1353. 最多可以参加的会议数目

遍历每一天,把当天开始的会议的结束时间加入优先队列(维护持续时间),删除过期的会议。参加顶上的会议(即将结束的会议)。

class Solution:
    def maxEvents(self, events: List[List[int]]) -> int:
        ans, q, n, i, day = 0, [], len(events), 0, 1
        events.sort()
        while i < n or q: # i 是 events 索引 遍历列表,延续的会议      
            # 登记:当天开始的会议
            while i < n and events[i][0] == day:
                heappush(q, events[i][1]) # 当天所开会议的结束时间
                i += 1
            # 清理:删除开完的会议
            while q and q[0] < day:
                heappop(q)
            # 参加:快结束的会议
            if q: 
                heappop(q)
                ans += 1
            day += 1
        return ans
class Solution {
    public int maxEvents(int[][] events) {       
        Arrays.sort(events, (a, b) -> a[0] - b[0]);       
        PriorityQueue<Integer> q = new PriorityQueue<>();
        int res = 0, day = 1, index = 0, n = events.length;
        while (index < n || !q.isEmpty()) {
            // 当天开始的会议
            while (index < n && events[index][0] == day) q.offer(events[index++][1]);          
            // 已经结束的会议
            while (!q.isEmpty() && q.peek() < day) q.poll()
            // 参加会议
            if (!q.isEmpty()) { q.poll(); res++; }
            day++;
        }
        return res;
    }
}

857. 雇佣 K 名工人的最低成本

class Solution:
    def mincostToHireWorkers(self, quality: List[int], wage: List[int], k: int) -> float:
        # △性价比从高到低排列,优先使用性价比高的,但是价钱由性价比低的那个人决定。
        workers = sorted((w / q, q) for q, w in zip(quality, wage))       
        # 大根堆:维护质量最低的 K 个工人
        pool = [-q for _, q in workers[:k-1]] 
        heapify(pool)
        sumq = -sum(pool)
        ans = inf
        # 枚举剩余的每一个工人
        for ratio, q in workers[k-1:]:
            # 压入当前工人
            heappush(pool, -q)
            sumq += q
            ans = min(ans, sumq * ratio)
            # 弹出质量最高的工人
            quMax = heappop(pool)
            # 更新 k 个质量最低工人的质量和
            sumq += quMax            
        return ans

1425. 带限制的子序列和

class Solution:
    def constrainedSubsetSum(self, nums: List[int], k: int) -> int:
        res = nums[0];
        q = [(-nums[0], 0)]
        n = len(nums)
        for i in range(1, n):
            while q[0][1] < i - k:
                heappop(q)
            temp = max(-q[0][0], 0) + nums[i]
            heappush(q, (-temp, i))
            res = max(temp, res)        
        return res
    
        # f 以 nums[i] 结尾满足条件的子序列最大和
        n = len(nums)
        x = nums[0]
        f = [0] * n
        f[0] = x
        q = deque([0])        
        ans = x
        for i in range(1, n):
            x = nums[i]
            # 状态转移方程
            f[i] = max(f[q[0]], 0) + x           
            ans = max(ans, f[i])
            # 维护最大值
            while q and f[i] >= f[q[-1]]:
                q.pop()
            q.append(i)
            # 维护窗口,多删一个
            while q and i - q[0] >= k:
                q.popleft()
        return ans
class Solution {
    public int constrainedSubsetSum(int[] nums, int k) {
    	// 双端队列
        Deque<Integer> q = new ArrayDeque<>();
        q.add(0);
        int n = nums.length, num = nums[0], ans = num;
        int[] f = new int[n];
        f[0] = num;
        for(int i = 1; i < n; i++){
            f[i] = Math.max(f[q.peek()], 0) + nums[i];
            ans = Math.max(ans, f[i]);
            while(!q.isEmpty() && f[i] > f[q.peekLast()]) q.pollLast();
            q.add(i);
            while(!q.isEmpty() && i - q.peek() >= k) q.poll();
        }
        return ans;
		
		// 优先队列
        int len = nums.length;
        int[] dp = new int[len];
        PriorityQueue<int[]> queue = new PriorityQueue<>((o1, o2) -> o2[1] - o1[1]);
        queue.offer(new int[]{0, nums[0]});
        int max = nums[0];
        for (int i = 1; i < len; i++) {
            while (i - queue.peek()[0] > k) {
                queue.poll();
            }
            int tmp = Math.max(queue.peek()[1], 0) + nums[i];
            max = Math.max(max, tmp);
            queue.offer(new int[]{i, tmp});
        }
        return max;
    }
}

630. 课程表 III

class Solution:
    def scheduleCourse(self, courses: List[List[int]]) -> int:
        courses.sort(key=lambda c:c[1])
        q, total = [], 0
        for t, d in courses:
            if total + t <= d:
                total += t        
                heapq.heappush(q, -t)
            elif q and -q[0] > t:
                total += t + q[0]
                heapq.heappop(q)
                heapq.heappush(q, -t)
                # heapq.heappushpop(q, -t)
        return len(q)
class Solution {
    public int scheduleCourse(int[][] courses) {
        Arrays.sort(courses, (a, b) -> a[1] - b[1]);
        PriorityQueue<Integer> q = new PriorityQueue<Integer>((a, b) -> b - a);
        int total = 0;
        for (int[] c: courses){
            int t = c[0], d = c[1];
            if (total + t <= d){
                total += t;
                q.offer(t);
            } else if (!q.isEmpty() && q.peek() > t){
                total += t - q.poll();
                q.offer(t);
            }
        }
        return q.size();
    }
}

218. 天际线问题

class Solution:
    def getSkyline(self, buildings: List[List[int]]) -> List[List[int]]:         
        q, bd, ans = [], [], []
        n, i = len(buildings), 0
        for b in buildings: # 加入左右边缘
            bd.extend(b[:2])
        bd.sort()        
        for b in bd:
            # 左边缘 <= b,[-右边缘, 高度] 加入最大堆
            while i < n and buildings[i][0] <= b:
                heappush(q, [-buildings[i][2], buildings[i][1]])
                i += 1
            # 右边缘 <= b 弹出
            while q and q[0][1] <= b: heappop(q)
            maxh = -q[0][0] if q else 0
            if not ans or  maxh != ans[-1][1]:
                ans.append([b,  maxh])   
        return ans
class Solution {
    public List<List<Integer>> getSkyline(int[][] buildings) {
        int n = buildings.length, idx = 0;
        List<List<Integer>> ans = new ArrayList<>();
        PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> b[1] - a[1]);
        Set<Integer> treeSet = new TreeSet<>();        
        for(int[] b : buildings){
            treeSet.add(b[0]);
            treeSet.add(b[1]);
        }
        for(int b : treeSet){
            while(idx < n && buildings[idx][0] <= b){
                q.offer(new int[]{buildings[idx][1], buildings[idx][2]});
                idx++;
            }
            while(!q.isEmpty() && q.peek()[0] <= b){
                q.poll();
            }
            int max = q.isEmpty() ? 0 : q.peek()[1];
            int len = ans.size();
            if(len == 0 || ans.get(len - 1).get(1) != max){
                ans.add(List.of(b, max));
            }
        }
        return ans;
    }
}

2386. 找出数组的第 K 大和

class Solution:
    def kSum(self, nums: List[int], k: int) -> int:
        # 记 nums 中所有非负数的和为 s。任意一个子序列的和,都等价于从 s 中减去某些非负数 / 加上某些负数得到。!!!
        s = 0
        for i, x in enumerate(nums):
            if x > 0: s += x
            else: nums[i] = -x

        nums.sort()
        h = [(-s, 0)] 
        for _ in range(k):
            s, i = heappop(h)
            if i < len(nums):
                heappush(h, (s + nums[i], i + 1))  # 保留 nums[i-1]
                if i: heappush(h, (s + nums[i] - nums[i - 1], i + 1))  # 不保留 nums[i-1]
        return -s

1054. 距离相等的条形码

class Solution:
    def rearrangeBarcodes(self, barcodes: List[int]) -> List[int]:
        d, n = Counter(barcodes), len(barcodes)
        ans = [0] * n
        q = sorted(d.items(), key=lambda x:x[1])
        i = 0
        for k, v in reversed(q):
            for _ in range(v):
                ans[i] = k
                i += 2
                if i >= n: i = 1        
        return ans
class Solution {
    public int[] rearrangeBarcodes(int[] barcodes) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int b : barcodes) {
            map.put(b, map.getOrDefault(b, 0) + 1);
        }
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (b[1] - a[1]));
        for (int k : map.keySet()) {
            pq.add(new int[]{k, map.get(k)});
        }
        int[] res = new int[barcodes.length];
        int i = 0;
        while (!pq.isEmpty()) {
            int[] temp = pq.poll();
            while (temp[1] -- > 0) {
                res[i] = temp[0];
                i += 2;
                if (i >= barcodes.length) i = 1;
            }            
        }
        return res;
    }
}

632. 最小区间

class Solution:
    def smallestRange(self, nums: List[List[int]]) -> List[int]:
        a, b = -10**9, 10**9
        q = [(r[0], i, 0) for i, r in enumerate(nums)]
        max_ = max(x[0] for x in q)
        heapify(q)

        while True:
            min_, i, j = heappop(q)
            if max_ - min_ < b - a:
                a, b = min_, max_
            x = nums[i]
            if j == len(x) - 1: break            
            max_ = max(max_, x[j + 1])
            heappush(q, (x[j + 1], i, j + 1))        
        return [a, b]

        # 滑动窗口 + map
        n, left = len(nums), 0
        arr = [(x, i) for i, row in enumerate(nums) for x in row]
        arr.sort()
        d = defaultdict(int)
        a, b = arr[0][0], arr[-1][0]
        for x, i in arr:
            d[i] += 1
            if len(d) < n: continue
            y, j = arr[left]
            while d[j] > 1:
                d[j] -= 1
                left += 1
                y, j = arr[left]
            if b - a > x - y:
                a, b = y, x
        return (a, b)
class Solution {
    public int[] smallestRange(List<List<Integer>> nums) {
        int a = 0, b = Integer.MAX_VALUE, max = Integer.MIN_VALUE, n = nums.size();
        int[] next = new int[n];
        PriorityQueue<Integer> q = new PriorityQueue<>((i, j) -> nums.get(i).get(next[i]) - nums.get(j).get(next[j]));
        for (int i = 0; i < n; i++) {
            q.offer(i);
            max = Math.max(max, nums.get(i).get(0));
        }
        while (true) {
            int i = q.poll();
            var x = nums.get(i);
            int min = x.get(next[i]);
            if(max - min < b - a){
                a = min;
                b = max;
            }
            next[i]++;
            if(next[i] == x.size()) break;            
            q.offer(i);
            max = Math.max(max, x.get(next[i]));
        }
        return new int[]{a, b};

        // 滑动窗口
        int m = 0, n = nums.size();
        for(var x : nums) m += x.size();
        int[][] q = new int[m][2];       
        for(int i = 0, k = 0; i < n; i++){
            for(int y : nums.get(i)){
                q[k++] = new int[]{y, i};
            }
        }
        Arrays.sort(q, (a, b) -> a[0] - b[0]);
        Map<Integer, Integer> map = new HashMap<>();
        int b = Integer.MAX_VALUE, a = 0, left = 0;
        for(int[] z : q){
            int x = z[0], i = z[1];
            map.merge(i, 1, Integer::sum);
            if(map.size() < n) continue;            
            int y = q[left][0], j = q[left][1];
            while(map.get(j) > 1){
                map.put(j, map.get(j) - 1);
                left++;
                y = q[left][0];
                j = q[left][1];
            }                   
            if(x - y < b - a){
                a = y; b = x;
            }                             
        }
        return new int[]{a, b};
    }
}

659. 分割数组为连续子序列

方法一:哈希表 + 最小堆
由于需要将数组分割成一个或多个由连续整数组成的子序列,因此只要知道子序列的最后一个数字和子序列的长度,就能确定子序列。

遍历数组,当遍历到元素 x 时,如果存在一个以 x − 1 结尾子序列,长度为 k,则可以将 x 加入该子序列中,得到长度为 k + 1 的子序列。如果不存在以 x − 1 结尾的子序列,则新建一个只包含 x 的子序列,长度为 1。

如果存在多个以 x − 1 结尾子序列,应该将 x 加入其中的哪一个子序列?由于题目要求每个子序列的长度至少为 3,显然应该让最短的子序列尽可能长,因此应该将 x 加入其中最短的子序列。

可以使用哈希表和最小堆进行实现。
哈希表的键为子序列的最后一个数字,值为最小堆,用于存储所有的子序列长度,最小堆满足堆顶的元素是最小的,因此堆顶的元素即为最小的子序列长度。

由于数组是有序的,因此当遍历到元素 x 时,不会出现当前元素比之前的元素小的情况。

遍历结束之后,检查哈希表中存储的每个子序列的长度是否都不小于 3,即可判断是否可以完成分割。

class Solution:
    def isPossible(self, nums: List[int]) -> bool:
        d = defaultdict(list) # 连续子序列的最后一个数,值为连续子序列的长度。
        for x in nums:
            cur = 1
            if q := d[x - 1]:
                cur = heappop(q) + 1
            heappush(d[x], cur)        
        return not any(q[0] < 3 for q in d.values() if q)
        
        # 以前一数结尾,长度分别是 1, 2, 3(>= 3) 的子序列的个数。
        a = b = c = 0
        n, i = len(nums), 0
        while i < n:
            start, x = i, nums[i]
            i += 1
            while i < n and nums[i] == x: i += 1            
            cnt = i - start
            if start > 0 and x != nums[start - 1] + 1: # 接不上
                if a + b > 0: return False                
                a = b = c = 0 # 从新开始
            else: # 能接上
                # 数不够
                if a + b > cnt: return False
                # 剩余
                left = cnt - a - b
                keep = min(c, left) # 可保留着
                c = keep + b
                b = a
                a = left - keep # 需要新开
        return a == 0 and b == 0
        
        w = nums[-1] - nums[0] 
        d = [0] * (w + 1)
        for x in nums:
            d[x - nums[0]] += 1         
 
        a = b = c = 0
        for cnt in d:
            if not cnt: # 接不上
                if a + b > 0: return False
                # 从新开始
                a = b = c = 0
            else: # 能接上
                # 数不够
                if a + b > cnt: return False
                # 剩余
                left = cnt - a - b
                keep = min(c, left) # 可保留着
                c = keep + b
                b = a
                a = left - keep # 需要新开

        return a == 0 and b == 0
class Solution {
    public boolean isPossible(int[] nums) {
        Map<Integer, PriorityQueue<Integer>> map = new HashMap();
        for(int x : nums){
            int pre = 1;
            if (map.containsKey(x - 1)) {
                if(!map.get(x - 1).isEmpty()) pre = map.get(x - 1).poll() + 1;
                // if (map.get(x - 1).isEmpty()) {
                //     map.remove(x - 1);
                // }
            } 
            map.compute(x, (k, v) -> v == null ? new PriorityQueue<Integer>(): v).offer(pre);
        } 
        for(PriorityQueue<Integer> q:map.values()){
            if(!q.isEmpty() && q.peek() < 3) return false;
        }
        return true;
        
        int a = 0, b = 0, c = 0, i = 0, n = nums.length;
        while(i < n){
            int start = i, x = nums[i];
            while(i < n && x == nums[i]) i++;
            int cnt = i - start;
            if(start > 0 && x != nums[start - 1] + 1){
                if(a + b > 0) return false;
                a = 0; b = 0; c = 0;            
            } else {
                if(a + b > cnt) return false;
                int left = cnt - a - b;
                int keep = c < left ? c : left;
                c = keep + b;
                b = a;
                a = left - keep;
            }
        }
        return a == 0 && b == 0;
    }
}

1488. 避免洪水泛滥

from sortedcontainers import SortedList

class Solution:
    def avoidFlood(self, rains: List[int]) -> List[int]:
       
        d = defaultdict(deque)
        for i, r in enumerate(rains):
            if r: d[r].append(i)
        q = []  # 湖满下一次下雨排队
        full = set() 
        res = []
        for i, r in enumerate(rains):        
            if r: # 下雨天
                if r in full: return []               
                res.append(-1)
                full.add(r)
                d[r].popleft()
                if d[r]:
                    heappush(q, d[r][0])                       
            else: # 晴天
                if not q:
                    res.append(1)
                else:
                    j = heappop(q)   
                    x = rains[j]   
                    res.append(x)
                    full.remove(x)
        return res
        
        n = len(rains)
        ans = [-1] * n        
        sl = SortedList() # 晴天
        d = dict() # lake : day
        for i, x in enumerate(rains):
            if x == 0:
                sl.add(i)
                continue
            # 第 i 天 x 湖下雨, 以前第 d[x] 天下过雨,用掉雨天后最近的晴天抽水。
            if x in d: 
                j = sl.bisect_left(d[x])
                if not 0 <= j < len(sl):
                    return []
                ans[sl.pop(j)] = x
            d[x] = i # 更新下雨天
       
        for i in sl:
            ans[i] = 1
        return ans
class Solution {
    public int[] avoidFlood(int[] rains) {
        int n = rains.length;
        int max = Integer.MAX_VALUE;
        int[] ans = new int[n];
        Arrays.fill(ans, -1);
        // 记录湖的下一个下雨天
        int[] next = new int[n];
        Map<Integer, Integer> map = new HashMap();
       for(int i = n - 1; i >= 0; i--){
            int r = rains[i];
            if(r > 0){
                next[i] = map.getOrDefault(r, max);
                map.put(r, i);
            }
        }
        Set<Integer> full = new HashSet();
        // 下一个排队去
        PriorityQueue<Integer> q = new PriorityQueue();
        for(int i = 0; i < n; i++){
            int r = rains[i];
            if (r > 0) {
                if (!full.add(r)) {
                    return new int[0];
                }
                if (next[i] < n) {
                    q.offer(next[i]);
                }
            } else {
                if (q.isEmpty()) {
                    ans[i] = 1;
                } else {
                    int j = q.poll();
                    int x = rains[j];
                    ans[i] = x;
                    full.remove(x);
                }
            }
        }
        return ans;
		
		// 方法二:
        int n = rains.length;
        int[] ans = new int[n];
        Arrays.fill(ans, -1);
        Set<Integer> s = new HashSet();
        PriorityQueue<Integer> q = new PriorityQueue();
        Map<Integer, ArrayDeque<Integer>> map = new HashMap();
        for(int i = 0; i < n; i++){
            int r = rains[i];
            if(r == 0) continue;
            map.compute(r, (k, v) -> v == null ? new ArrayDeque<Integer>() : v).offer(i);
        }
        for(int i = 0; i < n; i++){
            int r = rains[i];
            if(r > 0){
                if(s.contains(r)) return new int[0];
                s.add(r);
                map.get(r).poll();
                ArrayDeque<Integer> tmp = map.get(r);
                if(!tmp.isEmpty()) q.offer(tmp.peek());                
            } else {
                if(q.isEmpty()) ans[i] = 1;
                else {
                    int j = q.poll();
                    int x = rains[j];
                    s.remove(x);
                    ans[i] = x;
                }
            }
       }       
        return ans;
        
		// 方法三:map
        int n = rains.length;
        int[] ans = new int[n];
        Map<Integer, Integer> map = new HashMap(); // day:lake
        
        Deque<Integer> q = new LinkedList<>(); // noRainIdxs
        for(int i = 0; i < n; i++){
            int lake = rains[i];
            if(lake == 0){
                q.add(i);
                continue;
            }
            if (map.containsKey(lake)) {
                if (q.isEmpty()) {
                    return new int[0];
                }
                int fullLakeIdx = map.get(lake);
                boolean done = false;
                for (int noRainIdx : q) {
                    if (noRainIdx > fullLakeIdx) {
                        ans[noRainIdx] = lake;
                        q.remove(noRainIdx);
                        done = true;
                        break;
                    }
                }
                if (!done) {
                    return new int[0];
                }
            }
            ans[i] = -1;
            map.put(lake, i);
        }
        while (!q.isEmpty()) {
            ans[q.pop()] = 1;
        }
        return ans;
    }
}

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

from sortedcontainers import SortedList

class Solution:
    def busiestServers(self, k: int, arrival: List[int], load: List[int]) -> List[int]:
        sl = SortedList(range(k)) # free
        q = [] # busy
        req = [0] * k
        for i, (s, t) in enumerate(zip(arrival, load)):
            while q and q[0][0] <= s:
                sl.add(heappop(q)[1])
            if (n := len(sl)) == 0: continue
            j = sl.bisect_left(i%k) % n
            id = sl[j]
            req[id] += 1
            heappush(q, (s + t, id))
            sl.remove(id)
        maxreq = max(req)
        return [i for i in range(k) if req[i] == maxreq]

1776. 车队 II

按照顺序计算每辆车是否能追上其前面的车,将追击结果(time, 后车, 前车)放入堆,然后在堆中依次取出每次追击结果并判断后面的车是否能追上合并的车队,若能追上则将新的追击结果放入堆,如此循环直到堆中没有追击结果为止。

class Solution:
    def getCollisionTimes(self, cars: List[List[int]]) -> List[float]:
        # 计算追赶时间
        def cal(car1, car2):
            if car1[1] <= car2[1]: return -1
            return (car2[0] - car1[0]) / (car1[1] - car2[1])

        q = []
        n = len(cars)
        ans = [-1] * n
        # mapping 记录每辆车连接的后车
        d = {i: i - 1 for i in range(n)}

        # 首先根据连续的顺序计算追击
        for i in range(n - 1):
            tmp = cal(cars[i], cars[i + 1])
            # tmp 不为 -1 则将追击的时间入堆
            if tmp != -1:
                heappush(q, (tmp, i, i + 1))

        while q:
            time, left, right = heappop(q)
            if ans[left] != -1: continue
            ans[left] = time
            d[right] = d[left] # 相当于这两辆车合到一起,前方车的后车变为原来后车的后车
            if d[left] == -1:continue
            # 计算新的追击关系
            new = d[left]
            tmp = cal(cars[new], cars[right])
            # tmp 不为 -1 则将追击的时间入堆
            if tmp != -1:
                heappush(q, (tmp, new, right))
        return ans

1882. 使用服务器处理任务

class Solution:
    def assignTasks(self, servers: List[int], tasks: List[int]) -> List[int]:
        run = [] # (time, i)
        idle = [(w, i) for i, w in enumerate(servers)]
        heapify(idle)
        ans = []
        time = 0  # 记录运行服务器的释放时间。
        for s, t in enumerate(tasks):
            while run and s >= run[0][0]:  # 已运行结束的服务器加入idle
                _, i = heappop(run)
                heappush(idle, (servers[i], i))
            if not idle:  # 没有空闲服务器,将最近运行完成的服务器加入idle
                time = run[0][0]  # 当发生任务延迟运行时,需要记录服务器的释放时间,从释放时间起运行任务
                while run and time == run[0][0]:
                    _, i = heappop(run)
                    heappush(idle, (servers[i], i))
            _, i = heappop(idle)
            heappush(run, (max(s, time) + t, i))  # 服务器变为正在运行
            ans.append(i)
        return ans

2233. K 次增加后的最大乘积

class Solution:
    def maximumProduct(self, nums: List[int], k: int) -> int:
        heapq.heapify(nums)
        for i in range(k):
            heapq.heapreplace(nums, nums[0] + 1)           
        ans = 1
        for i in nums:
            ans *= i
            ans %= 1000000007
        return ans
class Solution {
    public int maximumProduct(int[] nums, int k) {
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        for (int num : nums) pq.offer(num);
        for (int i = 0; i < k; i++) pq.offer(pq.poll() + 1); 
        long ans = 1;
        long MOD = 1000000007;
        for (int num : pq){
            ans *= num;
            ans %= MOD;
        }
        return (int)ans;
    }
}

2349. 设计数字容器系统

class NumberContainers:

    def __init__(self):       
        self.h  = defaultdict(list)
        self.q = {}

    def change(self, i: int, x: int) -> None:
        self.q[i] = x
        heappush(self.h[x], i)

    def find(self, x: int) -> int:
        while self.h[x]:
            i = heappop(self.h[x])
            if self.q[i] == x:
                heappush(self.h[x], i)
                return i
        return -1

2353. 设计食物评分系统

class FoodRatings:

    def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):
        self.d = {}
        self.dc = defaultdict(list)
        for f, c, r in zip(foods, cuisines, ratings):
            self.d[f] = [r, c]
            heappush(self.dc[c], (-r, f))

    def changeRating(self, food: str, newRating: int) -> None:
        self.d[food][0] = newRating
        heappush(self.dc[self.d[food][1]], (-newRating, food)) 

    def highestRated(self, cuisine: str) -> str:
        while self.d[self.dc[cuisine][0][1]][0] != -self.dc[cuisine][0][0]:
            heappop(self.dc[cuisine])
        return self.dc[cuisine][0][1]
        
from sortedcontainers import SortedList
class FoodRatings:

    def __init__(self, foods: List[str], cuisines: List[str], ratings: List[int]):
        self.d = defaultdict(tuple)
        self.h = defaultdict(SortedList)
        for f, c, r in zip(foods, cuisines, ratings):
            self.d[f] = (r, c)
            self.h[c].add((-r, f))

    def changeRating(self, food: str, newRating: int) -> None:
        r, c = self.d[food]
        self.h[c].remove((-r, food))
        self.h[c].add((-newRating, food))
        self.d[food] = (newRating, c)

    def highestRated(self, cuisine: str) -> str:
        return self.h[cuisine][0][1]

2402. 会议室 III

class Solution:
    def mostBooked(self, n: int, meetings: List[List[int]]) -> int:
        room = [0] * n # n 个会议室
        meetings.sort()
        q = [] # 开会或排队中 (end, i)
        tmp = list(range(n)) # 空闲
        heapify(tmp)
        for s, e in meetings:
            # 会议已经结束,会议室空闲
            while q and s >= q[0][0]:
                _, i = heappop(q)
                heappush(tmp, i)
            # 编号最小的空闲的会议室开会
            if tmp: i = heappop(tmp)                       
            else:
                # 没有空闲会议室,延迟,排队中
                t, i = heappop(q)
                e += t - s
            room[i] += 1
            heappush(q, (e, i))
        # 开会最多的会议室
        maxcnt = 0
        ans = 0
        for i, x in enumerate(room):
            if x > maxcnt:
                maxcnt = x
                ans = i
        return ans

2462. 雇佣 K 位工人的总代价

class Solution:
    def totalCost(self, costs: List[int], k: int, candidates: int) -> int:
        ans, n = 0, len(costs)
        if candidates * 2 < n:
            pre = costs[:candidates]
            heapify(pre)
            suf = costs[-candidates:]
            heapify(suf)
            i, j = candidates, n - candidates - 1
            while k and i <= j:
                if pre[0] <= suf[0]:
                    ans += heapreplace(pre, costs[i])
                    i += 1
                else:
                    ans += heapreplace(suf, costs[j])
                    j -= 1
                k -= 1
            costs = pre + suf
        costs.sort()
        return ans + sum(costs[:k])  # 也可以用快速选择算法求前 k 小

1687. 从仓库到码头运输箱子

class Solution:
    def boxDelivering(self, boxes: List[List[int]], portsCount: int, maxBoxes: int, maxWeight: int) -> int:
        n = len(boxes)
        left = 0
        w = boxes[0][1]
        count = 2
        dp = [0] * (n + 1)
        dp[1] = 2
        for i in range(1, n):
            if boxes[i][0] != boxes[i - 1][0]: count += 1
            w += boxes[i][1]
            # 滑动窗口
            while i - left + 1 > maxBoxes or w > maxWeight or dp[left + 1] == dp[left]:
                w -= boxes[left][1] # 收缩左边界
                left += 1
                if boxes[left - 1][0] != boxes[left][0]: count -= 1            
            dp[i + 1] = dp[left] + count
        
        return dp[n]
class Solution {
    public int boxDelivering(int[][] boxes, int portsCount, int maxBoxes, int maxWeight) {
        int n = boxes.length, j = 0, count = 2, w = boxes[0][1];
        int[] f = new int[n + 1];
        f[1] = 2;

        for (int i = 1; i < n; i++) {
            if (boxes[i][0] != boxes[i-1][0]) { count++; }
            w += boxes[i][1];
            while (i - j + 1 > maxBoxes || w > maxWeight || f[j] == f[j+1]) {
                w -= boxes[j++][1];
                if (boxes[j][0] != boxes[j-1][0]) count--;  
            }
            f[i+1] = f[j] + count;
        }        
        return f[n];
    }
}
class Solution {
    public int boxDelivering(int[][] boxes, int portsCount, int maxBoxes, int maxWeight) {
        int n = boxes.length, dif = 0, pre = 0;
        int[] f = new int[n + 1];
        f[1] = 2;
        Deque<int[]> q = new ArrayDeque<>();
        q.add(new int[]{0, 0, 0}); // f[j] - dif[j + 1], j, pre
        for(int i = 0; i < n; i++){
            int p = boxes[i][0], w = boxes[i][1];
            pre += w;
            dif += i == 0 ? 0 : p != boxes[i - 1][0] ? 1 : 0;
            while(!q.isEmpty() && (pre - q.peek()[2] > maxWeight || i + 1 - q.peek()[1] > maxBoxes)) q.poll();
            f[i + 1] = dif + q.peek()[0] + 2;
            if(i < n - 1){
                int d = f[i + 1] - (dif + (p != boxes[i + 1][0] ? 1 : 0));
                while(!q.isEmpty() && q.peekLast()[0] >= d) q.pollLast();
                q.add(new int[]{d, i + 1, pre});
            }
        }      
        return f[n];
    }
}

407. 接雨水 II

480. 滑动窗口中位数

class Solution:
    def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
        # # 方法一:
        # res = []
        # for i in range(len(nums) - k + 1):
        #     w = sorted(nums[i:i + k]) # 需要优化              
        #     res.append((w[(k - 1) // 2] + w[k // 2]) / 2)
        # return res

        # # 方法二:双堆对顶
        # dh = DualHeap(k)
        # for num in nums[:k]:
        #     dh.insert(num)
        
        # ans = [dh.getMedian()]
        # for i in range(k, len(nums)):
        #     dh.insert(nums[i])
        #     dh.erase(nums[i - k])
        #     ans.append(dh.getMedian())
        
        # return ans

        # 方法三:双堆对顶,简化版
        def get(k): # 中位数
            return -small[0] if k % 2 else (-small[0] + big[0]) / 2

        small, big = [], [] # 大堆,小堆
        d = defaultdict(int) # 延迟删除
        # 1、初始化大小堆,分配前 k 个元素
        for i in range(k): # 先把 k 个元素放入大堆
            heappush(small, -nums[i])
        for i in range(k // 2): # 一半较大的元素从大堆移到小堆
            heappush(big, -heappop(small))
        ans = [get(k)] # 第一个中位数
        # 2、滑动窗口
        for i in range(k, len(nums)):
            balance = 0 # 堆已经平衡
            # 2-1、记录将要删除的元素 
            left = nums[i - k] # 要删除的元素
            d[left] += 1 # 先记下要删除的元素            
            if small and left <= -small[0]: balance -= 1 # 在小堆
            else: balance += 1 # 在大堆
            # 2-2、确定当前元素加入那个堆
            if small and nums[i] <= -small[0]:
                heappush(small, -nums[i])
                balance += 1
            else:
                heappush(big, nums[i])
                balance -= 1
            # 2-3、平衡大小堆
            if balance > 0:
                heappush(big, -heappop(small))
            if balance < 0:
                heappush(small, -heappop(big))     
            # 2-4、清理堆顶
            while small and d[-small[0]] > 0:
                d[-heappop(small)] -= 1
            while big and d[big[0]] > 0:
                d[heappop(big)] -= 1
            # 2-5、计算中位数
            ans.append(get(k)) 
        return ans

# class DualHeap:
#     def __init__(self, k: int):        
#         self.small = [] # 大根堆,维护较小的一半元素        
#         self.large = [] # 小根堆,维护较大的一半元素
#         # 哈希表,记录「延迟删除」的元素,key 为元素,value 为需要删除的次数
#         self.delayed = Counter()
#         self.k = k
#         # small 和 large 当前包含的元素个数,需要扣除被「延迟删除」的元素
#         self.smallSize = 0
#         self.largeSize = 0

#     # 清理堆顶延迟删除的元素
#     def prune(self, heap: List[int]):
#         while heap:
#             x = heap[0]
#             if heap is self.small: x = -x
#             if x in self.delayed:
#                 self.delayed[x] -= 1
#                 if self.delayed[x] == 0:
#                     self.delayed.pop(x)
#                 heappop(heap)
#             else: break
    
#     # 调整 small 和 large 中的元素个数,使得二者的元素个数满足要求
#     def makeBalance(self):
#         if self.smallSize > self.largeSize + 1: # small 比 large 元素多 2 个
#             heappush(self.large, -heappop(self.small))            
#             self.smallSize -= 1
#             self.largeSize += 1            
#             self.prune(self.small) # 清理
#         elif self.smallSize < self.largeSize:
#             # large 比 small 元素多 1 个
#             heappush(self.small, -heappop(self.large))            
#             self.smallSize += 1
#             self.largeSize -= 1
#             self.prune(self.large) # 清理

#     def insert(self, num: int):
#         if not self.small or num <= -self.small[0]:
#             heappush(self.small, -num)
#             self.smallSize += 1
#         else:
#             heappush(self.large, num)
#             self.largeSize += 1
#         self.makeBalance()

#     def erase(self, num: int):
#         self.delayed[num] += 1
#         if num <= -self.small[0]:
#             self.smallSize -= 1
#             if num == -self.small[0]:
#                 self.prune(self.small)
#         else:
#             self.largeSize -= 1
#             if num == self.large[0]:
#                 self.prune(self.large)
#         self.makeBalance()

#     def getMedian(self) -> float:
#         return float(-self.small[0]) if self.k % 2 else (-self.small[0] + self.large[0]) / 2

1183. 矩阵中 1 的最大数量

1851. 包含每个查询的最小区间

class Solution:
    def minInterval(self, intervals: List[List[int]], queries: List[int]) -> List[int]:      
        m, n = len(intervals), len(queries)
        res = [-1] * n
        heap = []
        # 将 queries 中数字按照从小到大的顺序排列,并记录每个值对应的下标
        qr = sorted([(i,q) for i,q in enumerate(queries)],key=lambda x:x[1])
        
        intervals.sort()
        index = 0
        for i, q in qr:
            # 如果 intervals 中区间的左端点小于等于查询值q,则将区间的长度和右端点加入堆中
            while index < m and intervals[index][0] <= q:
                l, r = intervals[index]
                heappush(heap, [r - l + 1, r])
                index += 1
            # 如果堆顶元素存储的右端点小于查询值,则对顶元素出堆
            while heap and heap[0][1] < q:
                heappop(heap)
            # 如果堆不为空,堆顶元素即为所求
            if heap:
                res[i] = heap[0][0]
                
        return res

743. 网络延迟时间

class Solution:
    def networkDelayTime(self, times: List[List[int]], n: int, k: int) -> int:
        heap, record, res = [(0, k)], [False] * (n + 1), 0
        link = [{} for _ in range(n + 1)]
        for x, y, t in times:
            link[x][y] = t
        while n:
            while len(heap) and record[heap[0][1]]:
                heappop(heap)
            if not len(heap):
                return -1
            res, x = heappop(heap)
            record[x] = True
            for y in link[x]:
                if not record[y]:
                    heappush(heap, (res + link[x][y], y))
            n -= 1
        return res

2163. 删除元素后和的最小差值

class Solution {
    public long minimumDifference(int[] nums) {
        var m = nums.length;
        var n = m / 3;
        var minPQ = new PriorityQueue<Integer>();
        var sum = 0L;
        for (var i = m - n; i < m; i++) {
            minPQ.add(nums[i]);
            sum += nums[i];
        }
        var sufMax = new long[m - n + 1]; // 后缀最大和
        sufMax[m - n] = sum;
        for (var i = m - n - 1; i >= n; --i) {
            minPQ.add(nums[i]);
            sum += nums[i] - minPQ.poll();
            sufMax[i] = sum;
        }

        var maxPQ = new PriorityQueue<Integer>(Collections.reverseOrder());
        var preMin = 0L; // 前缀最小和
        for (var i = 0; i < n; ++i) {
            maxPQ.add(nums[i]);
            preMin += nums[i];
        }
        var ans = preMin - sufMax[n];
        for (var i = n; i < m - n; ++i) {
            maxPQ.add(nums[i]);
            preMin += nums[i] - maxPQ.poll();
            ans = Math.min(ans, preMin - sufMax[i + 1]);
        }
        return ans;
    }
}

855. 考场就座

class ExamRoom {
    int n;
    TreeSet<Integer> seats;
    PriorityQueue<int[]> q;

    public ExamRoom(int n) {
        this.n = n;
        seats = new TreeSet<Integer>();
        q = new PriorityQueue<int[]>((a, b) -> {
            int d1 = a[1] - a[0], d2 = b[1] - b[0];
            return d1 / 2 < d2 / 2 || (d1 / 2 == d2 / 2 && a[0] > b[0]) ? 1 : -1;
        });
    }

    public int seat() {
        if (seats.isEmpty()) {
            seats.add(0);
            return 0;
        }
        int left = seats.first(), right = n - 1 - seats.last();
        while (seats.size() >= 2) {
            int[] p = q.peek();
            if (seats.contains(p[0]) && seats.contains(p[1]) && seats.higher(p[0]) == p[1]) { // 不属于延迟删除的区间
                int d = p[1] - p[0];
                if (d / 2 < right || d / 2 <= left) { // 最左或最右的座位更优
                    break;
                }
                q.poll();
                q.offer(new int[]{p[0], p[0] + d / 2});
                q.offer(new int[]{p[0] + d / 2, p[1]});
                seats.add(p[0] + d / 2);
                return p[0] + d / 2;
            }
            q.poll(); // leave 函数中延迟删除的区间在此时删除
        }
        if (right > left) { // 最右的位置更优
            q.offer(new int[]{seats.last(), n - 1});
            seats.add(n - 1);
            return n - 1;
        } else {
            q.offer(new int[]{0, seats.first()});
            seats.add(0);
            return 0;
        }
    }

    public void leave(int p) {
        if (p != seats.first() && p != seats.last()) {
            int prev = seats.lower(p), next = seats.higher(p);
            q.offer(new int[]{prev, next});
        }
        seats.remove(p);
    }
}
class ExamRoom {
    TreeSet<Integer> set;
    int n;
    public ExamRoom(int n) {
        this.n = n;
        set = new TreeSet<>(); 
    }
    public int seat() {
        if (set.size() == 0) {set.add(0); return 0;} //没有人时,一定返回0
        int pre = set.first(), ans = set.first(), idx = 0; //初始话为选择最左的长度
        for (int x : set) {
            if (ans < (x - pre) / 2) {
                ans = (x - pre) / 2;
                idx = (x + pre) / 2;
            }
            pre = x;
        }
        //最右进行判断
        int d = n - 1 - set.last();
        if (ans < d) {ans = d; idx = n - 1;}
        set.add(idx);
        return idx;
    }

    public void leave(int p) { 
        set.remove(p);
    }
}
class ExamRoom {
    PriorityQueue<int[]> q;
    TreeSet<Integer> set;
    int n;
    public ExamRoom(int n) {
        this.n = n;
        q = new PriorityQueue<>((a, b) -> {
            int d1 = (a[1] - a[0]) / 2, d2 = (b[1] - b[0]) / 2;
            return d1 == d2 ? a[0] - b[0] : d2 - d1; //当长度相等时,坐标更小先弹出,当不相等时,长度更大的先弹出
        });
        set = new TreeSet<>(); //创建有序集合
    }

    public int seat() {
        if (set.size() == 0) { set.add(0); return 0;} //1.没有人时,一定返回0
        int d1 = set.first(), d2 = n - 1 - set.last(); //获取最左和最右放置学生能获取的长度
        while (set.size() >= 2) { //2.大于等于两个人的时候,可以选择最左最右 或者中间的区间
            int[] t = q.poll();
            if (!set.contains(t[0]) || !set.contains(t[1]) || set.higher(t[0]) != t[1]) continue; //无效区间,某个端点已经被删除
            int d3 = (t[1] - t[0]) / 2;                
            if (d3 <= d1 || d3 < d2) {q.add(new int[]{t[0], t[1]}); break;}; //选择最左或者最右
            int mid = (t[0] + t[1]) / 2; //选择终点
            q.add(new int[]{t[0], mid});
            q.add(new int[]{mid, t[1]});
            set.add(mid);  
            return mid;
        } 
        //3.选择最左或者最右的位置
        int l = 0, r = set.first(), sel = 0;
        if (d1 < d2) {l = set.last(); r = n - 1; sel = n - 1;}
        q.add(new int[]{l, r});
        set.add(sel);
        return sel;
    }

    public void leave(int p) { 
        if (p != set.first() && p != set.last()) q.add(new int[]{set.lower(p), set.higher(p)}); //如果不是删除两端点, 那么会增加新区间
        set.remove(p); 
    }
}

855. 考场就座

class ExamRoom {
    int n;
    TreeSet<Integer> s = new TreeSet();
    PriorityQueue<int[]> q = new PriorityQueue<int[]>((a, b) -> {
            int x = (a[1] - a[0]) / 2, y = (b[1] - b[0]) / 2;
            return x == y ? a[0] - b[0] : y - x;
            //return x < y || (x == y && a[0] > b[0]) ? 1 : -1;
        });

    public ExamRoom(int n) {
        this.n = n;
    }

    public int seat() {
        if (s.isEmpty()) {
            s.add(0);
            return 0;
        }
        // 延迟删除无效区间
        while (!q.isEmpty() && (!s.contains(q.peek()[0]) || !s.contains(q.peek()[1]) || s.higher(q.peek()[0]) != q.peek()[1])) {
            q.poll();         
        }
        int p = 0, left = s.first(), right = n - 1 - s.last();
        if(!q.isEmpty()){
            int[] t = q.peek();            
            int d = (t[1] - t[0]) / 2; // 向上取整
            if(d > left && d >= right){
                p = t[0] + d;
                q.offer(new int[]{t[0], p});
                q.offer(new int[]{p, t[1]});
                s.add(p);
                return p;
            }            
        }        
        if (right > left) { // 最右的位置更优
            p = n - 1;
            q.offer(new int[]{s.last(), p});            
        } else {
            q.offer(new int[]{0, s.first()});
        }
        s.add(p);
        return p;
    }

    public void leave(int p) {
        // 不是两端填加区间
        if (p != s.first() && p != s.last()) {         
            q.offer(new int[]{s.lower(p), s.higher(p)});
        }
        s.remove(p);
    }
}

1439. 有序矩阵中的第 k 个最小数组和

1686. 石子游戏 VI

1985. 找出数组中的第 K 大整数

2290. 到达角落需要移除障碍物的最小数目

2454. 下一个更大元素 IV

2503. 矩阵查询可获得的最大分数

class Solution {
    int[] DIRS = {1, 0, -1, 0, 1};

    public int[] maxPoints(int[][] grid, int[] queries) {
        var m = queries.length;
        var id = IntStream.range(0, m).boxed().toArray(Integer[]::new);
        Arrays.sort(id, (i, j) -> queries[i] - queries[j]);

        var ans = new int[m];
        var pq = new PriorityQueue<int[]>((a, b) -> a[0] - b[0]);
        pq.add(new int[]{grid[0][0], 0, 0});
        grid[0][0] = 0; // 充当 vis 数组的作用
        int r = grid.length, c = grid[0].length, cnt = 0;
        for (var i : id) {
            var q = queries[i];
            while (!pq.isEmpty() && pq.peek()[0] < q) {
                ++cnt;
                var p = pq.poll();
                for (var k = 0; k < 4; k++) {
                    int x = p[1] + DIRS[k], y = p[2] + DIRS[k + 1];
                    if (0 <= x && x < r && 0 <= y && y < c && grid[x][y] > 0) {
                        pq.add(new int[]{grid[x][y], x, y});
                        grid[x][y] = 0;
                    }
                }
            }
            ans[i] = cnt;
        }
        return ans;
    }
}
class Solution {
    int[] DIRS = {1, 0, -1, 0, 1};
    int[] p, size;
    public int[] maxPoints(int[][] grid, int[] queries) {
        int r = grid.length, c = grid[0].length, n = r * c;
        p = new int[n];
        size = new int[n];
        for (var i = 0; i < n; i++) {
            p[i] = i;
            size[i] = 1;
        } 
        Integer[] idx = new Integer[n];
        Arrays.setAll(idx, i -> i);
        Arrays.sort(idx, (i, j) -> grid[i/c][i%c] - grid[j/c][j%c]);
        int m = queries.length;
        Integer[] ids = new Integer[m];
        Arrays.setAll(ids, i -> i);
        Arrays.sort(ids, (i, j) -> queries[i] - queries[j]);

        var ans = new int[m];
        int i = 0;
        for (int id : ids) {
            int q = queries[id];
            for (; i < n; ++i) {                
                int x = idx[i]/c, y = idx[i]%c;
                if (grid[x][y] >= q) break;
                for (int k = 0; k < 4; k++) {
                    int u = x + DIRS[k], v = y + DIRS[k + 1];
                    if (0 <= u && u < r && 0 <= v && v < c && grid[u][v] < q)
                        merge(x * c + y, u * c + v); 
                }
            }
            if (grid[0][0] < q)
                ans[id] = size[find(0)]; 
        }
        return ans;
    }

    
    int find(int x) {
        if (p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

    void merge(int from, int to) {
        from = find(from);
        to = find(to);
        if (from != to) {
            p[from] = to;
            size[to] += size[from];
        }
    }
}

23. 合并K个升序链表

272. 最接近的二叉搜索树值 II

355. 设计推特

358. K 距离间隔重排字符串

378. 有序矩阵中第 K 小的元素

420. 强密码检验器

451. 根据字符出现频率排序

499. 迷宫 III

502. IPO

505. 迷宫 II

675. 为高尔夫比赛砍树

703. 数据流中的第 K 大元素

767. 重构字符串

882. 细分图中的可到达节点

912. 排序数组

1172. 餐盘栈

1354. 多次求和构造目标数组

1368. 使网格图至少有一条有效路径的最小代价

1388. 3n 块披萨

1424. 对角线遍历 II

1514. 概率最大的路径

1675. 数组的最小偏移量

1738. 找出第 K 大的异或坐标值

1786. 从第一个节点出发到最后一个节点的受限路径数

1792. 最大平均通过率

1825. 求出 MK 平均值

1834. 单线程 CPU

1845. 座位预约管理系统

1878. 矩阵中最大的三个菱形和

1912. 设计电影租借系统

1405. 最长快乐字符串

class Solution:
    def longestDiverseString(self, a: int, b: int, c: int) -> str:
        d = {'a':a, 'b':b, 'c':c}
        q = []
        while (cnt := sorted([k for k, v in d.items() if v], key = lambda x : -d[x])):
            for ch in cnt:
                if q[-2:] != [ch, ch]:
                    q.append(ch)
                    d[ch] -= 1
                    break
            if len(cnt) == 1 and q[-2:] == cnt * 2: break
        return ''.join(q)

1499. 满足不等式的最大值

class Solution {
    public int findMaxValueOfEquation(int[][] points, int k) {
        Deque<int[]> q = new ArrayDeque<>();
        int n = points.length, ans = Integer.MIN_VALUE;
        for(int i = 0; i < n; i++){
            int x = points[i][0], y = points[i][1];
            // 滑动窗口 头部维护窗口
            while(!q.isEmpty() && x - q.peek()[0] > k){
                q.poll();
            }
            if(!q.isEmpty()){
                int cur = q.peek()[1] + x + y;
                if(cur > ans) ans = cur;
            }
            // 单调队列 队尾维护最大值
            while(!q.isEmpty() && q.peekLast()[1] <= y - x) q.pollLast();
            q.add(new int[]{x, y - x});
        }
        return ans;
    }}

1785. 构成特定和需要添加的最少元素

class Solution:
    def minElements(self, nums: List[int], limit: int, goal: int) -> int:
              
        return (abs(goal - sum(nums)) + limit-1)//limit;

2034. 股票价格波动

from sortedcontainers import SortedList

class StockPrice:
    def __init__(self):
        self.price = SortedList()
        self.timePriceMap = {}
        self.maxTimestamp = 0

    def update(self, timestamp: int, price: int) -> None:
        if timestamp in self.timePriceMap:
            self.price.discard(self.timePriceMap[timestamp])
        self.price.add(price)
        self.timePriceMap[timestamp] = price
        self.maxTimestamp = max(self.maxTimestamp, timestamp)

    def current(self) -> int:
        return self.timePriceMap[self.maxTimestamp]

    def maximum(self) -> int:
        return self.price[-1]

    def minimum(self) -> int:
        return self.price[0]

2054. 两个最好的不重叠活动

class Solution:
    def maxTwoEvents(self, events: List[List[int]]) -> int:

        events.append([0, 0, 0])
        n = len(events)
        events.sort(key=lambda x: x[1])
        f = [0] * n
        for i in range(1, n):
            f[i] = max(f[i - 1], events[i][2])
        
        ans = 0 
        for i, (s, e, v) in enumerate(events):            
            l, r = 0, i - 1 
            while l < r:
                mid = l + r + 1 >> 1 
                if events[mid][1] < s: l = mid 
                else: r = mid - 1 
            ans = max(ans, f[r] + v)
        return ans

2456. 最流行的视频创作者

class Solution:
    def mostPopularCreator(self, creators: List[str], ids: List[str], views: List[int]) -> List[List[str]]:
#         d = defaultdict(list) # 统计 creator, id
#         e = defaultdict(int) # 统计播放量
#         for c, i, v in zip(creators, ids, views):
#             e[c] += v
#             d[c].append([i, v])
#         ans = []
#         max_ = max(e.values())
#         for c, v in e.items():
#             if v == max_:
#                 ans.append([c, 0])
#         for i, (c, _) in enumerate(ans):
#             x = sorted(d[c], key=lambda y:[-y[1], y[0]])
#             ans[i][1] = x[0][0] # 更新 id
#         return ans


        d = {} # {作者:[和, 最大, id]}
        for c, i, v in zip(creators, ids, views):
            if c in d:
                t = d[c]
                t[0] += v
                if v > t[1] or v == t[1] and i < t[2]:
                    t[1], t[2] = v, i
            else:
                d[c] = [v, v, i]

        ans, maxvs = [], -1
        for c, (vs, v, i) in d.items():
            if vs > maxvs:
                maxvs = vs
                ans = [[c, i]]
            elif vs == maxvs:
                ans.append([c, i])
        return ans

2497. 图中最大星和

class Solution:
    def maxStarSum(self, vals: List[int], edges: List[List[int]], k: int) -> int:
        ans = max(vals)
        d = defaultdict(list)
        for i, j in edges:
            x, y = vals[i], vals[j]
            if y > 0: d[i].append(y)
            if x > 0: d[j].append(x)
        
        for i, v in d.items():
            q = sorted(v, reverse=True)[:k]
            
            ans = max(ans, vals[i] + sum(q))
        return ans

973. 最接近原点的 K 个点

class Solution:
    def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
        points.sort(key=lambda x: x[0]*x[0] + x[1]*x[1])
        return points[:k]

1337. 矩阵中战斗力最弱的 K 行

class Solution:
    def kWeakestRows(self, mat: List[List[int]], k: int) -> List[int]:
        m, n = len(mat), len(mat[0])
        power = list()
        for i in range(m):
            l, r, pos = 0, n - 1, -1
            while l <= r:
                mid = (l + r) // 2
                if mat[i][mid] == 0:
                    r = mid - 1
                else:
                    pos = mid
                    l = mid + 1
            power.append((pos + 1, i))

        # heapq.heapify(power)
        # ans = list()
        # for i in range(k):
        #     ans.append(heapq.heappop(power)[1])
        power.sort()
        
        return [power[i][1] for i in range(k)] 
        return ans

2333. 最小差值平方和

class Solution:
    def minSumSquareDiff(self, nums1: List[int], nums2: List[int], k1: int, k2: int) -> int:
        n = len(nums1)
        q = sorted([abs(a - b) for a, b in zip(nums1, nums2)])
        k = k1 + k2
        s = sum(q)
        if s <= k:  return 0
        for i, x in enumerate(q):
            j = n - i
            if s - j * x <= k:
                k -= s - j * x
                m = k // j
                k %= j
                break
            s -= x
        q = q[:i] + [x - m - 1] * k + [x - m] * (n - i - k) 
        return sum([i*i for i in q])

2342. 数位和相等数对的最大和

class Solution:
    def maximumSum(self, nums: List[int]) -> int:
        ans = -1
        d = {}
        for v in nums:
            s, x = 0, v
            while x:
                s += x % 10
                x //= 10
            if s in d:
                if ans < d[s] + v: ans = d[s] + v
            if v > d.get(s, 0): d[s] = v
        return ans

2343. 裁剪数字后查询第 K 小的数字

class Solution:
    def smallestTrimmedNumbers(self, nums: List[str], queries: List[List[int]]) -> List[int]:
        # q, n = [], len(nums)
        # for k, t in queries:
        #     arr = [s[-t:] for s in nums]
        #     a = sorted(range(n), key=lambda x: arr[x])[k-1]
        #     q.append(a)
        # return q

        idx = sorted(range(len(nums)), key=lambda i: nums[i][-1])  # 按照最后一个字符排序
        ans, j = [0] * len(queries), 2
        for (k, trim), id in sorted(zip(queries, range(len(queries))), key=lambda q: q[0][1]):  # 按 trim 排序
            while j <= trim:
                idx.sort(key=lambda i: nums[i][-j])  # 只比较倒数第 j 个字符的大小
                j += 1
            ans[id] = idx[k - 1]
        return ans

2344. 使数组可以被整除的最少删除次数

class Solution:
    def minOperations(self, nums: List[int], numsDivide: List[int]) -> int:        
        nums.sort()
        pre = -1        
        for i, x in enumerate(nums):
            if x == pre: continue
            for y in numsDivide:
                if y % x: break
            else: return i             
            pre = x
        return -1

2424. 最长上传前缀

class LUPrefix:

    def __init__(self, n: int):
        self.n = n
        self.q = set()
        self.x = 1
        
    def upload(self, video: int) -> None:
        self.q.add(video)
        while self.x in self.q:
            self.x += 1

    def longest(self) -> int:        
        return self.x - 1

1263. 推箱子

方法一:bfs + 双端队列
本质上是最短路径问题,求箱子最少移动多少次可以到达目标位置。
同一个点可以被玩家多次访问,因为某个点能否被访问同时取决于玩家和箱子的状态。 设置四维数组 vis[playerx][playery][boxx][boxy] 来防止重复访问, 若本次访问的位置 <px, py, bx, by> 之前被访问过,则本次到达此位置的路径一定比之前到达此位置路径长,因此不再从此点继续扩展。
BFS 是逐层遍历,每遍历一层距离增加 1, 最终访问到目的节点,获得最小距离。
玩家移动 --> 距离不会增加,而箱子移动 --> 距离增加,因此为了保证遍历过程中路径长度从小到大:
箱子移动后,距离 + 1,放入队列末尾;
玩家移动,而箱子未移动,距离不变,放入队列首部;
每次从队列首部取元素。
队列中的所有点的距离最多只会出现两种可能, d 和 d + 1, 因为每次都是先访问距离较小的点,距离为 d 的点未访问结束,不可能访问距离为 d + 1 的点,队列中一定不会出现距离为 d + 2 的点。

class Solution:
    def minPushBox(self, grid: List[List[str]]) -> int:
        
        def check(x, y): # 检测位置是否合法
            return m > x >= 0 and n > y >= 0 and grid[x][y] != '#'

        dirs = (1, 0, -1, 0, 1)
        m, n = len(grid), len(grid[0])
        vis = [[[[False] * 20 for _ in range(20)] for _ in range(20)] for _ in range(20)]

        boxX = boxY = targetX = targetY = startX = startY = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == 'S': startX, startY = i, j
                elif grid[i][j] == 'B': boxX, boxY = i, j
                elif grid[i][j] == 'T': targetX, targetY = i, j

        q = deque([(startX, startY, boxX, boxY, 0)])  # 玩家坐标、箱子坐标, 距离        
        vis[startX][startY][boxX][boxY] = True

        while q:
            px, py, bx, by, d = q.popleft()
            if bx == targetX and by == targetY: return d # 箱子到达目的地
            for k in range(4):
                npx, npy = px + dirs[k], py + dirs[k + 1]
                if not check(npx, npy): continue
                if npx == bx and npy == by: # 人能到达箱子的位置,箱子同方向移动
                    nbx, nby = bx + dirs[k], by + dirs[k + 1]
                    if not check(nbx, nby) or vis[npx][npy][nbx][nby]: continue
                    q.append((npx, npy, nbx, nby, d + 1));  # 箱子移动,放入队尾部
                elif not vis[npx][npy][bx][by]: # 只能人不动箱子,直到人能到达箱子的位置
                    q.appendleft((npx, npy, bx, by, d)) # 箱子未动,放入队首
                vis[npx][npy][bx][by] = True

        return -1
class Solution {
    int n, m;  
    public int minPushBox(char[][] grid) {        
        m = grid.length; n = grid[0].length;
        Deque<int[]> q = new ArrayDeque();
        int[] dir = new int[]{1,0,-1,0,1};
        boolean[][][][] vis = new boolean[20][20][20][20];
        int tx = 0, ty = 0, sx = 0, sy = 0, bx = 0, by = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == 'S'){
                    sx = i; sy = j;
                }else if(grid[i][j] == 'T'){
                    tx = i; ty = j;
                }else if(grid[i][j] == 'B'){
                    bx = i; by = j;
                }
            }
        }
        q.add(new int[]{sx, sy, bx, by, 0});
        while(!q.isEmpty()){
            int[] c = q.poll();
            if(c[2] == tx && c[3] == ty) return c[4];
            vis[c[0]][c[1]][c[2]][c[3]] = true;
            for(int k = 0; k < 4; k++){
                int px = c[0] + dir[k], py = c[1] + dir[k + 1];
                if(!check(px, py, grid)) continue;
                if(px == c[2] && py == c[3]){
                    bx = c[2] + dir[k]; by = c[3] + dir[k + 1];
                    if(!check(bx, by, grid) || vis[px][py][bx][by]) continue;
                    q.add(new int[]{px, py, bx, by, c[4] + 1});
                } else if(!vis[px][py][c[2]][c[3]]){
                    q.addFirst(new int[]{px, py, c[2], c[3], c[4]});
                }
            }
        }
        return -1;
    }
    boolean check(int x, int y, char[][] grid){
        return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] != '#';
    }
}

1753. 移除石子的最大得分

class Solution:
    def maximumScore(self, a: int, b: int, c: int) -> int:
        q = [a, b, c]
        q.sort()
        a, b, c = q
        if a + b <= c:
            return a + b
        # a 放入 b, c        
        return sum(q) // 2

506. 相对名次

class Solution:
    def findRelativeRanks(self, score: List[int]) -> List[str]:
        x = sorted(score, reverse=True)
        
        n = len(score)
        d = {x[i]:str(i+1) for i in range(n)}
        
        d[x[0]] = "Gold Medal"
        if n > 1: d[x[1]] = "Silver Medal"
        if n > 2: d[x[2]] = "Bronze Medal"
        
        return [d[i] for i in score]

1338. 数组大小减半

class Solution:
    def minSetSize(self, arr: List[int]) -> int:
        n = (len(arr) + 1)// 2
        c = Counter(arr)
        a = sorted(c.values(), reverse=True)
        cnt = 0
        for i, x in enumerate(a):
            cnt += x
            if cnt >= n: return i + 1

2099. 找到和最大的长度为 K 的子序列

class Solution:
    def maxSubsequence(self, nums: List[int], k: int) -> List[int]:
        index = sorted(range(len(nums)), key=lambda i:nums[i])[-k:]   
        index.sort()   
        return [nums[i] for i in index]

2335. 装满杯子需要的最短总时长

class Solution:
    def fillCups(self, amount: List[int]) -> int:
        # 三种,最大两个各减一,否则取最大
        ans = 0
        while amount[0]:
            amount.sort()
            ans += 1
            amount[1] -= 1
            amount[2] -= 1
        
        return ans + max(amount)

2357. 使数组中所有元素都等于零

class Solution:
    def minimumOperations(self, nums: List[int]) -> int:
        nums.sort()
        ans = s = 0
        for x in nums:
            x -= s
            if x > 0:
                s += x
                ans += 1
        return ans

621. 任务调度器

class Solution(object):
    def leastInterval(self, tasks, n):  
        if n == 0: return len(tasks)
        # 计算出现最多的任务所需的最短时间 - 1,其它的填空即可。
        d = Counter(tasks)
        arr = sorted(d.values(), reverse=True)      
        res = (arr[0] - 1) * (n + 1) # 最短时间 - 1        
        for x in arr:
            if x == arr[0]: res += 1 # 最多的任务的种类
            else: break
        # 如果结果比任务数量少,则返回总任务数,任务种类 > n.
        return max(res, len(tasks))

658. 找到 K 个最接近的元素

class Solution:
    def findClosestElements(self, arr: List[int], k: int, x: int) -> List[int]:
        # arr.sort(key=lambda y:abs(y-x))
        # return sorted(arr[:k])
        # 方法一:双指针
        i, j = 0, len(arr)
        while j - i > k:
            if abs(arr[i] - x) > abs(arr[j-1] - x): i += 1
            else: j -= 1
        return arr[i:j]
        
        # 方法二:二分 查找左边界
        i, j = 0, len(arr) - k
        while i < j:
            mid = i + j >> 1
            if x - arr[mid] > arr[mid + k] - x: i = mid + 1
            else: j = mid
        return arr[i:i+k]
        
        # 方法三:
        # n, i = len(arr) - k, 0
        # while i < n and x - arr[i] > arr[i + k] - x: 
        #     i += 1
        # return arr[i:i+k]

295. 数据流的中位数

from sortedcontainers import SortedList

class MedianFinder:

    def __init__(self):
        self.sl = SortedList()

    def addNum(self, num: int) -> None:
        self.sl.add(num)      
        
    def findMedian(self) -> float:
        n = len(self.sl)
        return (self.sl[n // 2] + self.sl[(n-1)//2]) / 2

2146. 价格范围内最高排名的 K 样物品

class Solution:
    def highestRankedKItems(self, grid: List[List[int]], pricing: List[int], start: List[int], k: int) -> List[List[int]]:
        m, n = len(grid), len(grid[0])
        q = deque([(start[0], start[1], 0)])
        items = []   # 感兴趣物品的信息  
        grid[start[0]][start[1]] = -grid[start[0]][start[1]] # 避免重复遍历  
        while q:
            x, y, d = q.popleft()
            if pricing[0] <= -grid[x][y] <= pricing[1]:
                items.append((d, -grid[x][y], x, y))
                
            for i, j in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
                if i >= 0 and i < m and j >= 0 and j < n and grid[i][j] > 0:
                    q.append((i, j, d + 1))  
                    grid[i][j] = -grid[i][j]   # 避免重复遍历                 
                    
        items.sort() # 按照优先级从高到低排序
        return [item[2:] for item in items[:k]] # 排名最高 k 件物品的坐标

2102. 序列顺序查询

class SORTracker {
    
    class Scenery {
        String name;
        int score;
        public Scenery(String name, int score){
            this.name = name;
            this.score = score;
        }
    }

    TreeSet<Scenery> set;
    Scenery spot = new Scenery("哨兵", -1); // 哨兵   
   
    public SORTracker() {
        set = new TreeSet<Scenery>((a, b) ->  (a.score != b.score) ? b.score - a.score : a.name.compareTo(b.name));
        set.add(spot);        
    }
    
    public void add(String name, int score) {
        Scenery s = new Scenery(name, score);
        set.add(s);
       
        if(s.score >= spot.score){           
            if(s.score == spot.score){
                if(s.name.compareTo(spot.name) < 0) spot = set.lower(spot);
            }else spot = set.lower(spot);
        }
    }
    
    public String get() {
        String res = spot.name;
        spot = set.higher(spot);
        return res;
    }
}

778. 水位上升的泳池中游泳

class Solution:
    def swimInWater(self, grid: List[List[int]]) -> int:
        def check(h):
            q = [(0, 0)]
            vis = set(((0, 0)))
            while q:
                i, j = q.pop()
                if (i, j) == (m - 1, n - 1): return True
                for a, b in dirs:
                    x, y = a + i, b + j
                    if 0 <= x < m and 0 <= y < n and (x, y) not in vis and grid[x][y] <= h:
                        vis.add((x, y))
                        q.append((x, y))
            return False

        m, n = len(grid), len(grid[0])
        dirs = ((-1, 0), (1, 0), (0, -1), (0, 1))
        i, j = grid[0][0], 2500
        while i < j:
            mid = i + j >> 1
            if check(mid): j = mid
            else: i = mid + 1
        return j

787. K 站中转内最便宜的航班

class Solution:    
    def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:       
        dp = [inf] * n
        dp[src] = 0
        res = inf
        for _ in range(1, k + 2):
            tmp = [inf] * n
            for s, d, p in flights:
                tmp[d] = min(tmp[d], dp[s]+p)
            dp = tmp
            res = min(res, dp[dst])
        return -1 if res == inf else res

786. 第 K 个最小的素数分数

class Solution:
    def kthSmallestPrimeFraction(self, arr: List[int], k: int) -> List[int]:
        n = len(arr)
        left, right = 0, 1

        while True:
            mid = (left + right) / 2
            i = count = 0 # count 小于 mid 的分数的个数
            x, y = 0, 1 # 记录最大的分数的分子分母
            
            for j in range(1, n):
                while count <= k and arr[i] / arr[j] < mid:
                    if arr[i] * y > arr[j] * x:
                        x, y = arr[i], arr[j]
                    i += 1
                    
                count += i
                # if count > k: break

            if count == k:  return [x, y]            
            if count < k: left = mid
            else: right = mid

2398. 预算内的最多机器人数目

class Solution {
    public int maximumRobots(int[] chargeTimes, int[] runningCosts, long budget) {
        int n = chargeTimes.length, left = 0;
        long prefix = 0;
        int ans = 0;
        Deque<Integer> q = new ArrayDeque<>();
        for(int i = 0; i < n; i++){
            int x = chargeTimes[i];
            while(!q.isEmpty() && x > q.peekLast()){
                q.pollLast();
            }
            q.add(x);

            prefix += runningCosts[i];
            while(q.peek() + (i - left + 1) * prefix > budget){
                if(q.peek() == chargeTimes[left]){
                    q.poll();
                }
                prefix -= runningCosts[left++];
            }
            ans = Math.max(ans, i - left + 1);
        }
        return ans;
    }
}

2182. 构造限制重复的字符串

class Solution {
    public String repeatLimitedString(String s, int repeatLimit) {
        int[] chs = new int[26];
        StringBuilder sb = new StringBuilder();
        for (char c : s.toCharArray()) chs[c - 'a']++;
        sigh:
        for (int i = 25; i >= 0; i--){
            if (chs[i] == 0) continue;
            String t = String.valueOf((char)(i + 97));
            int d = chs[i] / repeatLimit, r = chs[i] % repeatLimit;
            flag:  // 1、处理整数部分
            for (int k = 0; k < d; k++){
                sb.append(t.repeat(repeatLimit));
                if (k == d - 1 && r == 0) continue sigh; // t 用完后不再添加分割符
                for (int j = i - 1; j >= 0; j--){
                    if (chs[j] > 0){
                        chs[j]--;
                        sb.append((char)(j + 97)); // 添加分割字符
                        continue flag;
                    }
                }
                return sb.toString(); // 其它字母用完后直接返回答案
            }            
            sb.append(t.repeat(r)); // 2、处理余数部分
        }
        return String.valueOf(sb);        
    }
}

2231. 按奇偶性交换后的最大数字

class Solution:
    def largestInteger(self, num: int) -> int:
        a = list(map(int, str(num)))
        n = len(a)
        # 进行选择排序
        for i in range(n - 1):
            for j in range(i + 1, n):
                if (a[i] - a[j]) % 2 == 0 and a[i] < a[j]:
                    a[i], a[j] = a[j], a[i]
        
        return int("".join(map(str, a)))

2285. 道路的最大总重要性

class Solution:
    def maximumImportance(self, n: int, roads: List[List[int]]) -> int:
        m = len(roads)
        cnt = [0] * n
        for a, b in roads:
            cnt[a] += 1
            cnt[b] += 1

        res = 0
        cnt.sort()
        for i, x in enumerate(cnt):
            res += x * (i + 1)
        return res

1438. 绝对差不超过限制的最长连续子数组

class Solution {
    public int longestSubarray(int[] nums, int limit) {

        // 方法一:滑动窗口 + 有序集合 70 ms
        // TreeMap<Integer, Integer> map = new TreeMap<>();
        // int n = nums.length, left = 0;
        // for(int i = 0; i < n; i++) {
        //     int x = nums[i];
        //     // map.put(x, map.getOrDefault(x, 0) + 1);
        //     map.merge(x, 1, Integer::sum);
        //     if (map.lastKey() - map.firstKey() > limit) {
        //         int y = nums[left];
        //         map.put(y, map.get(y) - 1);
        //         if (map.get(y) == 0) map.remove(y);
        //         left++;
        //     }
        // }
        // return n - left;

        // 方法二:滑动窗口 + 单调队列 26 ms
        // Deque<Integer> min = new ArrayDeque<>(), max = new ArrayDeque<>();
        // int n = nums.length, left = 0;
        // for(int i = 0; i < n; i++){
        //     int x = nums[i];
        //     while(!max.isEmpty() && x > max.peekLast()) max.pollLast();
        //     while(!min.isEmpty() && x < min.peekLast()) min.pollLast();
        //     max.add(x);
        //     min.add(x); // 元素
        //     if(max.peek() - min.peek() > limit){
        //         if(max.peek() == nums[left]) max.poll();
        //         if(min.peek() == nums[left]) min.poll();
        //         left ++;                
        //     }            
        // }
        // return n - left;

        // 方法三:滑动窗口 + 数组模拟 4 ms
        // int left = 0, n = nums.length;
        // int[] max = new int[n], min = new int[n];
        // int start = 0, top = -1, a = 0, b = -1;
        // for(int i = 0; i < n; i++){
        //     int x = nums[i];
        //     while(top >= start && x > max[top]) top--;
        //     while(b >= a && x < min[b]) b--;
        //     max[++top] = x;
        //     min[++b] = x;            
        //     if(max[start] - min[a] > limit){
        //         if(max[start] == nums[left]) start++;
        //         if(min[a] == nums[left]) a++;
        //         left ++;                
        //     }            
        // }
        // return n - left;
    }
}

1631. 最小体力消耗路径

class Solution:
    def minimumEffortPath(self, heights: List[List[int]]) -> int:
        max_d=max([max(h) for h in heights])-min([min(h) for h in heights])     
        l, r=0,max_d
        while l<r:
            mid=(l+r)//2
            if self.workable(heights, mid):
                r=mid
            else:
                l=mid+1
        return l
                
    def workable(self, heights, d):
        m, n=len(heights), len(heights[0])
        directs=[[-1,0],[1,0],[0,-1],[0,1]]
        visited, que=set([(0,0)]),[(0,0)]
        while que:
            x, y=que.pop(0)
            for dx, dy in directs:
                x1, y1=x+dx, y+dy
                if 0<=x1<m and 0<=y1<n and ((x1,y1) not in visited):
                    if abs(heights[x1][y1]-heights[x][y])<=d:
                        if [x1, y1]==[m-1, n-1]: return True
                        que.append([x1, y1])
                        visited.add((x1,y1))
        return False

1648. 销售价值减少的颜色球

class Solution:
    def maxProfit(self, inventory: List[int], orders: int) -> int:
        # 最小堆 超时
        # q, ans, mod = [], 0, 10 ** 9 + 7
        # for x in inventory: heappush(q, -x)
        # while orders > 0:
        #     x = -heappop(q)
        #     top = -q[0] if q else 0
        #     cnt = min(x - top + 1, orders)
        #     ans += x * cnt - (cnt - 1) * cnt // 2
        #     orders -= cnt
        #     heappush(q, - top + 1) 
        # return ans % mod

        def check(k):  
            return sum(x - k for x in inventory if x >= k)

        mod = 10 ** 9 + 7
        i, j = 0, max(inventory) # 查找阈值
        while i < j:
            mid = i + j >> 1
            if check(mid) <= orders: j = mid # 反比
            else: i = mid + 1

        # 阈值是 i, i + 1 是取平的部分,i 是多出部分
        ans = sum((x + i + 1) * (x - i) // 2 for x in inventory if x >= i)
        # 计算多余次数
        ans += (orders - check(i)) * i
        return ans % mod

862. 和至少为 K 的最短子数组

class Solution:
    def shortestSubarray(self, nums: List[int], k: int) -> int:
        acc = list(accumulate(nums, initial=0))
        #[0, 84, 47, 79, 119, 214]
        q = deque()
        ans = (n := len(nums)) + 1
        for i, x in enumerate(acc):
            while q and x <= acc[q[-1]]: q.pop() 
            while q and x - acc[q[0]] >= k:
                ans = min(i - q.popleft(), ans)
                 
            q.append(i)
        return -1 if ans == n + 1 else ans