第 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