队列大部分应用都是与其他数据结构和算法结合,leetcode中队列的题目也比较少,免费题目只有621. Task Scheduler和363.Max Sum of RectangleNo Larger Than K,363后面再解决。
与其他数据结构和算法结合:
(1) 拓扑排序,判断有向无环图中是否有环。用队列保存入度为0的元素,用一个一维数组indegree存放每个节点的入度,并每取出一个元素,就对indegree进行更新。
(2) 广度优先搜索,无向图寻找最短路径,求最短路径长度,条数。
(3) 树的层次遍历
621. Task Scheduler
此题给出一个任务序列,n是相同的任务至少需要的间隔数量。如果相同任务的间隔不能满足至少n个,那么就要向其中插入idle. 所以需要我们设计任务序列使之在尽可能少的时间完成这些任务,尽可能减少或不插入idle。本题solution中有三种解决方案,其中第二种运用队列。下面将具体介绍这三种方法:
(1) 贪心法
思路:为了确保尽可能少的idle,且task从A到Z做多有26种任务,只有将数量较多且相同任务的间隔尽可能到达最小间隔时(贪心),才能使idle更少。例如,任务有A, B, C, D, E,他们的数量分别为6, 1,1, 1, 1,n = 2. 如果采用A, B, C, D, E作为第一次循环,那么还剩下5个A,而这5个A就需要更多的idle放在其中了,A, B, C, D, E,A, idle,idle,A,idle,idle, A,idle,idle,A,idle,idle, A,总共用时18。但如果遵循每次将尽可能多数量的task先运行,且间隔到达最小允许的间隔,那么就可以使数量最多的task尽可能的和数量第二多,数量第三多…的task进行组合,减少数量较多的task剩下的可能,从而缩短总时间了。例如这里A有6个是最多的元素,就要减少就剩下A的可能,因为剩下的A越多,最后剩下的A之间就要只能选择间隔n个idle了,没有其他选择了。所以A要尽可能的和其他元素进行组合。所以A,B,C,A,D,E,A,idle,idle, A, idle,idle,A, idle,idle,A,总共用时16,减少了两个idle。
class Solution {
public:
int leastInterval(vector& tasks, int n) {
vector task(26); // size = 26
for(int i = 0; i < tasks.size(); ++i)
{
task[tasks[i] - 'A']++;
}
int timer = 0;
sort(task.begin(), task.end());
while(task[25] > 0)
{
int i = 0;
while(i <= n)
{
if(task[25] == 0)
break;
if(i < 26 && task[25 - i] > 0)
task[25 - i]--;
++i;
++timer;
}
sort(task.begin(), task.end());
}
return timer;
}
};
(2) 优先队列
思路:和第一种方法的大体思路是一致的,采用优先队列的好处是在插入元素时自动排序。省去了用数组每次要重新排序的过程。
class Solution {
public:
int leastInterval(vector& tasks, int n) {
vector task(26); // size = 26
for(int i = 0; i < tasks.size(); ++i)
{
task[tasks[i] - 'A']++;
}
priority_queue q; // 默认大根堆,且自动排序
for(int i = 0; i < task.size(); ++i)
{
if(task[i] > 0)
q.push(task[i]);
}
cout << q.size() << endl;
int timer = 0;
vector tmp; // 缓存每次执行的任务,他们所剩下的任务数量
while(!q.empty())
{
int i = 0;
while(i <= n)
{
if(q.empty() && tmp.size() == 0)
break;
if(!q.empty())
{
if(q.top() > 1)
tmp.push_back(q.top() - 1);
q.pop();
}
++timer;
++i;
}
for(int j = 0; j < tmp.size(); ++j)
{
q.push(tmp[j]);
}
tmp.clear();
}
return timer;
}
};
(3) 直接计算
思路:先计算idle的数量,再加上tasks中任务数量就是总的时间。除了最大出现的task外其他位置全部被填充为idle, 之后再减去所有的任务,剩下的就是所有的idle。
class Solution {
public:
int leastInterval(vector& tasks, int n) {
vector task(26); // size = 26
for(int i = 0; i < tasks.size(); ++i)
{
task[tasks[i] - 'A']++;
}
sort(task.begin(), task.end());
int maxTask = task[25] - 1; // tasks中任务出现的最大次数
int idleCnt = maxTask * n; // 全部填充idle的时间
for(int i = 24; i >=0 && task[i] > 0; --i)
{
idleCnt -= min(task[i], maxTask);
}
return idleCnt > 0? idleCnt + tasks.size() : tasks.size();
}
};