本文主要内容:顺序循环队列,链式队列,栈实现队列
队列:队列和栈一样只允许在断点处插入和删除元素。先进先出(FIFO)
作用:缓冲,排队,等待
顺序循环队列
初始状态:一个头一个尾,头出尾进,出队对头操作,入队对尾操作。
注意点:
在栈中和表中,top和last都是表示数据存在的位置,而在队列中,head和tail有时有有时没有。
head和tail表示“即将”,head表示即将在head位置出队,tail表示即将在tail位置出队。
head++,tail++,到了尾部应该怎么办?
h = (h+1)% s
t = (t+1)% s
为什么最多放S-1个?
t == h 放着放着就满了,但是存在两义性,和空队列一样。所以只放S-1个,t和h不重叠。
什么时候是空,什么时候是满?
队列最多只能放S-1个
(t+1)%s == h t再放一个就满了
或(t-h+s)%s == s-1 (长度公式)
第一种:此时 t-h == 4; t+1 == 5; 可得 t-h+s == 9;
第二种:此时 t-h == -1; t+1 == h; 可得 t-h+s == 5;
为了同时满足第一种和第二种 可得下面公式: !!重点!!
(t+1)%s == h
(t-h+s)% s == s-1
定义结构体:
typedef struct node { int * data;//存数据元素 int size;//标记数据元素个数 int head;//即将要取数据的位置(下标) int tail;//即将要存数据的位置(下标)}queue_t;//顺序循环队列的数据类型
创建:
queue_t* create_queue(int size){ if(size<=0||size>20000) return NULL; //申请顺序循环队列标识的空间 queue_t* queue=malloc(sizeof(queue_t)); if(queue == NULL) { printf("标识申请空间失败!\n"); return NULL; } //申请存储数据元素空间 queue->data=malloc(sizeof(int)*size); if(queue->data == NULL) { printf("数据空间申请失败!\n"); return NULL; } //标记数据元素个数 queue->size=size; //初始状态约定head和tail都保存0位置 queue->head=queue->tail=0; return queue;}
判空判满:
//判满int isfull_queue(queue_t * queue) { return queue->head == (queue->tail + 1) % queue->size;}//判空int isnull_queue(queue_t * queue) { return queue->head == queue->tail;}//判满2 s大小的队列 最多存放 s-1个元素 (不让h和t在放满的情况下重合)int isfull_queue_length(queue_t* queue){ return (queue->tail-queue->head+queue->size)%queue->size==queue->size-1;}
入队:
int push_queue(queue_t* queue,int data) { if(queue==NULL||isfull_queue(queue)) return -1; queue->data[queue->tail]=data; //修改tail的值(假使tail在7位置,存入数据后指向下一个位置0,所以操作时需要对队列长度取余) queue->tail=(queue->tail+1)%queue->size; return 0;}
出队:
int pop_queue(queue_t* queue,int *data) { //0 容错 if(queue==NULL||isnull_queue(queue)) return -1; //1 取数据 *data=queue->data[queue->head]; queue->data[queue->head]=0; //2 head 变化 queue->head=(queue->head+1)%queue->size; return 0;}
假打印(0~N~1):
int printf_fromzero_fake(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return -1; int i; for(i=0;isize;i++) { printf("%3d ",queue->data[i]); } printf("\n"); return 0;}
假打印 h~t:
int printf_from_h_to_t_fake(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return -1; int i; for(i=queue->head;i!=queue->tail;i=(i+1)%queue->size) { printf("%3d ",queue->data[i]); }#if 0 i=queue->head; while(i!=queue->tail) { printf("%3d ",queue->data[i]); i=(i+1)%queue->size; }#endif printf("\n"); return 0;}
真打印 (再造一个队列):
int printf_queue_real(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return -1; queue_t* temp=create_queue(queue->size); // queue_t* temp=create_queue(length_queue(queue)+1); int data; while(!isnull_queue(queue)) { pop_queue(queue,&data); printf("%3d ",data); push_queue(temp,data); } while(!isnull_queue(temp)) { pop_queue(temp,&data); push_queue(queue,data); } destroy_queue(&temp); printf("\n"); return 0;}
真逆打印 (造两个栈 实现一个队列):
int reprintf_queue_real(queue_t* queue){ if(queue==NULL||isnull_queue(queue)) return -1; int *s1=malloc(sizeof(int)*length_queue(queue)); int *s2=malloc(sizeof(int)*length_queue(queue)); int top1=-1; int top2=-1; int data; while(!isnull_queue(queue)) { pop_queue(queue,&data); //出队 s1[++top1]=data; //入栈 第1步 } while(top1!=-1) { data=s1[top1--]; //出栈,此时已倒序 第2步 printf("%3d ",data); s2[++top2]=data; //入第二个栈 } while(top2!=-1) { data=s2[top2--];//出栈,顺序再次逆,变为正 第3步 push_queue(queue,data); //入队 } printf("\n"); //销毁 free(s1); free(s2); return 0;}
长度:
int length_queue(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return 0; return (queue->tail-queue->head+queue->size)%queue->size;}
清空:
int clear_queue(queue_t* queue) { if(queue==NULL) return -1; queue->head=queue->tail=0; return 0;}
销毁:
int destroy_queue(queue_t** queue) { if(*queue==NULL) return -1; free((*queue)->data); free(*queue); *queue=NULL; return 0;}
main函数:
#include #include typedef struct node { int * data;//存数据元素 int size;//标记数据元素个数 int head;//即将要取数据的位置(下标) int tail;//即将要存数据的位置(下标)}queue_t;//顺序循环队列的数据类型int main(int argc, const char *argv[]){ queue_t* queue=create_queue(30); int i; for(i=1;i<=20;i++) { push_queue(queue,i); //printf_fromzero_fake(queue); printf_from_h_to_t_fake(queue); } printf("length_queue:%d\n",length_queue(queue));#if 0 int data; for(i=1;i<=20;i++) { pop_queue(queue,&data); printf("pop data:%d\n",data); //printf_fromzero_fake(queue); printf_from_h_to_t_fake(queue); }#endif printf("real printf:"); printf_queue_real(queue); printf("reprintf_queue_real:\n"); reprintf_queue_real(queue); clear_queue(queue); printf("length_queue:%d\n",length_queue(queue)); destroy_queue(&queue); if(queue==NULL) { printf("success destroyed\n"); } return 0;}
链式队列
链式队列,首先是链表,加上两个指向头和尾的指针!
采用尾进头出:
原因:头部插删都方便,而尾部插方便,删不方便!
定义结构体(一个链表和一个头尾):
typedef struct node1 { int data;//数据域 struct node1 * next;//指针域}list_t;//结点的数据类型typedef struct node2 { list_t* head;//头指针,指向链式队列的头 list_t* tail;//尾指针,指向链式队列的尾巴}queue_t;//链式队列的数据类型
创建:头尾都指向第一个空节点。
queue_t* create_queue(void) { //申请队列标识即申请头指针和尾指针的存储空间 queue_t* queue=malloc(sizeof(queue_t)); if(queue== NULL) { printf("队列标识申请失败!\n"); return NULL; } //申请头结点的空间 queue->head=malloc(sizeof(list_t)); if(head == NULL) { printf("头结点申请失败!\n"); return NULL; } //头结点初始化 queue->head->next=NULL; //初始化头指针和尾指针,初始化状态下,约定头尾指针均指向头结点 queue->tail=queue->head; return queue;}
判空:
int isnull_queue(queue_t* queue) { return (queue->head->next==NULL&&queue->tail->next==NULL);//通过链表判断 //return queue->head == queue->tail; //通过头尾判断}
入队:通过尾部入队,利用尾指针增加
int push_queue(queue_t* queue,int data) { if(queue==NULL) return -1; //1 申请新节点的空间 list_t* newnode=malloc(sizeof(list_t)); //2 存入数据 newnode->data=data; newnode->next=NULL; //3 将新节点连接到链表中 queue->tail->next=newnode; //4 修改尾指针tail的指向,指向新的尾巴结点 queue->tail=newnode; return 0;}
出队:
int pop_queue(queue_t* queue,int* data) { if(queue==NULL||isnull_queue(queue)) return -1; //备份出队的结点 list_t* temp=queue->head->next; //获取出队的数据 *data=temp->data; //数据出队 queue->head->next=temp->next; //如果出队数据后,队列为空,要找回尾指针tail的指向 if(temp->next==NULL) queue->tail=queue->head; //释放出队数据的结点 free(temp); return 0;}
假打印:
int print_queue_fake(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return -1; //标记头结点 list_t* temp=queue->head; //循环打印数据 while(temp->next!=NULL) { //跳过头结点 printf("%3d ",temp->next->data); temp=temp->next; } printf("\n"); return 0;}
真打印(需要出队):
int print_queue_real(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return -1; //创建一个临时队列 queue_t* temp=create_queue(); int data; while(!isnull_queue(queue)) { //pop_queue 出队 打印 pop_queue(queue,&data); printf("%3d ",data); //push_queue 入临时的队列存放 push_queue(temp,data); } printf("\n"); while(!isnull_queue(temp)) { //数据放回原队列 pop_queue(temp,&data); push_queue(queue,data); } //销毁临时队列 destroy_queue(&temp); return 0;}
真逆打印(两个栈实现一个队列,和顺序队列一样):
int reprintf_queue_real(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return -1; int *s1=malloc(sizeof(int)*length_queue(queue)); int *s2=malloc(sizeof(int)*length_queue(queue)); int top1=-1; int top2=-1; int data; while(!isnull_queue(queue)) { pop_queue(queue,&data); //出队 s1[++top1]=data; //入栈 第1步 } while(top1!=-1) { data=s1[top1--]; //出栈,此时已倒序 第2步 printf("%3d ",data); s2[++top2]=data; //入第二个栈 } while(top2!=-1) { data=s2[top2--];//出栈,顺序再次逆,变为正 第3步 push_queue(queue,data); //入队 } printf("\n"); //销毁 free(s1); free(s2); return 0;}
长度:
int length_queue(queue_t* queue) { if(queue==NULL||isnull_queue(queue)) return 0; //标记头结点 list_t* temp=queue->head; //定义保存长度的变量 int sum=0; //循环判断,只要队列不为空,sum++ while(temp->next!=NULL) { //跳过头结点 sum++; temp=temp->next; } //返回长度 return sum;}
清空:
int clear_queue(queue_t* queue) { if(queue==NULL) return -1; int data; //只要不为空,就执行出队数据操作 while(!isnull_queue(queue)) { pop_queue(queue,&data); } return 0;}
销毁:
int destroy_queue(queue_t** queue) { if(*queue==NULL) return -1; //清空 if(!isnull_queue(*queue)) { clear_queue(*queue); } //释放 free((*queue)->head); free(*queue); *queue=NULL; return 0;}
main函数:
int main(int argc, const char *argv[]){ queue_t* queue=create_queue(); int i; for(i=1;i<=20;i++) { push_queue(queue,i); print_queue_fake(queue); }#if 0 int data; for(i=1;i<=20;i++) { if(pop_queue(queue,&data)==0) printf("popdata:%d\n",data); print_queue_fake(queue); }#endif printf("real printf:"); print_queue_real(queue); printf("real reprintf:"); reprint_queue_real(queue); printf("length_queue:%d\n",length_queue(queue)); if(clear_queue(queue)==0) { printf("success cleared!\n"); printf("length_queue:%d\n",length_queue(queue)); } push_queue(queue,888); push_queue(queue,666); print_queue_fake(queue); destroy_queue(&queue); if(queue==NULL) { printf("success destroyed!\n"); } return 0;}