Open azl397985856 opened 2 years ago
Heap
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
if not tasks:
return []
t = [[tasks[i][0], tasks[i][1], i] for i in range(len(tasks))]
t.sort(reverse = True)
heap = []
ans = []
cur = t[-1][0]
while t:
while t and cur>=t[-1][0]:
start,process, index = t.pop()
heapq.heappush(heap,[process, index])
if t and cur < t[-1][0] and not heap:
cur = t[-1][0]
continue
time, i = heapq.heappop(heap)
ans.append(i)
cur += time
while heap:
ans.append(heapq.heappop(heap)[1])
return ans
Time: O(NlogN) Space: O(N)
class Solution {
public:
vector<int> getOrder(vector<vector<int>>& tasks) {
long time = 0;
int n = tasks.size(), idx = 0;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> waitingTasks;
// append the original order/index to each task to avoid getting a wrong order of result
for (int i = 0; i < n; i++) tasks[i].push_back(i);
// sort tasks by enqueueTime in ascending order
sort(tasks.begin(), tasks.end());
vector<int> res;
// when we still have tasks left
while (idx < n || !waitingTasks.empty()) {
// if we don't have any waiting task, we need to update time
if (waitingTasks.empty()) time = max(time, (long)tasks[idx][0]);
// the task is earlier than time, it needs to line up
while (idx < n && tasks[idx][0] <= time) {
waitingTasks.push(make_pair(tasks[idx][1], tasks[idx][2]));
idx++;
}
pair<int, int> task = waitingTasks.top();
waitingTasks.pop();
time += task.first;
res.push_back(task.second);
}
return res;
}
};
O(nlogn) O(n)
Thoughts
we should deal with the first enqueued task first, but when a new task come, we should consider the remaining task time and the new task time
Code
public int[] getOrder(int[][] tasks) {
int n = tasks.length;
int[][] newTasks = new int[n][3];
for(int i = 0; i < n; i++) {
newTasks[i][0] = tasks[i][0];
newTasks[i][1] = tasks[i][1];
newTasks[i][2] = i;
}
Arrays.sort(newTasks, (o1, o2) -> o1[0] - o2[0]);
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((o1, o2) ->
(o1[1] != o2[1] ? o1[1] - o2[1] : o1[2] - o2[2]));
int time = 0, resIdx = 0, enqIdx = 0;
int[] res = new int[n];
while (resIdx < n) {
while (enqIdx < n && newTasks[enqIdx][0] <= time) {
pq.offer(newTasks[enqIdx++]);
}
if (pq.isEmpty()) {
time = newTasks[enqIdx][0];
continue;
}
int[] nextJob = pq.poll();
res[resIdx++] = nextJob[2];
time += nextJob[1];
}
return res;
}
Complexity Time: O(nlogn), O(nlogn) for quick sort, and priorityqueue poeration for O(logn) Space: O(n)
Problem Link
Ideas
tasks[pos][0] <= time
and while
and pos+=1
to push all the satisfied tasks. We only need to record the duration and task index.O(NlogN)
, each heap operation is O(logN)
Complexity: hash table and bucket
Code
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
#we add task index to the tasks. Now it is start time, task index, duration
tasks.sort()
#sort by start time
backlog = []
ans = []
time = 0
pos = 0
#we use pos to iterate through all the tasks
for _ in tasks:
if not backlog:
#if no tasks in the backlog, fast forward to the next task's start time using pos
time = max(time, tasks[pos][0])
while pos < len(tasks) and tasks[pos][0] <= time:
#push all the tasks starting before current time to the backlog, using heap
heapq.heappush(backlog, (tasks[pos][2], tasks[pos][1])) #only push duration and task index
pos += 1
d, j = heapq.heappop(backlog)
time += d
ans.append(j)
return ans
Simulation
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
sorted_tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
sorted_tasks.sort()
res = []
h = []
time = 0
pos = 0
for _ in sorted_tasks:
if not h:
time = max(time, sorted_tasks[pos][0])
while(pos<n and sorted_tasks[pos][0]<=time):
heapq.heappush(h,[sorted_tasks[pos][2],sorted_tasks[pos][1] ])
pos+=1
pt, idx = heapq.heappop(h)
time+=pt
res.append(idx)
return res
Space: O(n) Time: O(nlog(n))
将任务开始时间,索引,结束时间三元组组成一个列表,进行排序后,可以选择出最先开始执行的任务
然后使用持续时间,索引作为二元组,然后变成一个堆,就可以按照执行时间长短进行排序
Python3 Code:
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
"""
根据两条标准,1. CPU选择队列中执行时间最短的任务开始执行 2. 一旦某项任务开始执行,CPU在执行完整个任务前不会停止
"""
# 使用堆+模拟
tasks = [(task[0],i,task[1]) for i,task in enumerate(tasks)]
tasks.sort()
time = 0 # 记录当前时间
pos = 0 # 记录当前任务索引位置
ans =[] # 返回结果集合
backlog = [] # 还未执行的任务列表
for _ in tasks:
# 如果没有未执行的任务 更新当前时间
if not backlog:
time = max(time,tasks[pos][0])
# 将那些还未执行的任务 加入最小堆
while pos < len(tasks) and tasks[pos][0] <= time:
heapq.heappush(backlog,(tasks[pos][2],tasks[pos][1]))
pos += 1
d,j = heapq.heappop(backlog)
time += d
ans.append(j)
return ans
复杂度分析
令 n 为数组长度。
class Solution {
public:
struct Task {
int id;
long start_time;
int duration;
};
struct cmp_by_start_time {
bool operator() (Task &a, Task &b) {
return a.start_time > b.start_time;
}
};
struct cmp_by_duration_id {
bool operator() (Task &a, Task &b) {
return a.duration != b.duration ? a.duration > b.duration : a.id > b.id;
}
};
vector<int> getOrder(vector<vector<int>>& tasks) {
priority_queue<Task, vector<Task>, cmp_by_start_time> candidate_tasks;
priority_queue<Task, vector<Task>, cmp_by_duration_id> ready_tasks;
for(int i = 0; i < tasks.size(); i ++)
candidate_tasks.push(Task{i, tasks[i][0], tasks[i][1]});
long cur_time = 0;
vector<int> res;
while(res.size() < tasks.size()) {
if(ready_tasks.empty())
cur_time = max(cur_time, candidate_tasks.top().start_time);
while(!candidate_tasks.empty() && candidate_tasks.top().start_time <= cur_time) {
ready_tasks.push(candidate_tasks.top());
candidate_tasks.pop();
}
Task task_to_process = ready_tasks.top(); //去除当前需要处理的task
ready_tasks.pop();
res.push_back(task_to_process.id);
cur_time += task_to_process.duration; //更新时间
}
return res;
}
};
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks=[(task[0],i,task[1]) for i,task in enumerate(tasks)]
tasks.sort() # 按照进入队列的时间进行排序
backlog=[]
time=0
ans=[]
pos=0
for _ in tasks:
if not backlog:
time=max(time,tasks[pos][0])
while pos<len(tasks) and tasks[pos][0]<=time:
heapq.heappush(backlog,(tasks[pos][2],tasks[pos][1]))
pos+=1
d,j=heapq.heappop(backlog)
time+=d
ans.append(j)
return ans
时间复杂度:O(nlogn)
空间复杂苏:O(n)
模拟,用两个堆来排序任务的开始时间,用另一个堆模拟等待队列。
class Solution {
public int[] getOrder(int[][] tasks) {
PriorityQueue<int[]> waitQ = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
// 重新改写数组,o1[0]表示下标,o1[1]表示执行时间
if (o1[1] != o2[1]) {
return o1[1] - o2[1];
} else {
return o1[0] - o2[0];
}
}
});
PriorityQueue<int[]> timeQ = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
int time = 0;
int idnex = 0;
int[] res = new int[tasks.length];
for (int i = 0; i < tasks.length; i++) {
int[] t = new int[]{tasks[i][0], i};
timeQ.offer(t);
}
while (idnex < tasks.length) {
while (!timeQ.isEmpty() && timeQ.peek()[0] <= time) {
int[] t = timeQ.poll();
waitQ.offer(new int[]{t[1], tasks[t[1]][1]});
}
if (waitQ.isEmpty() && !timeQ.isEmpty()) {
time = timeQ.peek()[0];
}
if (!waitQ.isEmpty()) {
int[] preToDo = waitQ.poll();
res[idnex++] = preToDo[0];
// System.out.println("taskId: " + preToDo[0] + " time: " + time);
time += preToDo[1];
}
}
return res;
}
}
模拟题,用堆完成就好
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
tasks.sort()
backlog = []
time = 0
ans = []
pos = 0
for _ in tasks:
if not backlog:
time = max(time, tasks[pos][0])
while pos < len(tasks) and tasks[pos][0] <= time:
heapq.heappush(backlog, (tasks[pos][2], tasks[pos][1]))
pos += 1
d, j = heapq.heappop(backlog)
time += d
ans.append(j)
return ans
sort + priorityqueue
class Solution {
public int[] getOrder(int[][] tasks) {
if (tasks == null || tasks.length == 0) return null;
int n = tasks.length;
int[][] ts = new int[n][3];
int[] ans = new int[n];
//ts{a,b,c} a:enqueuetime b:precesstime c:index
for (int i = 0; i < n; i++) {
ts[i] = new int[]{tasks[i][0], tasks[i][1], i};
}
Arrays.sort(ts, (a, b) -> (a[0] - b[0]));
//sort by process time (shortest)
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> (a[1] == b[1] ? a[2] - b[2] : a[1] - b[1]));
int tasktime = ts[0][0];
int idx = 0, i = 0;
while(idx < n) {
while (i < n && ts[i][0] <= tasktime) {
pq.add(ts[i++]);
}
//empty -> go to next task
if (pq.isEmpty()) {
tasktime = ts[i][0];
continue;
}
//update tasktime and result
int[] cur = pq.poll();
ans[idx++] = cur[2];
tasktime += cur[1];
}
return ans;
}
}
time=O(nlogn) space=O(n)
var getOrder = function (tasks) {
const queue = new MinPriorityQueue()
tasks = tasks.map((task, index) => ({
index,
start: task[0],
time: task[1]
}))
tasks.sort((a, b) => b.start - a.start)
const answer = []
let time = 0
while (tasks.length > 0 || !queue.isEmpty()) {
if (queue.isEmpty() && tasks[tasks.length - 1].start > time) {
time = tasks[tasks.length - 1].start
}
while (tasks.length > 0) {
if (tasks[tasks.length -1].start <= time) {
const task = tasks.pop()
queue.enqueue(task, task.time * 100000 + task.index)
} else {
break
}
}
const { element: task } = queue.dequeue()
time += task.time
answer.push(task.index)
}
return answer
}
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
tasks.sort()
ans = []
will_pro = []
time, pro = 0, 0
for _ in tasks:
if not will_pro:
#time = task[pro][0]
time = max(time, tasks[pro][0])
while pro < len(tasks) and tasks[pro][0] <= time:
heapq.heappush(will_pro, (tasks[pro][2], tasks[pro][1]))
pro += 1
en, i = heapq.heappop(will_pro)
ans.append(i)
time += en
return ans
使用两个优先队列,分别入队时间排序,和处理时间排序
class Solution {
public:
vector<int> getOrder(vector<vector<int>>& tasks) {
//时间戳
long long timestamp=0;
//两个优先队列,因为指定了第三个参数,因此第二个参数也需要指定,不能默认了
priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> entry,ready;
//先将entry队列进行排序,分别是入队时间,编号
for(int i=0;i<tasks.size();i++){
entry.push(make_pair(tasks[i][0],i));
}
vector<int> res;
while(!entry.empty() || !ready.empty()){
if(ready.empty()){
timestamp=entry.top().first;
while(!entry.empty() && entry.top().first==timestamp){
int index=entry.top().second;
ready.push(make_pair(tasks[index][1],index));
entry.pop();
}
}
res.emplace_back(ready.top().second);
timestamp+=ready.top().first;
ready.pop();
//时间戳此时更新,需要更新entry顺序
while(!entry.empty() && entry.top().first<=timestamp){
int index=entry.top().second;
ready.push(make_pair(tasks[index][1],index));
entry.pop();
}
}
return res;
}
};
3 level sort: sort by start time, sort by time using, sort by index Track the timestamp, and put all available task (start time < current timestamp) into pq
class Solution {
public int[] getOrder(int[][] tasks) {
int n = tasks.length;
int[][] taskIdx = new int[n][3];
for (int i = 0; i < n; i++) {
taskIdx[i][0] = tasks[i][0]; // start time
taskIdx[i][1] = tasks[i][1]; // time using
taskIdx[i][2] = i; // index
}
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[1] == b[1] ? a[2] - b[2] : a[1] - b[1]);
Arrays.sort(taskIdx, (a, b) -> a[0] - b[0]);
int time = 0;
int done = 0;
int i = 0;
int[] res = new int[n];
while (done < n) {
if (pq.isEmpty()) {
time = Math.max(time, taskIdx[i][0]);
}
while (i < n && taskIdx[i][0] <= time) {
pq.offer(taskIdx[i++]);
}
int[] curr = pq.poll();
res[done++] = curr[2];
time += curr[1];
}
return res;
}
}
Time: O(nlogn) Space: O(n)
class Solution {
public int[] getOrder(int[][] ts) {
int n = ts.length;
// 将 ts 转存成 nts,保留任务编号
int[][] nts = new int[n][3];
for (int i = 0; i < n; i++) nts[i] = new int[]{ts[i][0], ts[i][1], i};
// 根据任务入队时间进行排序
Arrays.sort(nts, (a,b)->a[0]-b[0]);
// 根据题意,先按照「持续时间」排序,再根据「任务编号」排序
PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->{
if (a[1] != b[1]) return a[1] - b[1];
return a[2] - b[2];
});
int[] ans = new int[n];
for (int time = 1, j = 0, idx = 0; idx < n; ) {
// 如果当前任务可以添加到「队列」中(满足入队时间)则进行入队
while (j < n && nts[j][0] <= time) q.add(nts[j++]);
if (q.isEmpty()) {
// 如果当前「队列」没有任务,直接跳到下个任务的入队时间
time = nts[j][0];
} else {
// 如果有可执行任务的话,根据优先级将任务出队(记录下标),并跳到该任务完成时间点
int[] cur = q.poll();
ans[idx++] = cur[2];
time += cur[1];
}
}
return ans;
}
}
使用堆
···cpp
class Solution {
public:
static bool comp(pair<int,vector
vector<int> getOrder(vector<vector<int>>& tasks) {
priority_queue<pair<int,vector<int>>,vector<pair<int,vector<int>>>,decltype(&comp)> Q(comp);
int size = tasks.size();
long long time = 0;
//将进入时间最早的先入队列
vector<pair<int,vector<int>>> _tasks;//为了在入队时区别自己
for(int i = 0; i< size; i++)
{
_tasks.push_back({i,tasks[i]});
}
sort(_tasks.begin(),_tasks.end(),[&](pair<int,vector<int>> i, pair<int,vector<int>> j) {
return i.second[0] < j.second[0];
});
vector<int> result;
int ptr = 0;
for(int i = 0; i< tasks.size(); i++)//确保不会多次入队
{
if(Q.empty())
{
time = max(time,(long long)_tasks[i].second[0]);
}
while(ptr< size &&_tasks[ptr].second[0] <= time)
{
Q.emplace(_tasks[ptr]);
ptr++;
}
pair<int,vector<int>> p = Q.top();
Q.pop();
time += p.second[1];
result.push_back(p.first);
}
return result;
} }; ···
使用数组按照 enqueueTime 从小到大记录tasks的index;使用最小堆选择处理时间最小的数组;维护一个timestamp,用于判断哪些数据入堆
class minHeap{
constructor() {
this.data = [];
}
compare(i, j) {
if (this.data[i] && this.data[j]) {
if (this.data[i][0] == this.data[j][0]) {
return this.data[i][1] > this.data[j][1];
} else {
return this.data[i][0] > this.data[j][0];
}
}
}
offer(val) {
this.data.push(val);
this.bubbleUp(this.size() - 1);
}
poll() {
const result = this.data[0];
const last = this.data.pop();
if (this.size() !== 0) {
this.data[0] = last;
this.bubbleDown(0);
}
return result;
}
bubbleUp(i) {
while (i > 0) {
const parent = Math.floor((i - 1) / 2);
if (this.compare(parent, i)) {
this.swap(parent, i);
i = parent;
} else {
break;
}
}
}
bubbleDown(i) {
while (true) {
const left = 2 * i + 1;
const right = 2 * i + 2;
let min = i;
if (i < this.size() && this.data[left] && this.compare(min, left)) {
min = left;
}
if (i < this.size() && this.data[right] && this.compare(min, right)) {
min = right;
}
if (min !== i) {
this.swap(i, min);
i = min;
} else {
break;
}
}
}
size() {
return this.data.length;
}
swap(i, j) {
const temp = this.data[i];
this.data[i] = this.data[j];
this.data[j] = temp;
}
}
var getOrder = function(tasks) {
const n = tasks.length;
const indices = tasks.map((task, index) => ({
index,
start: task[0]
})).sort((a, b) => a.start - b.start).map((val) => val.index);
const heap = new minHeap();
let timestamp = 0;
let ptr = 0;
let ans = [];
for (let i = 0; i < n; i++) {
if (heap.data.length == 0) {
timestamp = Math.max(timestamp, tasks[indices[ptr]][0]);
}
while(ptr < n && tasks[indices[ptr]][0] <= timestamp) {
heap.offer([tasks[indices[ptr]][1], indices[ptr]]);
ptr++;
}
const [process, index] = heap.poll();
timestamp += process;
ans.push(index);
}
return ans;
};
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
idx = list(range(n))
# sort index by enqueue time
idx.sort(key=lambda x: tasks[x][0])
ans = []
queue = []
time = 0
ptr = 0
for i in range(n):
if not queue:
time = max(time, tasks[idx[ptr]][0])
while ptr < n and tasks[idx[ptr]][0] <= time:
heapq.heappush(queue, (tasks[idx[ptr]][1], idx[ptr]))
ptr += 1
p, i = heapq.heappop(queue)
time += p
ans.append(i)
return ans
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
ans = []
# 这里我们要记住原来的index
tasks = sorted([(t[0],t[1],i) for i, t in enumerate(tasks)] )
i = 0
h = []
# 用curtime表示当前时间
curtime = tasks[0][0]
while len(ans) < len(tasks):
# 只要是enque time都小于curtime的,都heappush进去
while (i<len(tasks) and tasks[i][0] <= curtime):
heapq.heappush(h,(tasks[i][1],tasks[i][2]))
i += 1
if h:
# pop 出任务进行process,这里我们不需要关心enque time,只需要proces time
t_diff,original_index = heapq.heappop(h)
curtime += t_diff
ans.append(original_index)
# when CPU is idle, set the current time to the next push task enqueue time
elif i<len(tasks):
curtime = tasks[i][0]
return ans
时间:Onlogn 空间:On
Day33
1、动态求极值,构建一个堆
2、初始化堆,第一优先级执行时间,第二优先级任务索引
3、不断添加任务到堆中
4、记录已执行的任务并增加当前时间
5、返回记录的任务序列
var getOrder = function(tasks) {
// 为每个 task 添加一个索引属性并按照时长排序排序
tasks.forEach((v, i) => v.push(i));
tasks.sort((a, b) => b[0] - a[0]);
// 初始化小根堆/优先队列
const heap = new PriorityQueue(function(x, y) {
// 第一优先级为执行时间,第二优先级为任务索引
return this.A[x][1] !== this.A[y][1]
? this.A[x][1] < this.A[y][1]
: this.A[x][2] < this.A[y][2]
});
const ans = [];
// CPU 时间:初始化为首个任务的开始时间
let time = tasks[tasks.length - 1][0];
// 保证执行完所有任务
while (heap.size() || tasks.length) {
// 取出任务队列中优先级最高的任务
if (!heap.isEmpty()) {
const task = heap.dequeue();
ans.push(task[2]); // 记录结果
time += task[1]; // 任务执行完毕,递增 CPU 时间
}
// 添加任务到任务队列
addTask(heap, tasks, time);
// 边界:任务队列为空(剩余任务的入队时间跨度太大,导致上一步未能添加任务)
if (heap.isEmpty() && tasks.length) {
// 将 CPU 时间快进到下一个任务,并添加任务到任务队列
time = tasks[tasks.length - 1][0];
addTask(heap, tasks, time);
}
}
return ans;
};
function addTask(heap, tasks, time) {
// 将 tasks 中入队时间不快于当前 CPU 时间的任务入队
while (tasks.length && tasks[tasks.length - 1][0] <= time) {
heap.enqueue(tasks.pop());
}
}
class Heap {
constructor(compare) {
this.A = [];
this.compare = compare;
}
size() {
return this.A.length;
}
left(i) {
return 2 * i + 1;
}
right(i) {
return 2 * i + 2;
}
parent(i) {
return i > 0 ? (i - 1) >>> 1 : -1;
}
isEmpty() {
return this.size() === 0;
}
peek() {
return this.A[0];
}
heapifyDown(i) {
let p = i;
const l = this.left(i),
r = this.right(i),
size = this.size();
if (l < size && this.compare(l, p)) p = l;
if (r < size && this.compare(r, p)) p = r;
if (p !== i) {
this.exchange(i, p);
this.heapifyDown(p);
}
}
heapifyUp(i) {
const p = this.parent(i);
if (p >= 0 && this.compare(i, p)) {
this.exchange(i, p);
this.heapifyUp(p);
}
}
exchange(x, y) {
const temp = this.A[x];
this.A[x] = this.A[y];
this.A[y] = temp;
}
compare() {
throw new Error('Must be implement!');
}
}
class PriorityQueue extends Heap {
constructor(compare) {
super(compare);
}
enqueue(node) {
this.A.push(node);
this.heapifyUp(this.size() - 1);
}
dequeue() {
const first = this.A[0];
const last = this.A.pop();
if (first !== last) {
this.A[0] = last;
this.heapifyDown(0);
}
return first;
}
}
时间复杂度:O(nlogn)
空间复杂度:O(n)
class Solution { public int[] getOrder(int[][] tasks) { int len = tasks.length; // 建堆 执行完获取数组中小于等于的入堆并重新poll执行 Queue<int[]> q = new PriorityQueue<>((a,b) -> { if(a[1] == b[1]) return a[2] - b[2]; // 否则下标小的先出队 return a[1] - b[1]; // 执行时间小的先出队 }); for (int i = 0; i < len; i++) { tasks[i] = new int[]{tasks[i][0],tasks[i][1], i}; } // 排序后保留原来的下标 Arrays.sort(tasks, Comparator.comparingInt(a -> a[0])); int[] res = new int[len]; int idx = 0, i = 0, curTime = tasks[0][0]; while(i < len && tasks[i][0] <= curTime) { q.offer(tasks[i]); i++; } while(!q.isEmpty()) { int[] poll = q.poll(); curTime += poll[1]; if(i < len && curTime < tasks[i][0] && q.isEmpty()) { // 上一个任务完成了 但是没到下一个任务开始时间 队列也是空的了 直接跳过去加进来 这里是难点 curTime = tasks[i][0]; } res[idx++] = poll[2]; while(i < len && tasks[i][0] <= curTime) { // 当前时间之前的全部加入排序 用于下一次poll q.offer(tasks[i]); i ++; } } return res; } }
看题解学到的
const getOrder = function (tasks) {
const answer = [];
// 官方自带的优先队列
const queue = new MinPriorityQueue();
tasks = tasks.map((task, index) => ({
index,
start: task[0],
time: task[1],
}));
// 根据任务入队时间排序
tasks.sort((a, b) => b.start - a.start);
let time = 0;
while (tasks.length > 0 || !queue.isEmpty()) {
// 队列为空,且没有到达下一个入队任务的入队时间
// 直接将time设置为下一个入队任务的入队时间
if (queue.isEmpty() && tasks[tasks.length - 1].start > time) {
time = tasks[tasks.length - 1].start;
}
while (tasks.length > 0) {
// 将可以到达入队时间的任务加入队列
if (tasks[tasks.length - 1].start <= time) {
const task = tasks.pop();
// 传入的第二个值表示的是指定当前元素的优先级
queue.enqueue(task, task.time * 100000 + task.index);
} else {
break;
}
}
// 执行任务
const { element: task } = queue.dequeue();
// 将time设置为该任务的结束时间
time += task.time;
answer.push(task.index);
}
return answer;
};
使用堆进行题目的模拟
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
tasks.sort()
backlog = []
time = 0
ans = []
pos = 0
for _ in tasks:
if not backlog:
time = max(time, tasks[pos][0])
while pos < len(tasks) and tasks[pos][0] <= time:
heapq.heappush(backlog, (tasks[pos][2], tasks[pos][1]))
pos += 1
d, j = heapq.heappop(backlog)
time += d
ans.append(j)
return ans
时间 O(nlogn) \ 空间 O(n)
https://leetcode-cn.com/problems/single-threaded-cpu/
给你一个二维数组 tasks ,用于表示 n 项从 0 到 n - 1 编号的任务。其中 tasks[i] = [enqueueTimei, processingTimei] 意味着第 i 项任务将会于 enqueueTimei 时进入任务队列,需要 processingTimei 的时长完成执行。
现有一个单线程 CPU ,同一时间只能执行 最多一项 任务,该 CPU 将会按照下述方式运行:
如果 CPU 空闲,且任务队列中没有需要执行的任务,则 CPU 保持空闲状态。
如果 CPU 空闲,但任务队列中有需要执行的任务,则 CPU 将会选择 执行时间最短 的任务开始执行。如果多个任务具有同样的最短执行时间,则选择下标最小的任务开始执行。
一旦某项任务开始执行,CPU 在 执行完整个任务 前都不会停止。
CPU 可以在完成一项任务后,立即开始执行一项新任务。
返回 CPU 处理任务的顺序。
示例 1:
输入:tasks = [[1,2],[2,4],[3,2],[4,1]]
输出:[0,2,3,1]
解释:事件按下述流程运行:
- time = 1 ,任务 0 进入任务队列,可执行任务项 = {0}
- 同样在 time = 1 ,空闲状态的 CPU 开始执行任务 0 ,可执行任务项 = {}
- time = 2 ,任务 1 进入任务队列,可执行任务项 = {1}
- time = 3 ,任务 2 进入任务队列,可执行任务项 = {1, 2}
- 同样在 time = 3 ,CPU 完成任务 0 并开始执行队列中用时最短的任务 2 ,可执行任务项 = {1}
- time = 4 ,任务 3 进入任务队列,可执行任务项 = {1, 3}
- time = 5 ,CPU 完成任务 2 并开始执行队列中用时最短的任务 3 ,可执行任务项 = {1}
- time = 6 ,CPU 完成任务 3 并开始执行任务 1 ,可执行任务项 = {}
- time = 10 ,CPU 完成任务 1 并进入空闲状态
示例 2:
输入:tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]]
输出:[4,3,2,0,1]
解释:事件按下述流程运行:
- time = 7 ,所有任务同时进入任务队列,可执行任务项 = {0,1,2,3,4}
- 同样在 time = 7 ,空闲状态的 CPU 开始执行任务 4 ,可执行任务项 = {0,1,2,3}
- time = 9 ,CPU 完成任务 4 并开始执行任务 3 ,可执行任务项 = {0,1,2}
- time = 13 ,CPU 完成任务 3 并开始执行任务 2 ,可执行任务项 = {0,1}
- time = 18 ,CPU 完成任务 2 并开始执行任务 0 ,可执行任务项 = {1}
- time = 28 ,CPU 完成任务 0 并开始执行任务 1 ,可执行任务项 = {}
- time = 40 ,CPU 完成任务 1 并进入空闲状态
提示:
tasks.length == n
1 <= n <= 105
1 <= enqueueTimei, processingTimei <= 109
JavaScript Code:
/**
* @param {number[][]} tasks
* @return {number[]}
*/
class Heap {
constructor(compare) {
this.A = [];
this.compare = compare;
}
size() {
return this.A.length;
}
left(i) {
return 2 * i + 1;
}
right(i) {
return 2 * i + 2;
}
parent(i) {
return i > 0 ? (i - 1) >>> 1 : -1;
}
isEmpty() {
return this.size() === 0;
}
peek() {
return this.A[0];
}
heapifyDown(i) {
let p = i;
const l = this.left(i),
r = this.right(i),
size = this.size();
if (l < size && this.compare(l, p)) p = l;
if (r < size && this.compare(r, p)) p = r;
if (p !== i) {
this.exchange(i, p);
this.heapifyDown(p);
}
}
heapifyUp(i) {
const p = this.parent(i);
if (p >= 0 && this.compare(i, p)) {
this.exchange(i, p);
this.heapifyUp(p);
}
}
exchange(x, y) {
const temp = this.A[x];
this.A[x] = this.A[y];
this.A[y] = temp;
}
compare() {
throw new Error('Must be implement!');
}
}
class PriorityQueue extends Heap {
constructor(compare) {
super(compare);
}
enqueue(node) {
this.A.push(node);
this.heapifyUp(this.size() - 1);
}
dequeue() {
const first = this.A[0];
const last = this.A.pop();
if (first !== last) {
this.A[0] = last;
this.heapifyDown(0);
}
return first;
}
}
var getOrder = function(tasks) {
// 为每个 task 添加一个索引属性并排序
tasks.forEach((v, i) => v.push(i));
tasks.sort((a, b) => b[0] - a[0]);
// 初始化小根堆/优先队列
const heap = new PriorityQueue(function(x, y) {
// 第一优先级为执行时间,第二优先级为任务索引
return this.A[x][1] !== this.A[y][1]
? this.A[x][1] < this.A[y][1]
: this.A[x][2] < this.A[y][2]
});
const ans = [];
// CPU 时间:初始化为首个任务的开始时间
let time = tasks[tasks.length - 1][0];
// 保证执行完所有任务
while (heap.size() || tasks.length) {
// 取出任务队列中优先级最高的任务
if (!heap.isEmpty()) {
const task = heap.dequeue();
ans.push(task[2]); // 记录结果
time += task[1]; // 任务执行完毕,递增 CPU 时间
}
// 添加任务到任务队列
addTask(heap, tasks, time);
// 边界:任务队列为空(剩余任务的入队时间跨度太大,导致上一步未能添加任务)
if (heap.isEmpty() && tasks.length) {
// 将 CPU 时间快进到下一个任务,并添加任务到任务队列
time = tasks[tasks.length - 1][0];
addTask(heap, tasks, time);
}
}
return ans;
};
function addTask(heap, tasks, time) {
// 将 tasks 中入队时间不快于当前 CPU 时间的任务入队
while (tasks.length && tasks[tasks.length - 1][0] <= time) {
heap.enqueue(tasks.pop());
}
}
Sort + PriorityQueue
class Solution {
public int[] getOrder(int[][] tasks) {
int n = tasks.length;
List<Integer> idx = new ArrayList<>();
for (int i = 0; i < n; i++) {
idx.add(i);
}
idx.sort(Comparator.comparingInt(o -> tasks[o][0]));
Queue<int[]> pq = new PriorityQueue<>((o1, o2) -> {
if (o1[0] != o2[0]) return Integer.compare(o1[0], o2[0]);
return Integer.compare(o1[1], o2[1]);
});
int[] res = new int[n];
int k = 0, time = 0;
for (int i = 0; i < n; i++) {
if (pq.isEmpty())
time = Math.max(time, tasks[idx.get(k)][0]);
while (k < n && tasks[idx.get(k)][0] <= time) {
pq.offer(new int[]{tasks[idx.get(k)][1], idx.get(k)});
k++;
}
int[] t = pq.poll();
time += t[0];
res[i] = t[1];
}
return res;
}
}
Complexity Analysis
主要思路是要使用堆结构进行排序,小根堆,每次弹出最小的。然后为了方便一开始要进行排序,但是要注意保持原来的数组的下标。
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
tasks.sort()
curtime = tasks[0][0]
ans = []
heap = []
i = 0
while i < len(tasks):
while i<len(tasks) and tasks[i][0] <= curtime:
task = (tasks[i][2], tasks[i][1])
heapq.heappush(heap, task)
i += 1
cur = heapq.heappop(heap)
curtime += cur[0]
ans.append(cur[1])
if i<len(tasks) and not heap:
curtime = max(tasks[i][0], curtime)
while heap:
cur = heapq.heappop(heap)
ans.append(cur[1])
return ans
时间复杂度Onlogn 空间复杂度On
单线程 https://leetcode-cn.com/problems/single-threaded-cpu/submissions/
class Solution {
public:
using P = pair<int,int>;
vector<int> getOrder(vector<vector<int>>& tasks) {
vector<int> res;
auto comp = [&](const P& a, const P& b){
if(a.first != b.first) return a.first > b.first;
else return a.second > b.second;
};
priority_queue<P,vector<P>,decltype(comp)> pq(comp);
int cnt = 0, n = tasks.size(), idx = 0, i = 0;
long long t = 1;
for(auto& vec: tasks){
vec.push_back(i++);
}
sort(tasks.begin(),tasks.end());
while(cnt < n){
bool exec = false;
while(idx < n){
if(tasks[idx][0] <= t){
exec = true;
pq.push(P(tasks[idx][1],tasks[idx][2]));
++idx;
}
else{
if(!exec && pq.empty()){
t = tasks[idx][0];
}
break;
}
}
if(pq.empty()) continue;
auto curr = pq.top();
pq.pop();
res.push_back(curr.second);
t += curr.first;
++cnt;
}
return res;
}
};
时间复杂度:O(nlogn) 空间复杂度:O(n)
class Solution {
public:
vector<int> getOrder(vector<vector<int>>& tasks) {
//两个优先队列,taskq和readyq,taskq用enqueTime排序,ready用processTime排序
//所有任务进入taskq,到了处理时间的进入readyq。
priority_queue<pair<int,int>, vector<pair<int, int>>, greater<> > taskq;
priority_queue<pair<int,int>, vector<pair<int, int>>, greater<> > readyq;
//tasks共有n项任务
int n=tasks.size();
//将tasks中所有的第一个元素放入taskq的容器中,taskq=[[1,0],[2,1],[3,2],[4,3]],
for(int i=0; i < n; i++){
taskq.emplace(tasks[i][0], i);
}
vector<int> ans;
//时间戳变量
long ts=0;
for(int i = 0; i < n; i++){
if(readyq.empty() && ts<taskq.top().first){
ts = taskq.top().first;
}
//将所有<=ts的任务放入readyq队列
while(!taskq.empty() && taskq.top().first <= ts){
int i =taskq.top().second;
taskq.pop();
readyq.emplace(tasks[i][1] , i);
}
//选择处理时间最小的任务 auto&& 右值引用,没看懂
auto&& [t, index]=readyq.top();
ts+=t;
ans.push_back(index);
readyq.pop();
}
return ans;
}
};
class Solution {
public int[] getOrder(int[][] tasks) {
int n = tasks.length;
// 把原始索引也添加上,方便后面排序用
ArrayList<int[]> triples = new ArrayList<>();
for (int i = 0; i < tasks.length; i++) {
triples.add(new int[]{tasks[i][0], tasks[i][1], i});
}
// 数组先按照任务的开始时间排序
triples.sort((a, b) -> {
return a[0] - b[0];
});
// 按照任务的处理时间排序,如果处理时间相同,按照原始索引排序
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> {
if (a[1] != b[1]) {
// 比较处理时间
return a[1] - b[1];
}
// 比较原始索引
return a[2] - b[2];
});
ArrayList<Integer> res = new ArrayList<>();
// 记录完成任务的时间线
int now = 0;
int i = 0;
while (res.size() < n) {
if (!pq.isEmpty()) {
// 完成队列中的一个任务
int[] triple = pq.poll();
res.add(triple[2]);
// 每完成一个任务,就要推进时间线
now += triple[1];
} else if (i < n && triples.get(i)[0] > now) {
// 队列为空可能因为还没到开始时间,
// 直接把时间线推进到最近任务的开始时间
now = triples.get(i)[0];
}
// 由于时间线的推进,会产生可以开始执行的任务
for (; i < n && triples.get(i)[0] <= now; i++) {
pq.offer(triples.get(i));
}
}
// Java 语言特性,将 List 转化成 int[] 格式
int[] arr = new int[n];
for (int j = 0; j < n; j++) {
arr[j] = res.get(j);
}
return arr;
}
}
堆。先将index添加到tasks中,再将tasks按enqueueTime排序。遍历tasks,将enqueueTime小于当前时间的任务添加到堆中等待执行;取出堆中第一个task执行,更新执行完之后的当前时间。
var getOrder = function(tasks) {
tasks = tasks.map((val, index) => [...val, index]);
tasks.sort((a, b) => b[0] - a[0]);
let queue = new MinPriorityQueue({priority: (val) => (10 ** 5) * val[1] + val[2]});
let res = [], time = tasks[tasks.length - 1][0];
while (tasks.length > 0 || queue.size() > 0) {
while (tasks.length > 0 && time >= tasks[tasks.length - 1][0]) queue.enqueue(tasks.pop());
if (queue.size() > 0) {
let current = queue.dequeue().element;
time = time + current[1];
res.push(current[2]);
} else if (tasks.length > 0) {
time = tasks[tasks.length - 1][0];
}
}
return res;
};
time: O(nlog(n))
space: O(n)
排序+优先队列
class Solution {
private:
using PII=pair<int,int>;
using LL=long long;
public:
vector<int> getOrder(vector<vector<int>>& tasks) {
int n=tasks.size();
vector<int> indices(n);
iota(indices.begin(),indices.end(),0);
sort(indices.begin(),indices.end(),[&](int i,int j){
return tasks[i][0]<tasks[j][0];
});
vector<int> ans;
priority_queue<PII,vector<PII>,greater<PII>> q;
LL timestamp=0;
int ptr=0;
for(int i=0;i<n;++i){
if(q.empty()) timestamp=max(timestamp,(LL)tasks[indices[ptr]][0]);
while(ptr<n && tasks[indices[ptr]][0]<=timestamp){
q.emplace(tasks[indices[ptr]][1],indices[ptr]);
++ptr;
}
auto&& [process,index]=q.top();
timestamp+=process;
ans.push_back(index);
q.pop();
}
return ans;
}
};
纯模拟. 超时
# 超时
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
start_time = min(i[0] for i in tasks)
finished = 0
start_dic = defaultdict(list) # start_time : idx
for idx, task in enumerate(tasks):
start_dic[task[0]].append(idx)
# print(start_dic)
cur_time = end_time = start_time
queue = [] # indexes
idle = True
res = []
while finished < len(tasks):
# 1. Prepare task queue
queue.extend(start_dic[cur_time])
# 2. If idle.
idle = (cur_time == end_time)
if idle:
queue.sort(key=lambda idx: (tasks[idx][1], idx)) # 相同值按照tasks里面的index排序.
task_idx = queue.pop(0)
res.append(task_idx)
end_time = cur_time + tasks[task_idx][1]
finished += 1
cur_time += 1
return res
# 使用heap
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
indices = list(range(n))
indices.sort(key=lambda x: tasks[x][0])
ans = list()
# 优先队列
q = list()
# 时间戳
timestamp = 0
# 数组上遍历的指针
ptr = 0
for i in range(n):
# 如果没有可以执行的任务,直接快进
if not q:
timestamp = max(timestamp, tasks[indices[ptr]][0])
# 将所有小于等于时间戳的任务放入优先队列
while ptr < n and tasks[indices[ptr]][0] <= timestamp:
heapq.heappush(q, (tasks[indices[ptr]][1], indices[ptr]))
ptr += 1
# 选择处理时间最小的任务
process, index = heapq.heappop(q)
timestamp += process
ans.append(index)
return ans
class Solution {
private:
using PII = pair<int, int>;
using LL = long long;
public:
vector<int> getOrder(vector<vector<int>>& tasks) {
int n = tasks.size();
vector<int> indices(n);
iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(), [&](int i, int j) {
return tasks[i][0] < tasks[j][0];
});
vector<int> ans;
// 优先队列
priority_queue<PII, vector<PII>, greater<PII>> q;
// 时间戳
LL timestamp = 0;
// 数组上遍历的指针
int ptr = 0;
for (int i = 0; i < n; ++i) {
// 如果没有可以执行的任务,直接快进
if (q.empty()) {
timestamp = max(timestamp, (LL)tasks[indices[ptr]][0]);
}
// 将所有小于等于时间戳的任务放入优先队列
while (ptr < n && tasks[indices[ptr]][0] <= timestamp) {
q.emplace(tasks[indices[ptr]][1], indices[ptr]);
++ptr;
}
// 选择处理时间最小的任务
auto&& [process, index] = q.top();
timestamp += process;
ans.push_back(index);
q.pop();
}
return ans;
}
};
堆排序,关键字1是任务执行时间,关键字2任务序号。
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
indices = list(range(n))
indices.sort(key=lambda x: tasks[x][0])
ans = list()
# 优先队列
q = list()
# 时间戳
timestamp = 0
# 数组上遍历的指针
ptr = 0
for i in range(n):
# 如果没有可以执行的任务,直接快进
if not q:
timestamp = max(timestamp, tasks[indices[ptr]][0])
# 将所有小于等于时间戳的任务放入优先队列
while ptr < n and tasks[indices[ptr]][0] <= timestamp:
# 排序的关键字为运行时间优先,其次是任务序号
heapq.heappush(q, (tasks[indices[ptr]][1], indices[ptr]))
ptr += 1
# 选择处理时间最小的任务
process, index = heapq.heappop(q)
timestamp += process
ans.append(index)
return ans
时间复杂度:O(NlogN)
空间复杂度:O(N)
先按照enqueueTime排序,然后遍历任务数组,针对每个任务,执行下述操作:
func getOrder(tasks [][]int) []int {
var ret []int
var newTasks = make([]*taskMeta, 0, len(tasks))
for idx, item := range tasks {
newTasks = append(newTasks, &taskMeta{
idx: idx,
time: item,
})
}
sort.Slice(newTasks, func(i, j int) bool {
if newTasks[i].time[0] < newTasks[j].time[0] {
return true
}
if newTasks[i].time[0] > newTasks[j].time[0] {
return false
}
if i < j {
return true
}
return false
})
taskIdx := 0
curTime := newTasks[0].time[0]
heap := newMyHeap()
for ; taskIdx < len(newTasks); taskIdx++ {
if curTime != newTasks[taskIdx].time[0] {
break
}
heap.push(&taskMeta{idx: newTasks[taskIdx].idx, time: newTasks[taskIdx].time})
}
for heap.len() != 0 {
cur := heap.delete()
ret = append(ret, cur.idx)
curTime += cur.time[1]
found := false
for ; taskIdx < len(newTasks); taskIdx++ {
if newTasks[taskIdx].time[0] <= curTime {
heap.push(&taskMeta{idx: newTasks[taskIdx].idx, time: newTasks[taskIdx].time})
found = true
continue
}
break
}
if !found && taskIdx != len(newTasks) && heap.len() == 0 {
// CPU空闲时间
curTime = newTasks[taskIdx].time[0]
for ; taskIdx < len(newTasks); taskIdx++ {
if curTime != newTasks[taskIdx].time[0] {
break
}
heap.push(&taskMeta{idx: newTasks[taskIdx].idx, time: newTasks[taskIdx].time})
}
}
}
return ret
}
type taskMeta struct {
idx int
time []int
}
type myHeap struct {
data []*taskMeta
}
func newMyHeap() *myHeap {
return &myHeap{data: make([]*taskMeta, 0)}
}
func (h *myHeap) push(v *taskMeta) {
h.data = append(h.data, v)
h.up()
}
func (h *myHeap) delete() *taskMeta {
tmp := h.data[0]
h.swap(0, h.len() - 1)
h.data = h.data[:h.len() - 1]
h.down(0)
return tmp
}
func (h *myHeap) up() {
last := h.len() - 1
for {
parent := h.getParent(last)
if parent == -1 {
break
}
if !h.less(last, parent) {
break
}
h.swap(last, parent)
last = parent
}
}
func (h *myHeap) down(b int) {
var maxChildValue func(idx int) int
maxChildValue = func(idx int) int {
leftChild := h.getLeftChild(idx)
rightChild := h.getRightChild(idx)
if rightChild != -1 && leftChild != -1 {
if h.less(leftChild, rightChild) {
return leftChild
}
return rightChild
}
return leftChild
}
for {
child := maxChildValue(b)
if child == -1 {
break
}
if h.less(b, child) {
break
}
h.swap(b, child)
b = child
}
}
func (h *myHeap) getParent(idx int) int {
if idx == 0 {return -1}
return (idx - 1) / 2
}
func (h *myHeap) getLeftChild(idx int) int {
cIdx := 2 * idx + 1
if cIdx >= h.len() {
return -1
}
return cIdx
}
func (h *myHeap) getRightChild(idx int) int {
cIdx := 2 * idx + 2
if cIdx >= h.len() {
return -1
}
return cIdx
}
func (h *myHeap) len() int {
return len(h.data)
}
func (h *myHeap) swap(i, j int) {
h.data[i], h.data[j] = h.data[j], h.data[i]
}
func (h *myHeap) less(i, j int) bool {
if h.data[i].time[1] < h.data[j].time[1] {
return true
}
if h.data[i].time[1] > h.data[j].time[1] {
return false
}
if h.data[i].idx < h.data[j].idx {
return true
}
return false
}
复杂度分析
class Solution {
public int[] getOrder(int[][] tasks) {
int len = tasks.length;
int[][] tasksWithId = new int[len][3];
int[] res = new int[len];
Queue<int[]> queue = new PriorityQueue<>((a, b) ->
a[1] != b[1] ? a[1] - b[1] : a[2] - b[2]);
for (int i = 0; i < len; i++) {
tasksWithId[i] = new int[]{tasks[i][0], tasks[i][1], i};
}
Arrays.sort(tasksWithId, (a, b) -> a[0] - b[0]);
int time = 0, i = 0, j = 0;
while (j < len) {
while (i < len && tasksWithId[i][0] <= time) {
queue.offer(tasksWithId[i]);
i++;
}
if (queue.isEmpty()) {
time = tasksWithId[i][0];
continue;
}
int[] toDo = queue.poll();
time += toDo[1];
res[j] = toDo[2];
j++;
}
return res;
}
}
先按照起始时间排序,再用大顶堆维护持续时间
import heapq
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i , task[1]) for i,task in enumerate(tasks)]
tasks.sort()
ans = []
backlog = []
pos = 0
time = 0
for _ in tasks:
if not backlog:
time = max(time,tasks[pos][0])
while pos < len(tasks) and tasks[pos][0] <= time :
heapq.heappush(backlog, (tasks[pos][2], tasks[pos][1])) # 默认按持续时长排序,大顶
pos += 1
d,j = heapq.heappop(backlog)
ans.append(j)
time += d
return ans
时间复杂度:O(nlogn) 空间复杂度:O(n)
先将任务的开始时间按从小到大排序,用一个临时数组来存储下标 任务队列用小根堆来存储,根节点就是所需时间最短的任务,每次取出来
如果任务队列为空:就快进到下一个任务的开始时间处,否则将先处理可以加入优先队列的元素,然后从中取出一个执行,处理完时间戳增加此任务的processTime
class Solution {
public:
vector<int> getOrder(vector<vector<int>>& tasks) {
int n = tasks.size();
vector<int> sortTasks;
for(int i = 0;i < n; i++){
sortTasks.push_back(i);
}
std::sort(sortTasks.begin(), sortTasks.end(), [&](int i, int j){
return tasks[i][0] < tasks[j][0];
});
vector<int> ans;
// 优先队列, pair<任务处理时间,索引位置>
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
// 时间戳
long long timestamp = 0;
int pos = 0;
for (int i = 0; i < n; ++i) {
if (q.empty()) {
timestamp = std::max(timestamp, (long long)tasks[sortTasks[pos]][0]);
}
while (pos < n && tasks[sortTasks[pos]][0] <= timestamp) {
q.push({tasks[sortTasks[pos]][1], sortTasks[pos]});
++pos;
}
const auto &[processTime, index] = q.top();
timestamp += processTime;
ans.push_back(index);
q.pop();
}
return ans;
}
};
复杂度分析
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
idx = list(range(n))
# sort index by enqueue time
idx.sort(key=lambda x: tasks[x][0])
ans = []
queue = []
time = 0
ptr = 0
for i in range(n):
if not queue:
time = max(time, tasks[idx[ptr]][0])
while ptr < n and tasks[idx[ptr]][0] <= time:
heapq.heappush(queue, (tasks[idx[ptr]][1], idx[ptr]))
ptr += 1
p, i = heapq.heappop(queue)
time += p
ans.append(i)
return ans
import heapq;
class Solution(object):
def getOrder(self, tasks):
"""
:type tasks: List[List[int]]
:rtype: List[int]
"""
#use heap to find the min processing time
heap = [];
task_list = [(task[0], i, task[1]) for i, task in enumerate (tasks)]; #keeps tracking the index
task_list = sorted(task_list, key = lambda x : x[0]) #sort processing time
res = [];
index = 0;
time = 0;
for _ in tasks:
if len(heap) == 0: #if no task, process the next avaliable time
time = max(time, task_list[index][0]);
while index < len(tasks) and task_list[index][0] <= time:
heapq.heappush(heap,(task_list[index][2], task_list[index][1])); #choose the shortest processing time first
index += 1;
processing_time, task_index = heapq.heappop(heap);
res.append(task_index);
time += processing_time;
return res;
#time complexity: sorting takes O(nlogn), heap O(nlogn)
使用Priority Queue进行排序模拟
class Solution {
public int[] getOrder(int[][] tasks) {
int n = tasks.length;
int[] ans = new int[n];
int[][] extTasks = new int[n][3];
for(int i = 0; i < n; i++) {
extTasks[i][0] = i;
extTasks[i][1] = tasks[i][0];
extTasks[i][2] = tasks[i][1];
}
Arrays.sort(extTasks, (a,b)->a[1] - b[1]);
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[2] == b[2] ? a[0] - b[0] : a[2] - b[2]);
int time = 0;
int ai = 0;
int ti = 0;
while(ai < n) {
while(ti < n && extTasks[ti][1] <= time) {
pq.offer(extTasks[ti++]);
}
if(pq.isEmpty()) {
time = extTasks[ti][1];
continue;
}
int[] bestFit = pq.poll();
ans[ai++] = bestFit[0];
time += bestFit[2];
}
return ans;
}
}
思路:
代码
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
tasks.sort()
backlog = []
time = 0
ans = []
pos = 0
for _ in tasks:
if not backlog:
time = max(time, tasks[pos][0])
while pos < len(tasks) and tasks[pos][0] <= time:
heapq.heappush(backlog, (tasks[pos][2], tasks[pos][1]))
pos += 1
d, j = heapq.heappop(backlog)
time += d
ans.append(j)
return ans
复杂度分析
令 n 为数组长度。
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
tasks.sort()
backlog = []
time = 0
ans = []
pos = 0
for _ in tasks:
if not backlog:
time = max(time, tasks[pos][0])
while pos < len(tasks) and tasks[pos][0] <= time:
heapq.heappush(backlog, (tasks[pos][2], tasks[pos][1]))
pos += 1
d, j = heapq.heappop(backlog)
time += d
ans.append(j)
return ans
思路: 以时间为单位,步进,用list模拟stack,到了对应时间就入栈,然后等cpu处理完了就pop(0),继续处理 代码:
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
t = copy.deepcopy(tasks)
stack = []
ans = []
time = 0
doing = 0
expectTime = 0
def trans(L:list):
index = t.index(L)
return (L[1],index)
while tasks or stack:
i = 0
while i <len(tasks):
if tasks[i][0]==time:
stack.append(tasks[i])
tasks.remove(tasks[i])
else:
i += 1
if time == expectTime:
doing = 0
if doing ==0:
stack.sort(key = trans)
if stack:
doing = _, proc = stack.pop(0)
expectTime = time + proc
ans.append(t.index(doing))
time += 1
continue
return ans
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
alltask = [(item[0],i,item[1]) for i,item in enumerate(tasks)]
alltask.sort()
ans = []
logs = []
time = 0
pos = 0
for _ in alltask:
if not logs:
time = max(time,alltask[pos][0])
while pos < len(alltask) and alltask[pos][0] <= time:
heapq.heappush(logs,(alltask[pos][2],alltask[pos][1]))
pos += 1
temp = heapq.heappop(logs)
ans.append(temp[1])
time += temp[0]
return ans
/*
Time: O(nlogn), n = number of tasks
Space: O(n)
*/
class Solution {
public int[] getOrder(int[][] tasks) {
int n = tasks.length;
int[] order = new int[n];
int[][] taskInfo = new int[n][3];
for (int i = 0; i < n; i++) {
taskInfo[i][0] = i;
taskInfo[i][1] = tasks[i][0];
taskInfo[i][2] = tasks[i][1];
}
// sort by enqueue time
Arrays.sort(taskInfo, (a,b)->a[1] - b[1]);
// pq
// process time ==, smaller index first; not equal, shorter process time first
PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[2] == b[2] ? a[0] - b[0] : a[2] - b[2]);
int time = 0;
int resIndex = 0;
int ti = 0;
while(resIndex < n) {
while(ti < n && taskInfo[ti][1] <= time) {
pq.offer(taskInfo[ti++]);
}
if(pq.isEmpty()) {
time = taskInfo[ti][1];
continue;
}
int[] bestFit = pq.poll();
order[resIndex++] = bestFit[0];
time += bestFit[2];
}
return order;
}
}
const getOrder = function (tasks) {
const queue = new MinPriorityQueue()
tasks = tasks.map((task, index) => ({
index,
start: task[0],
time: task[1]
}))
tasks.sort((a, b) => b.start - a.start)
const answer = []
let time = 0
while (tasks.length > 0 || !queue.isEmpty()) {
if (queue.isEmpty() && tasks[tasks.length - 1].start > time) {
time = tasks[tasks.length - 1].start
}
while (tasks.length > 0) {
if (tasks[tasks.length -1].start <= time) {
const task = tasks.pop()
queue.enqueue(task, task.time * 100000 + task.index)
} else {
break
}
}
const { element: task } = queue.dequeue()
time += task.time
answer.push(task.index)
}
return answer
}
/**
* @param {number[][]} tasks
* @return {number[]}
*/
const getOrder = function (tasks) {
const queue = new MinPriorityQueue()
tasks = tasks.map((task, index) => ({
index,
start: task[0],
time: task[1]
}))
tasks.sort((a, b) => b.start - a.start)
const answer = []
let time = 0
while (tasks.length > 0 || !queue.isEmpty()) {
// 队列为空,且没有任务能加入队列,直接跳过时间
if (queue.isEmpty() && tasks[tasks.length - 1].start > time) {
time = tasks[tasks.length - 1].start
}
// 向队列中加入可执行任务
while (tasks.length > 0) {
if (tasks[tasks.length -1].start <= time) {
const task = tasks.pop()
queue.enqueue(task, task.time * 100000 + task.index)
} else {
break
}
}
// 执行任务
const { element: task } = queue.dequeue()
time += task.time
answer.push(task.index)
}
return answer
}
class Solution:
def getOrder(self, tasks: List[List[int]]) -> List[int]:
n = len(tasks)
# (enqueueTime, taskIndex, processingTime)
tasks = [(task[0], i, task[1]) for i, task in enumerate(tasks)]
# 将tasks中所有任务按照enqueueTime升序排序
tasks.sort()
waiting = []
time = 0
ans = []
pos = 0
for i in range(n):
# 如果队列没有任务,则直接将 time 快进到下一个任务的enqueueTime
if not waiting:
time = max(time, tasks[pos][0])
# enqueueTime小于等于时间戳的task放入等待队列
while pos< n and tasks[pos][0] <= time:
heapq.heappush(waiting, (tasks[pos][2], tasks[pos][1]))
pos += 1
# 从等待队列中取出一个时间最短的进行处理
processingTime, taskIndex = heapq.heappop(waiting)
time += processingTime
ans.append(taskIndex)
return ans
1834. 单线程 CPU
入选理由
暂无
题目地址
https://leetcode-cn.com/problems/single-threaded-cpu/
前置知识
题目描述
现有一个单线程 CPU ,同一时间只能执行 最多一项 任务,该 CPU 将会按照下述方式运行:
如果 CPU 空闲,且任务队列中没有需要执行的任务,则 CPU 保持空闲状态。 如果 CPU 空闲,但任务队列中有需要执行的任务,则 CPU 将会选择 执行时间最短 的任务开始执行。如果多个任务具有同样的最短执行时间,则选择下标最小的任务开始执行。 一旦某项任务开始执行,CPU 在 执行完整个任务 前都不会停止。 CPU 可以在完成一项任务后,立即开始执行一项新任务。
返回 CPU 处理任务的顺序。
示例 1:
输入:tasks = [[1,2],[2,4],[3,2],[4,1]] 输出:[0,2,3,1] 解释:事件按下述流程运行:
示例 2:
输入:tasks = [[7,10],[7,12],[7,5],[7,4],[7,2]] 输出:[4,3,2,0,1] 解释:事件按下述流程运行:
提示:
tasks.length == n 1 <= n <= 105 1 <= enqueueTimei, processingTimei <= 109