一.键盘缓冲区模拟
在操作系统中,循环队列经常用于实时应用程序。比如,当程序正在执行其他任务时,用户可以从键盘不断输入内容。很多字处理软件就是这样工作。系统在采用这种分时处理方法时,用户输入的内容不能在屏幕上立刻显示出来,直到当前正在工作的那个进程结束为止。但在这个进程执行时,系统是在不断地检查键盘状态,如果检测到用户输入了一个新的字符,就立刻把它存到系统缓存区中,然后继续运行原来的进程。在当前工作的进程结束后,系统就从缓冲区中取出用户输入的字符,并按照要求进行处理。这里的键盘输入缓冲区采用了循环队列。队列的特性保证了输入的字符先输入,先保存,先处理的要求,循环结构又有效地限制了缓冲区的大小,并避免了假溢出问题。
这里通过一个不断在屏幕上输出6的进程P_6(),在其运行时,程序不断检测键盘上是否有输入,有的话就接受保存到输入缓冲区,也就是我们的模拟队列中。然后在用户输入一个逗号时,表示结束第一个进程,然后执行第二个进程,从缓冲区读取已经输入进去的字符,并将其显示到屏幕上。第二个进程结束后,又和原来一样,重新开始第一个进程。直到连续输入两个分号终止程序。
1.算法思想
实际上我们就希望程序一不断执行,就需要考虑到循环语句,用for或者while都行,而程序二需要在键盘输入时不动声色地将输入内容保存到队列中,并且键盘没有输入时程序一直进行,不会中断。难点就在于如何不动声色地输入以及在键盘不输入时程序一持续进行。
2.getch()
为了默默的输入,我们需要使用没有回显并且不需要回车来结束的输入函数getch();
这里做个简单介绍,C语言中的输入函数会在后续详细讲解。
函数原型:int getch(void) 参数void
返回值:读取的字符的ASCII码值(整数)
getch()是一个C语言中的函数,它可以用于接收隐藏的输入,例如密码、ATM密码等。使用getch()函数时,输入的字符不会在屏幕上显示,而是直接传递给程序。getch()函数需要在程序中包含头文件conio.h。在使用getch()函数时,可以直接使用getch()函数等待用户输入任意键,也可以使用ch=getch()函数等待用户输入任意键并将该字符所对应的ASCII码赋给变量ch。
3.kbhit()
用getch()解决了默默输入,这时就是平时不打断程序一施法,只在键盘有输入时打断,那么就需要一个可以检测键盘是否输入的函数--kbhit();
这里也是简单介绍。
函数原型: int kbhit(void) 参数void
返回值:非零整数
kbhit()是一个C语言函数,位于conio.h头文件中,用于检测键盘是否有输入。如果有输入,kbhit()函数将返回一个非零值,否则返回0。通常,它与getch()函数一起使用,以便在等待用户输入时不会阻塞程序。当用户按下键盘上的任何键时,kbhit()函数将返回非零值,然后可以使用getch()函数获取用户输入的字符。因此,kbhit()函数在编写需要等待用户输入的程序时非常有用。
4.实现
现在万事俱备,只欠东风。只需将队列与这些函数调用起来就可以实现简单的键盘缓冲区模拟。
//_Kbhit() _getch()
void Cache() {
AQ* L = Init_AQ(); // 用队列模拟缓冲区
char C = '6'; // 用来接收输入的字符
for (;;) {
for (;;) { // 第一个进程
P_6();
if (_kbhit()) { // _kbhit() 非阻塞函数,没有检测到键盘输入就继续
C = _getch(); // 用来接收输入的字符,并且不在屏幕上显示
if (C != ',' && C != ';') { // 如果输入 ',' 或者';'停止第一个进程
if (((L->front - L->rear + Max) % Max) == 1) { // 满,放上溢退出,这里用的循环队列,也可以用链队列就不必考虑溢出问题了
printf("队满!!!\t");
Out_AQ(L);
return;
}
Push_AQ_B(L, C); // 来一个,入一个
}
else {
Out_AQ(L); // 全部输出
Clear_AQ(L); // 重头再来
break;
}
}
}
C = _getch(); // 判断是否结束
if (C == ';') {
break;
}
}
}
二.杨辉三角形打印
杨辉三角大家肯定熟知,这里做一个简单介绍。
我们这里需要了解如下几个性质:
-
每个数等于它上方两数之和。
-
每行数字左右对称,由1开始逐渐变大。
-
第n行的数字有n项。
杨辉三角,是二项式系数在三角形中的一种几何排列,中国南宋数学家杨辉1261年所著的《详解九章算法》一书中出现。在欧洲,帕斯卡(1623----1662)在1654年发现这一规律,所以这个表又叫做帕斯卡三角形。帕斯卡的发现比杨辉要迟393年,比贾宪迟600年。
1.算法思想
根据上述性质,我们可知,杨辉三角形的第i行元素需要第i-1行元素生成。可以利用队列实现打印杨辉三角:在队列中可以依次放置第i-1行元素,然后将其打印后逐个出列并生成下一列元素。
打印自然需要用到输出函数,出列用到Pop函数,表示下一行元素自然需要将队列前两个元素作和,并将其结果入队,那么可以先出列一个元素a,然后用Top得到出列后的队列的第一个元素b,并将a+b入队(Push)。
具体如何实现呢?
只有在输出的行数n>2的时候,才具有意义,所以我们讨论当n>2的时候的具体操作。
首先,要生成第3行元素,就需要先在列中存储第二列元素,也就是两个1,由于每一行的首元素都是1,所以在生成第i+1行元素时,先给他入一个1,后面就将上一行的前两个元素,也就是队列的前两个元素求和并出队第一个元素,然后将它们的和入队,一直循环进行上述操作,直到遇到第i行的最后一个元素1,也就是这个队列的队首元素再次为1时,压入一个1,将上一行最后一个元素出队,这样就实现了从i行生成i+1行的过程。(由于杨辉三角每行的头和尾都是1,也可以将i行的最后一个元素1作为i+1行的第一个元素1)
2.代码实现
为了美观,我们可以在每一行元素输入前为其打上空格,因为每一行的元素数量就是其对应的行数,所以当行数n确定时,最长的元素数量也就确定了,假设每个元素占1个位置,则空格数=
总行数n-其所在行数i,也可以根据实际情况扩大每一个元素所占位置的大小。这里让每个元素占据4个空间。
01-输出空格
void KG(int n,int j) {
for (int k = 0; k < n*2 - j * 2 - 1; ++k) { //每个元素可占4个位置,每一行所需空格依次减少,空格总需求就取决于要输出的行数
printf(" ");
}
}
void YH() {
printf("请输入所要得到杨辉三角形的行数:\n");
int n;
scanf("%d", &n);
if (n == 1) { // 考虑物种多样性,确保每次输入都有反馈
printf("1\n");
return;
}
else if (n == 2) {
printf("1 1\n");
return;
}
else if (n <= 0) {
printf("6\n");
return;
}
ptq* Y = Init_LQ(); // 如果从第三行开始,就要考虑用队列帮助完成操作
Push_LQ_B(Y,1); // 先压入第二行元素作为初始资本
Push_LQ_B(Y, 1);
KG(n+2, -2); // 输出头两行
printf("1\n");
KG(n+2, -1);
printf("1 1\n");
for (int i = 0; i < n-2; ++i) { // 此后,每一行都先入队一个1,然后再入队列前两个元素之和,再出队一个元素
for(int g=0;g<i+2;++g) { // 因这是从第三行开始的,故n-2
if (g == 0) { // 刚开始,入一
Push_LQ_B(Y, 1);
}
else {
int a = Top_LQ(Y); // 出队一个元素
Pop_LQ(Y);
Push_LQ_B(Y, a + Top_LQ(Y)); // 压入队列前两个元素之和
}
}
// 上一行最后一个一,结束,入一
Push_LQ_B(Y, 1);
Pop_LQ(Y);
KG( n, i);
Out_LQ(Y);
printf("\n");
}
}
这就是一些对于队列的简单应用,对队列的实现存有困惑可以回顾此文:
希望本文可以对各位在队列应用上上起到帮助。