#include <stdio.h>
#include <stdlib.h>
//实现队列的数据结构
//队列是只允许队尾插入,队头删除的存储结构
//先来看顺序存储结构(数组实现)
#define OK 1
#define ERROR -1
#define MAX 10 //定义队列的长度
typedef int ElemType;
typedef int Status;
typedef struct {
ElemType data[MAX];
int head;//队头
int tail;//队尾
}Queue;
//初始化操作
Status InitQueue(Queue *Q){
// memset(Q->data,'\0',sizeof(ElemType)*MAX);
Q->head=0;
Q->tail=0;//指向队尾元素的下一个位置
return OK;
}
//产生测试队列
Status CreateQueue(Queue *Q,int n){
//初始化队列
//产生一个长度为n的测试队列
srand(time(0));
int i;
for(i=0;i<n;i++){
Q->data[(Q->tail)++]=rand()%100+1;
}
return OK;
}
//入队操作
Status EnQueue(Queue *Q,ElemType e){
//判断是否队满
if(Q->tail==MAX){
printf("队列已满,不能入队");
return ERROR;
}
Q->data[Q->tail]=e;
++(Q->tail);
return OK;
}
//出队操作
Status DeQueue(Queue *Q,ElemType *e){
//将出队的元素传给e
//判读是否为空队
if(Q->tail==0 || Q->tail<=Q->head){
printf("空队列,无法出队!");
return ERROR;
}
*e=Q->data[Q->head];
//队头后移
++(Q->head);
return OK;
}
int main()
{
Queue Q;
int i,n,e;
if(OK!=InitQueue(&Q)){
printf("初始化失败!");
return ERROR;
}
printf("输入要产生的队列长度!");
scanf("%d",&n);
if(n>MAX){
printf("已超出队列的最大长度");
return ERROR;
}
if(OK==CreateQueue(&Q,n)){
//输出队列的值
for(i=Q.head;i<Q.tail;i++){
printf("%d\t",Q.data[i]);
}
printf("\n头为%d\n",Q.head);
printf("尾为%d\n",Q.tail);
}
printf("输入要插入的元素!\n");
while(1==scanf("%d",&e)){
if(OK==EnQueue(&Q,e)){
for(i=Q.head;i<Q.tail;i++){
printf("%d\t",Q.data[i]);
}
printf("\n头为%d\n",Q.head);
printf("尾为%d\n",Q.tail);
}else{
break;
}
}
printf("元素依次出队\n");
while(OK==DeQueue(&Q,&e)){
printf("出队元素为%d\t,队头为%d\n",e,Q.head);
}
return 0;
}
以上代码是队列的简单实现;由于出队时仅仅是将队头后移,入队时队尾只能往后移动;会出现队头挨近队尾时,队头前面大量空间未被使用的情况,从而浪费了大量的存储空间。
考虑入队时,队尾如果到达存储队列的数组的末尾时,而实际上队列并没有满,前面还有空余的存储空间;队尾则移到数组的首部;执行后续操作。
循环队列是首尾相接的循序存储结构。
循环队列的数据结构和简单队列的结构一样;只是入队、出队、求长等需要变化一下。
Status EnQueue2(Queue *Q,ElemType e){
//循环队列同样要判断队列是否为空;
//注意循环队列的head可能大于tail
//循环队列满的条件判断
if((Q->tail+1)%MAX==Q->head){
//队列满了
printf("队列已满!\n");
return ERROR;
}
Q->data[Q->tail]=e;
//Q->tail的范围在0~MAX-1;
Q->tail=(Q->tail+1)%MAX;
return OK;
}
Status DeQueue2(Queue *Q,ElemType *e){
// 循环队列的出队操作
//判断队列是否为空
//此处会有一个bug;当输入的队长度为MAX时,初始化后的Q->tail=MAX;而Q->head的范围是0~MAX-1;永远无法满足条件Q->head==Q->tail
if(Q->head==Q->tail){
printf("循环队列为空!");
return ERROR;
}
*e=Q->data[Q->head];
Q->head=(Q->head+1)%MAX;
return OK;
}
int QLen(Queue Q){
return (Q.tail-Q.head+MAX)%MAX;
}
同线性表一样,队列既然有循序存储结构,就有链式存储结构;队列的链式存储结构主要是为了解决队列存储空间的问题。
#include <stdio.h>
#include <stdlib.h>
//队列的链式存储,链式存储的共性是不必考虑存储空间的溢出
//队列的链式存储的基本数据结构其实就是链表;我们还是用单链表来实现
#define OK 1
#define ERROR -1
typedef int Status;
typedef int ElemType;
typedef struct {
ElemType data;
struct Node *next;
}Node,*QNode;
//利用单链表的基本数据结构来定义队列的数据结构
typedef struct{
//队列有结点;头指针和尾指针
QNode head,tail;//QNode是指向结点的指针,该类型已经包含结点,不需要再定义结点了
//head这里设置为指向队头的结点,不是队头,是指向队头的结点
}QList;
//队列初始化
Status InitQ(QList *Q){
//创建一个临时结点
QNode N;
N=(QNode)malloc(sizeof(Node));
N->data=1;
N->next=NULL;
//让队列的首尾指针都指向他
Q->head=N;
Q->tail=N;
return OK;
}
//入队操作
Status EnQue(QList *Q,ElemType e){
//将元素e入队;
QNode N;
N=(QNode)malloc(sizeof(Node));
//需要考虑存储是否溢出
if(!N){
printf("存储分配失败!\n");
return ERROR;
}
N->data=e;
N->next=NULL;
Q->tail->next=N;
Q->tail=N;
return OK;
}
//出队操作
Status DeQue(QList *Q,ElemType *e){
//判断是否为空
if(Q->head==Q->tail){
printf("队列为空");
return ERROR;
}
//注意这里设置的队头是Q->head->next指向的结点;Q->head作为链表的头结点
QNode T;
T=Q->head->next;//将头结点传给临时结点
*e=T->data;
Q->head->next=T->next;
//如果队头到达队尾时(临时结点是尾结点),出队后,队尾指针要指向头结点
if(T==Q->tail){
Q->tail=Q->head;
}
//释放队头结点占的内存;一般的删除操作都要新建一个临时结点,接收要删除的结点,然后调整指针,释放内存
free(T);
return OK;
}
int main()
{
QList Q;
if(OK==InitQ(&Q)){
printf("初始化成功!\n");
}
int e;
e=1;
EnQue(&Q,5);
QNode t;
t=Q.head->next;
printf("%d\n",t->data);
EnQue(&Q,6);
t=t->next;
printf("%d\n",t->data);
DeQue(&Q,&e);
printf("%d\n",e);
DeQue(&Q,&e);
printf("%d\n",e);
DeQue(&Q,&e);
printf("%d\n",e);
return 0;
}