数据结构之队列的实现
队列的思想在cpu处理多任务是最为常见,先看一个例子。
题目
现有名称为namei且处理时间为timei 的n个任务顺序排成一列,CPU通过循环调度法逐一处理这些任务每个任务最多处理q ms(这个时间成为时间片)。如果q ms之后任务尚未处理完毕,那么该任务将被移动至队伍最末尾,CPU随即开始处理下一个任务。
例子:假设q是100,然后有如下任务队列
A(150)—B(80)—C(200)—D(200)
首先A被处理100ms,然后带着剩余的50ms移动至队列尾。
B(80)—C(200)—D(200)—A(50)
接下来B被处理80ms,在第180ms时完成处理从队列中消失。
C(200)—D(200)—A(50)
依次类推
———————————————————————————————————
输入
n q
name1 time1
name2 time2
… …
第1行输入表示任务数的整数n与表示时间片的整数q,用1个空格隔开。
接下来n行输入各任务的信息。字符串name与timei用1个空格隔开。
输出
按照任务完成的先后顺序输出各任务名以及结束时间,任务名与对应结束时间用空格隔开,每一对任务名与结束时间占1行。
限制
1 ≤ n ≤ 100 000
1 ≤ q ≤ 1000
1 ≤ timei ≤ 50 000
1 ≤ 字符串namei的长度 ≤ 10
1 ≤ timei的和 ≤ 1 000 000
输入示例
5 100
p1 150
p2 80
p3 200
p4 350
p5 20
输出示例
p2 180
p5 400
p1 450
p3 550
p4 800
先看代码
#include<stdio.h>
#include<string.h>
#define LEN 10005
typedef struct PP {
char name[100];
int t;
} P;
P Q[LEN];
int head, tail, n; //n代表总任务数
bool isEmpty() {
return head == tail;
}
void enqueue(P x) {
//该函数用来向队列尾添加元素
Q[tail] = x;
tail = (tail + 1) % LEN;
}
P dequeue() {
//该函数用来从队列前取出元素
P x = Q[head];
head = (head + 1) % LEN;
return x;
}
int min(int a, int b) {
return a < b ? a : b; }
int main()
{
int elaps = 0, c;
int i, q; //q代表的是时间片
P u;
scanf("%d %d", &n, &q);
//按顺序添加任务到队列
for (i = 1; i <= n ; i++)
{
scanf("%s", Q[i].name);
scanf("%d", &Q[i].t);
}
head = 1; tail = n + 1;
while (tail != head)
{
u = dequeue();
c = min(q, u.t);
u.t -= c;
elaps += c;
if (u.t > 0)
{
enqueue(u);
}
else
{
printf("%s %d\n", u.name, elaps);
}
}
return 0;
}
使用数组可以模拟队列,主要需要以下变量以及函数
1.储存数据的数组:Q
2.用作头指针的head,dequeue每次会取出head所指的元素。
3.指向队列尾的tail,他的值是n+1,每次enqueue会将处理好的数据存储在数据末尾的后边。
4.enqueue(x)函数,向队列中添加元素,注意他是有参的
5.dequeue()函数,用来取出元素的函数。
在程序运行时,首先会从Q的开头取出元素在处理之后用enqueue将其添加到tail所指的位置。这样一次处理下去,知道head与tail相等时,处理完成。
不过这样做首先需要保证数组的长度足够,否则当tail+1是很容易发生数组下标越界。在处理大型数据时,数组的长度可能会十分恐怖
如果想要避免这种情况,则需要将head指针常保持在0,而每次执行完dequeue()之后将所有数据向数组开头移动。缺点是,每次移动会增加O(n)的复杂度。
书中的解决办法时使用环形缓冲区来管理数据。
可以将其理解为秒针,当tail+1=12超出了数组的范围的时候,就将其置为0。类似于1分钟过去了,又从1秒开始计算。
//对head和tail的操作
head = (head + 1) % MAX //MAX表示数组元素个数
tail = (tail + 1) % MAX
这两个表达式会将head和tail超出数组数据个数时将其置为0。
环形缓冲区可以同时以复杂度O(1)实现dequeue和enqueue的操作。
队列以及栈作为两种简单的数据结构值得去认真学习。