0
点赞
收藏
分享

微信扫一扫

Java 第 42 课 第 45 课 优先队列(priority queue)



第 42 课

  • [优先队列(priority queue)
  • 1046.最后一块石头的重量
  • 1054.距离相等的条形码
  • 基础知识
  • 第 43 课
  • 630.课程表III
  • 1705.吃苹果的最大数目
  • 第 44 课
  • 1353.最多可以参加的会议数目
  • 373.查找和最小的K对数字
  • 第 45 课
  • 871.最低加油次数
  • 2233.K次增加后的最大乘积
  • 2208.将数组和减半的最少操作次数
  • 239.滑动窗口最大值
  • 378.有序矩阵中第 K 小的元素
  • 719.找出第 k 小的距离对


Python_heapq

优先队列(priority queue)

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

1046.最后一块石头的重量

from typing import List
from heapq import heappop, heappush

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;
    }
}

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;
    }
}

基础知识

第 43 课

630.课程表III

知识点:lambda, PriorityQueue, sort.

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();
    }
}

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;
    }
}

第 44 课

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;
    }
}

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), []
        ## 方法一:切片
        # for i in range(min(m, k)):        
        #     for j in range(min(n, k)):    
        #         res.append([nums1[i], nums2[j]])
        
        # res.sort(key=lambda x:x[0] + x[1])
        # return res[:k]

        ## 方法二:笛卡尔积
        # return sorted(product(nums1[:m], nums2[:n]), key=lambda x:x[0] + x[1])[:k]

        ## 方法三:堆 
        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[]> pq = new PriorityQueue<>(k, (o1, o2)->{
            return nums1[o1[0]] + nums2[o1[1]] - nums1[o2[0]] - nums2[o2[1]];
        });
        List<List<Integer>> ans = new ArrayList<>();
        int m = nums1.length;
        int n = nums2.length;
        for (int i = 0; i < Math.min(m, k); i++) {
            pq.offer(new int[]{i,0});
        }
        while (k-- > 0 && !pq.isEmpty()) {
            int[] idxPair = pq.poll();
            List<Integer> list = new ArrayList<>();
            list.add(nums1[idxPair[0]]);
            list.add(nums2[idxPair[1]]);
            ans.add(list);
            if (idxPair[1] + 1 < n) {
                pq.offer(new int[]{idxPair[0], idxPair[1] + 1});
            }
        }        
        return ans;
    }
}

第 45 课

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;
    }
}

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;
    }
}

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;
    }
}

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 = collections.deque()
        # for i in range(k):
        #     while q and nums[i] >= nums[q[-1]]:q.pop()
        #     q.append(i)
        for i in range(k - 1, -1, -1):
            if not q or nums[i] > nums[q[-1]]: q.append(i)
        q.reverse() # 对应值单调递减

        res = [nums[q[0]]]
        for i in range(k, len(nums)):
            while q and nums[i] >= nums[q[-1]]: q.pop()
            q.append(i) # 去掉小的后添加 维护对应值单调递减队列
            while q[0] <= i - k: q.popleft() # 移出窗口
            res.append(nums[q[0]])
        
        return res

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

Leetcode

719.找出第 k 小的距离对

Leetcode


举报

相关推荐

0 条评论