當有多個來源(ex: k 個排序好的序列、或 k 條任務流水線),需要按時間/優先級合併處理時可以用priority queue來解。
我們直接練習個幾題
題目:給兩個已經排好順序的數字陣列和常數k,從兩個陣列中各取一個值,回傳前k個總和最小的組合
輸入:nums1 = [1,7,11], nums2 = [2,4,6], k = 3
輸出:[[1,2],[1,4],[1,6]]
限制:
<= nums1.length, nums2.length <= 10^5
-10^9 <= nums1[i], nums2[i] <= 10^9
nums1
and nums2
both are sorted in non-decreasing order.1 <= k <= 10^4
k <= nums1.length * nums2.length
解法:一開始想用暴力解列出全部的值O(m*n),在把總和排序O(m**n log m**n),當陣列給多時會ETL,因為輸入陣列已經排好順序,所以能肯定的是最小和的一定是個拿第一個值,接著再從第一個值去推,nums1維持原狀nums2往下拿,或是nums1往下拿nums2維持原狀,以此類推直到拿到前k個值最小的。
拿到重複的怎辦
use std::collections::{BinaryHeap,HashSet};
use std::cmp::Reverse;
impl Solution {
pub fn k_smallest_pairs(nums1: Vec<i32>, nums2: Vec<i32>, k: i32) -> Vec<Vec<i32>> {
let mut heap = BinaryHeap::new();
let mut hash_set = HashSet::new();
let mut ans = Vec::new();
heap.push((Reverse(nums1[0]+nums2[0]),0,0));
while(!heap.is_empty()){
let Some((_,n1,n2)) = heap.pop() else {todo!()};
ans.push(vec!(nums1[n1],nums2[n2]));
if(ans.len()>=k as usize) {
break
}
if(n1 as usize ==nums1.len()-1 && n2 as usize ==nums2.len()-1){
continue
}
if(n1 as usize >=nums1.len()-1){
if(!hash_set.contains(&(n1,n2+1))){
heap.push((Reverse(nums1[n1 as usize]+nums2[(n2+1) as usize]),n1,n2+1));
hash_set.insert((n1,n2+1));
}
} else if(n2 as usize >=nums2.len()-1){
if(!hash_set.contains(&(n1+1,n2))){
heap.push((Reverse(nums1[(n1+1) as usize]+nums2[n2 as usize]),n1+1,n2));
hash_set.insert((n1+1,n2));
}
} else {
if(!hash_set.contains(&(n1+1,n2))){
heap.push((Reverse(nums1[(n1+1) as usize]+nums2[n2 as usize]),n1+1,n2));
hash_set.insert((n1+1,n2));
}
if(!hash_set.contains(&(n1,n2+1))){
heap.push((Reverse(nums1[n1 as usize]+nums2[(n2+1) as usize]),n1,n2+1));
hash_set.insert((n1,n2+1));
}
}
}
ans
}
}
解法優化:
use std::collections::BinaryHeap;
use std::cmp::Reverse;
impl Solution {
pub fn k_smallest_pairs(nums1: Vec<i32>, nums2: Vec<i32>, k: i32) -> Vec<Vec<i32>> {
let chose = (nums1.len() as i32).min(k); //juse take k pairs
let mut heap = BinaryHeap::new();
let mut ans = Vec::new();
for i in 0..chose as usize {
heap.push((Reverse(nums1[i]+nums2[0]),i as usize,0 as usize))
}
while(ans.len()< k as usize){
let Some((_,n1,n2)) = heap.pop() else {todo!()};
ans.push(vec!(nums1[n1],nums2[n2]));
if n2+1<nums2.len() {
heap.push((Reverse(nums1[n1]+nums2[n2+1]),n1,n2+1));
}
}
ans
}
}
時間複雜度:O(klogk)
題目:給一連串代號A、B、C…Z的任務,並設定一個區間n,在區間n內不能執行重複任務,最短能在幾個區間內執行完成
輸入:tasks = ["A","A","A","B","B","B"], n = 2
輸出:8 因為A→B→idle→A→B→idle→A→B
限制:
1 <= tasks.length <= 10^4
tasks[i]
is an uppercase English letter.0 <= n <= 100
priority queue 解法:
use std::collections::{BinaryHeap,HashMap};
use std::cmp::Reverse;
impl Solution {
pub fn least_interval(tasks: Vec<char>, n: i32) -> i32 {
//get freq
let mut cnt = [0;26];
for &c in &tasks {
cnt[(c as u8 -b'A') as usize]+=1;
}
let mut heap = BinaryHeap::new();
for (idx,freq) in cnt.into_iter().enumerate() {
if(freq>0){
heap.push((freq,Reverse(idx)));
}
}
let mut ans =0;
while(heap.len()>0){
let mut count = n +1 ;
let mut temp = Vec::new();
while(count>0){
if let Some((freq,Reverse(c))) = heap.pop() {
ans+=1;
if(freq-1>0){
temp.push((freq-1,Reverse(c)));
}
count-=1;
} else {
if(temp.len()==0){
break
}
ans+=1;
count-=1;
}
}
for element in temp.into_iter() {
heap.push(element);
}
}
ans
}
}
時間複雜度:O(mlogk)
1.Priority Queue 多來源、多任務題型練習