一、什么是队列
- 队列实际上就是只能在一端插入(队尾),在另一端(队头)删除(出队)的特殊线性表;
二、队列特点
- 只允许在一端进行插入操作,一端进行删除操作,即入队、出队。
- 有先进先出(FIFO)的特性;
- 我们熟知的键盘缓冲区就用队列实现的(scanf函数就有运用这种特性);
- 对于队列可以顺序存储,也可以链式存储;
三、代码实例
1、链式存储:实现简单的队列创建,入队、出队、打印队列数据等操作;
#include <stdio.h>
#include <stdlib.h>
#define ElemType int
typedef struct QNode{
ElemType data;
struct QNode* next;
}QNode_t,*P_QNode_t;
typedef struct Queue{
P_QNode_t front,rear;
}Queue,*LinkQueue;
void queue_creat(LinkQueue q);
void queue_insert(LinkQueue q,ElemType e);
ElemType queue_out(LinkQueue q);
void queue_show(LinkQueue q);
int main()
{
int i;
Queue q;
queue_creat(&q);
for(i=1;i<=5;i++)
{
queue_insert(&q,i);
}
queue_show(&q);
printf("\noutput operation:\n");
printf("the number is %d\n",queue_out(&q));
printf("the number is %d\n",queue_out(&q));
queue_show(&q);
return 0;
}
/*创建队列*/
void queue_creat(LinkQueue q)
{
q->front=q->rear=(P_QNode_t)malloc(sizeof(QNode_t));/*front指针指向的是一个头节点,这个节点只是为了方便操作而创建的,也可不用头节点*/
if(!q->front)
{
printf("create failed\n");
exit(0);
}
q->front->next=NULL;
}
/*入队操作,从队尾进入*/
void queue_insert(LinkQueue q,ElemType e)
{
/*先创建一个节点,该节点含有数据和指针*/
P_QNode_t q_node=(P_QNode_t)malloc(sizeof(struct QNode));
if(!q_node)
{
printf("node create failed\n");
exit(0);
}
/*给节点赋值*/
q_node->data=e;
q_node->next=NULL;
q->rear->next=q_node;
q->rear=q_node;
}
/*出队操作:出队操作,从队头出队,即时头节点中next指向的节点出队,因此出队后头节点中的next指针指向第二个节点*/
/*出队时要判断队列是否为空,*/
ElemType queue_out(LinkQueue q)
{
ElemType value;
P_QNode_t p;
if(q->front==q->rear)
{
printf("queue is empty\n");
return -1;
}
p=q->front->next;
value=p->data;
q->front->next=p->next;
/*当只有一个节点时,即rear指向的节点,出栈之后该栈为空,k*/
if(q->rear==p)
{
q->rear=q->front;
}
free(p);
return value;
}
void queue_show(LinkQueue q)
{
P_QNode_t p=q->front->next;
if(q->front!=q->rear)
{
while(p->next)
{
printf("%d ",p->data);
p=p->next;
}
printf("%d ",p->data);
}
else
printf("is empty\n");
}
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAXSIZE 20
typedef int data_t;
typedef struct
{
data_t data[MAXSIZE];
int front,rear;
}queue_t,*p_queue_t;
p_queue_t queue_create();
bool is_full(p_queue_t q);
void in_queue(data_t data,p_queue_t q);
bool is_empty(p_queue_t q);
void out_queue(p_queue_t q,data_t *data);
void queue_show(p_queue_t q);
int main()
{
int i;
p_queue_t q;
q=queue_create();
in_queue(1,q);
in_queue(2,q);
in_queue(5,q);
in_queue(11,q);
in_queue(5,q);
in_queue(8,q);
in_queue(5,q);
queue_show(q);
out_queue(q,&i);
printf("%d\n",i);
queue_show(q);
out_queue(q,&i);
printf("%d\n",i);
queue_show(q);
return 0;
}
p_queue_t queue_create()
{
p_queue_t q;
q=(p_queue_t)malloc(sizeof(queue_t));
if(q==NULL)
{
printf("error\n");
exit(-1);
}
q->front=q->rear=MAXSIZE-1;
return q;
}
bool is_full(p_queue_t q)
{
if((q->rear+1)%MAXSIZE==q->front)
return true;
else
return false;
}
void in_queue(data_t data,p_queue_t q)
{
if(is_full(q))
{
printf("q is full\n");
return ;
}
q->rear=(q->rear+1)%MAXSIZE;
q->data[q->rear]=data;
}
bool is_empty(p_queue_t q)
{
if(q->rear==q->front)
return true;
else
return false;
}
void out_queue(p_queue_t q,data_t *data)
{
if(is_empty(q))
{
printf("q is empty\n");
return;
}
q->front=(q->front+1)%MAXSIZE;
*data=q->data[q->front];
}
void queue_show(p_queue_t q)
{
int i;
for(i=(q->front+1)%MAXSIZE;i!=(q->rear+1)%MAXSIZE;i++)
{
printf("%d\t",q->data[i]);
}
printf("\n");
}
队列的顺序存储结构
在现实中也是如此,一群人在排队买火车票,前边的人买好了离开,后面的人就要全部向前一步补上空位。
可是我们研究数据结构和算法的一个根本目的就是要想方设法提高我们的程序的效率,按刚才的方式,出队列的时间复杂度是O(n),效率大打折扣!
如果我们不去限制队头一定要在下标为0的位置,那么出队列的操作就不需要移动全体元素。
但是这样也会出现一些问题,会出现数组越界的错误。所以引出了循环队列
循环队列定义
循环队列它的容量是固定的,并且它的队头和队尾指针都可以随着元素入出队列而发生改变,这循环队列逻辑上就好像是一个环形存储空间。但要注意的是,在实际的内存当中,不可能有真正的环形存储区,我们只是用顺序表模拟出来的逻辑上的循环。
似乎循环队列的实现只需要灵活改变front和rear指针即可。
也就是让front或rear指针不断加1,即时超出了地址范围,也会自动从头开始。我们可以采取取模运算处理:
(rear+1) % QueueSize
(front+1) % QueueSize
取模就是取余数的意思,他取到的值永远不会大于除数,大家结合实例拿张纸算一算就知道啦~