目录
顺序队列
思路 :先进先出,可以想象成不能插队的排队,其本质也是受限制的线性表,只允许在一端插入,在另一端取出
顺序实现:
数组:人为抽象理解为环状数组
判空:队头与队尾相等为空
判满:(队尾下标+1)%最大元素个数 == 队头下标
队列元素有:存储数据的空间、队头和队尾下标、队列最大元素个数自定义头文件_SEQUEUE_H_:
#ifndef _SEQUEUE_H_ #define _SEQUEUE_H_ #include <stdio.h> #include <stdlib.h> typedef struct node{ int maxlen; int front,rear; char *str;//数组的首地址 }Sequeue,*pSequeue; //初始化顺序队列 int queue_create(pSequeue *Q,int maxlen); //队列判空 int queue_empty(pSequeue q); //队列判满 int queue_full(pSequeue q); //入队 int queue_in(pSequeue q,char ch); //出队 char queue_out(pSequeue q,char *ch); //队列show int queue_show(pSequeue q); //清空队列 int queue_clear(pSequeue q); //释放队列 int queue_destory(pSequeue *Q); #endif
sequeue.c:
#include "sequeue.h" //初始化顺序循环队列 int queue_create(pSequeue *Q,int maxlen) { *Q = malloc(sizeof(Sequeue)); if(NULL == *Q){ perror("queue_malloc"); return -1; } (*Q)->str = malloc(maxlen*sizeof(char)); if(NULL == (*Q)->str){ perror("data_malloc"); free(*Q); *Q = NULL; return -1; } (*Q)->maxlen = maxlen; (*Q)->front = (*Q)->rear = 0; return 0; } //队列判空 int queue_empty(pSequeue q) { if(NULL == q) return -1; //队头队尾可以不等于0,只要相等即可 if(q->front == q->rear) return 0; return -1; } //队列判满 int queue_full(pSequeue q) { if(NULL == q) return -1; if((q->rear + 1) % q->maxlen == q->front) return 0; return -1; } //入队 int queue_in(pSequeue q,char ch) { if(NULL==q || 0==queue_full(q)) return -1; //约定队尾没有元素,将队尾元素赋给队尾,队尾再加1 //这样做只是为了解决"假溢出",空出队尾空间 q->str[q->rear] = ch; q->rear = (q->rear+1) % q->maxlen; return 0; } //出队 char queue_out(pSequeue q,char *ch) { if(NULL==q || 0==queue_empty(q)) return -1; *ch = q->str[q->rear-1]; q->front = (q->front+1) % q->maxlen; if(NULL == ch) return 0; else return *ch; // if(NULL == ch){ // char t;//等价于上面那些代码 // t = q->str[q->front]; // q->front = (q->front+1)%q->maxlen; // return t; // } // *ch = q->str[q->front]; // q->front = (q->front+1)%q->maxlen; // return *ch; } //队列show int queue_show(pSequeue q) { if(NULL==q || 0==queue_empty(q)) return -1; int flag = q->front; while(flag != q->rear){ printf("%c ",q->str[flag]); flag = (flag+1) % q->maxlen; } // for(int i=q->front;i!=q->rear;i=(i+1)%q->maxlen) // printf("%c ",q->str[i]); puts(""); return 0; } //清空队列 int queue_clear(pSequeue q) { if(NULL == q) return -1; q->front = q->rear; return 0; } //释放队列 int queue_destory(pSequeue *Q) { if(NULL == *Q) return -1; free((*Q)->str); free(*Q); *Q = NULL; return 0; }
main.c:
#include "sequeue.h" int queue_create(pSequeue *Q,int maxlen); int queue_empty(pSequeue q); int queue_full(pSequeue q); int queue_in(pSequeue q,char ch); char queue_out(pSequeue q,char *ch); int queue_show(pSequeue q); int queue_clear(pSequeue q); int queue_destory(pSequeue *Q); int main(int argc, char *argv[]) { pSequeue p = NULL; queue_create(&p,10); char ch; while(1){ fputs("please queue_in data :",stdout); //if(((0<=ch && 9>=ch) || ('0'<=ch && '9'>=ch))=scanf("%c",&ch)) //这样判断不行,因为键盘上的按键都是字符,另外,下面还有一点 scanf("%c%*c",&ch);//%*c也可以读取脏字符 if(ch>='0' && ch <= '9'){ // getchar(); //这里用getchar()会导致只有输入的是0~9的字符才会读取脏字符 break; } queue_in(p,ch); } queue_show(p); while(1){ printf("温馨提示:一个字母代表出队一次.\n"); fputs("please queue_out data :",stdout); scanf("%c%*c",&ch); if(ch>='0' && ch <= '9') break; queue_out(p,&ch); }//也可以用出队几次来操作,注意范围即可 queue_show(p); queue_clear(p); queue_show(p); queue_destory(&p); if(NULL == &p) printf("this NULL.\n"); return 0; }
结果:
root@ubuntu:/mnt/U_share/DATA/queue/sequeue# make gcc ./src/*.c -o queue -I ./inc/ root@ubuntu:/mnt/U_share/DATA/queue/sequeue# ./queue please queue_in data :a please queue_in data :d please queue_in data :c please queue_in data :w please queue_in data :z please queue_in data :x please queue_in data :g please queue_in data :1 a d c w z x g 温馨提示:一个字母代表出队一次. please queue_out data :a 温馨提示:一个字母代表出队一次. please queue_out data :c 温馨提示:一个字母代表出队一次. please queue_out data :w 温馨提示:一个字母代表出队一次. please queue_out data :1 w z x g
总结:
解决加溢出问题,也可判断队头、队尾重逢了几次,奇数次为空,偶数次为满
缺点:每次出入队列都要判断,一个标志变量等,很麻烦留出尾指针指向的空间,现在看着很大,但是以后队列中存放数据多了,将是100 1000 10000分之一
对于顺序队列,先++或者后++无影响,不过要一致对应,不过最后一个需要判断,所以可以进行求余判空满,例如:q->rear = (q->rear+1) % q->maxlen;
链队列
思路:受限制的顺序表,还是队列,先进先出
注意:队头、队尾指针不能被放在一起,也就是在一个结构体内,因为放在一个结构体内,会导致每个结点都有一个队头、队尾,操作不当时会导致对队列内部的结点进行操作,不符合队列的特性,因此要重新定义一个指针,指向队列的头和尾,也就是需要两个结构体。
而且也很浪费空间,比如100个,要浪费99个两个(头尾)结点的空间。
简单:头结点不存数据
复杂:头结点存数据不牺牲存储空间,设置size,定义一个变量
size
用于记录队列此时记录了几个数据元素,初始化size = 0
,进队成功size++
,出队成功size--
,根据size的值判断队满与队空=====================================================================
数据结点:
typedef struct node{
int data;
struct node *next;
}Node,*pNode;
队列结点:
typedef struct queue{
pNode front,rear;//结构体指针,由于使用了pNode,所以不能更换俩结点声明顺序
}Linkqueue,*pLinkqueue;开辟:先开辟上面,要用一个指针去接受,然后开辟下面时,再赋值进去,这里浪费了一个指针。
先开辟下面,只需要用这个指针来接收上面这个即可声明:先声明数据结点,再声明队列结点,不然不知道队列结点里面是啥,有个依赖关系。而且需要注意,声明队列结点时是用的数据结点的结构体指针。
声明不能换顺序,开辟可以换顺序====================================================================
linkqueue.h:
#ifndef _LINKQUEUE_H_ #define _LINKQUEUE_H_ #include <stdio.h> #include <stdlib.h> //数据节点的声明 typedef struct node{ int data; struct node *next; }Node, *pNode; //队列节点的声明 typedef struct queue{ pNode front, rear; }Linkqueue, *pLinkqueue; //二者声明不能换顺序,开辟可以换 //参数初始化链队列 int queue_create(pLinkqueue *Q); //链队列判空 int queue_empty(pLinkqueue q); //入队 int queue_in(pLinkqueue q, int data); //出队 int queue_out(pLinkqueue q); //清空队列 int queue_clear(pLinkqueue q); //释放队列 int queue_destory(pLinkqueue *Q); //队列show int queue_show(pLinkqueue q); #endif
linkqueue.c:
#include "linkqueue.h" //参数初始化链队列 int queue_create(pLinkqueue *Q) { //开辟队列结点 *Q = malloc(sizeof(Linkqueue)); if(NULL == *Q){ perror("queue_malloc"); return -1; } (*Q)->front = NULL; (*Q)->rear = NULL; return 0; } //链队列判空 int queue_empty(pLinkqueue q) { if(NULL == q) return -1; if(q->front==NULL || q->rear==NULL)//&& return 0; return -1; } //入队 int queue_in(pLinkqueue q, int data) { if(NULL == q) return -1; //开辟数据结点 pNode p = malloc(sizeof(Node)); if(NULL == p){ perror("data_malloc"); return -1; } p->data = data; p->next = NULL; if(0 == queue_empty(q)){ q->front = p; q->rear = p; }else{ //把 p 接到队列的尾巴上 q->rear->next = p; //把 q->rear 指向新的尾结点 q->rear = p; } return 0; } //出队 int queue_out(pLinkqueue q) { if(NULL==q || 0==queue_empty(q)) return 0; int data = q->front->data; pNode p = q->front; //判断要释放的结点是不是队列中的最后一个结点 if(q->front == q->rear) q->front = q->rear = NULL; else q->front = p->next; free(p); return data; } //清空队列 int queue_clear(pLinkqueue q) { if(NULL==q || 0==queue_empty(q)) return -1; //用一个临时变量 t 指向待释放的结点 pNode p = q->front,t; while(p){ t = p; p = p->next; free(t); } q->front = q->rear = NULL; return 0; } //释放队列 int queue_destory(pLinkqueue *Q) { if(NULL == *Q) return -1; //释放除头结点以外的所有结点 queue_clear(*Q); //释放队列结点 free(*Q); *Q = NULL; return 0; } //队列show int queue_show(pLinkqueue q) { if(NULL==q || 0==queue_empty(q)) return -1; pNode p = q->front; while(p){ printf("%d ",p->data); p = p->next; } puts(""); return 0; }
main.c:
#include "linkqueue.h" int queue_create(pLinkqueue *Q); int queue_empty(pLinkqueue q); int queue_in(pLinkqueue q, int data); int queue_out(pLinkqueue q); int queue_clear(pLinkqueue q); int queue_destory(pLinkqueue *Q); int queue_show(pLinkqueue q); int main(int argc, char *argv[]) { pLinkqueue q = NULL; queue_create(&q); int data; while(1){ fputs("please queue_in data : ",stdout); if(0 == scanf("%d",&data)){ getchar(); break; } queue_in(q,data); } queue_show(q); printf("queue_out data:%d\n",queue_out(q)); queue_show(q); queue_clear(q); queue_in(q,101); queue_show(q); queue_destory(&q); if(NULL == q) fputs("this NULL.\n",stdout); return 0; }
结果:
root@ubuntu:/mnt/U_share/DATA/queue/linkqueue# make gcc ./src/*.c -o queue -I ./inc/ root@ubuntu:/mnt/U_share/DATA/queue/linkqueue# ./queue please queue_in data : 1 please queue_in data : 1 please queue_in data : 2 please queue_in data : 3 please queue_in data : 3 please queue_in data : q 1 1 2 3 3 queue_out data:1 1 2 3 3 101 this NULL.
总结:
修改指针要用二级指针
一个元素再次出队会段错误
创建头结点只需判空,不用询问是否创建头结点,因为创建的多了,用户根本不知道是不是创建的头结点,只需要判断头结点是否为空即可,循环执行的时候会自动先创建头结点.