郝斌数据结构入门---P35---队列
线性结构的常见应用之一:队列(头部删除,尾部插入)
定义:一种可以实现“ 先进先出 ”的存储结构,队列类似于排队去买票(一端入,一端出)
分类:链式队列(用链表实现),静态队列(用数组实现),静态队列通常都必须是循环队列。
循环队列: (循环队列的好处:可以重复使用已经废弃的内存!!)
1、静态队列为什么必须是循环队列(f:front前,r:rear后)
r永远指向当前队列的最后一个元素的下一个位置!!!!!
f永远指向第一个元素!!!
如果f指向第一个元素,r指向最后一个元素的下一个元素。
如果f指向第一个元素的前一个元素,r就指向最后一个元素。
它们两个是错开的。
如果是增加元素(入队),r增加。如果是删除元素(出队),f增加。
上面是传统的数组实现队列。(只能用一次)
下面是循环队列:(可以多次使用)
初始状态:只有“c”这个元素
增加:增加一个为“中”的元素,现在队列的元素有“c”和“中”:(r增加一个位置,因为循环,到对头部)
删除:现在要删除一个元素,删除了“c”元素,剩下“中”元素(f增加一个位置)
增加:要增加一个为“国”元素,现在队列的元素中有“中”和“国”两个元素(r增加一个位置)
删除:现在要删除一个元素,删除了“中”元素,剩下“国”元素(f增加一个位置,因为循环,到对头部)
2、循环队列需要几个参数来确定
需要2个参数来确定
3、循环队列各个参数的含义
front:指向第一个元素(队列头)
rear:最后一个的下一个元素(队列尾的下一个)
2个参数在不同场合有不同的含义:
队列初始化:front和rear的值都是零
队列非空:front代表的是队列的第一个元素,rear代表的是队列的最后一个有效元素的下一个元素
队列空:front和rear的值相等,但不一定是零
4、循环队列入队伪算法讲解(注意判断r的位置)
第一步:先把将入队的值存入到r所代表的位置
第二步:错误的写法: r=r+1;
正确的写法是:r=(r+1) % 数组的长度
5、循环队列出队伪算法讲解
一步搞定:f=(f+1) % 数组的长度
6、如何判断循环队列是否为空
如果 front 和 rear的值相等,则该队列就一定为空
7、如何判断循环队列是否已满
预备知识:
front的值可能比rear大,front的值也可能比rear小,当然也可能两者相等
两种方式:
1、多增加一个标识参数
2、少用一个元素(n-1)【通常使用第二种方式】
如果 r 和 f 的值紧挨着,则队列已满。
用C语言伪算法表示就是:
if ( (r+1)%数组长度 == f )
已满
else
不满
求链表的长度:
循环队列的源代码:
关键是取余符号%的理解,相当于走了一圈,然后再回到开始位置,再循环。
#include <stdio.h>
#include <malloc.h>
typedef struct Queue
{
int *pBase;//数组
int front;//前
int rear;//后
}QUEUE;
void init(QUEUE *);
int en_queue(QUEUE *, int val);//入队
void traverse_queue(QUEUE *);//遍历
int full_queue(QUEUE *);//判断队列是否满了
int out_queue(QUEUE *, int *);//出队,并且保存出队元素
int empty_queue(QUEUE *);//判断队列是否空的
int main(void)
{
QUEUE Q;
int val;
init(&Q);
en_queue(&Q, 1);
en_queue(&Q, 2);
en_queue(&Q, 3);
en_queue(&Q, 4);
en_queue(&Q, 5);
en_queue(&Q, 6);
en_queue(&Q, 7);
en_queue(&Q, 8);
traverse_queue(&Q);
if (out_queue(&Q, &val))
{
printf("出队成功,队列出队的元素是:%d\n", val);
}
else
{
printf("出队失败!\n");
}
traverse_queue(&Q);
return 0;
}
void init(QUEUE *pQ)
{
pQ->pBase = (int *)malloc(sizeof(int) * 6);//数组
pQ->front = 0;
pQ->rear = 0;
}
int full_queue(QUEUE *pQ)//判断队列是否满了
{
if ((pQ->rear + 1) % 6 == pQ->front)
return 1;
else
return 0;
}
int en_queue(QUEUE *pQ, int val)//入队
{
if (full_queue(pQ))
{
return 0;
}
else
{
//先把值放入rear位置,然后再往后移动一个
pQ->pBase[pQ->rear] = val;
pQ->rear = (pQ->rear+1) % 6;
return 1;
}
}
void traverse_queue(QUEUE *pQ)//头部遍历
{
int i = pQ->front;
while (i != pQ->rear)
{
printf("%d ", pQ->pBase[i]);
i = (i+1) % 6;
}
printf("\n");
return ;
}
int empty_queue(QUEUE *pQ)
{
if (pQ->front == pQ->rear)
return 1;
else
return 0;
}
int out_queue(QUEUE *pQ, int *pVal)//出队,并且保存出队元素
{
if (empty_queue(pQ))
{
return false;
}
else
{
//先保存数据,然后再向后移动一个位置
*pVal = pQ->pBase[pQ->front];
pQ->front = (pQ->front+1) %6;
return 1;
}
}