队列的分类
队列一般分为三种:顺序队列、循环队列、链式队列
其中顺序队列和循环队列按照存储方式又可以分为动态和静态,链式队列是动态的。动态表示队列元素采用动态内存分配,静态表示队列元素采用数组的方式分配。
顺序队列
顺序队列存在”假溢出“现象:即队头由于出队操作,还有剩余空间,但队尾指针已达到数组的末尾,如果继续插入元素,队尾指针就会越出数组的上界,而造成“溢出”,这种溢出不是因为存储空间不够而产生的溢出,这种溢出不是因为存储空间不够而产生的溢出,像这种有存储空间而不能插入元素的操作称为“假溢出“。
由于存在顺序队列存在”假溢出“现象,因此一般使用顺序队列。
循环队列
关于判断队满和队空
当循环队列为空或为满时,队尾和队头指针都指向同一个元素,可以通过下面几种方式判断队列空或队列满:
(1)、牺牲一个队列元素用来识别队列满和队列空
(2)、通过计数器来判断队列满和队列空
(3)、通过一个读写标志来判断队列满和队列空
静态循环队列
采用静态循环队列来展示三种队列满和队列空的判断方法,举例如下:
#include#include #include <string.h> #include #define queue_max_length 5 #if 1 //牺牲一个队列元素用来识别队列满和队列空 typedef struct queue_srt { int front; int rear; int queue_data[queue_max_length]; } queue_srt; int queue_init(queue_srt *q_srt) { q_srt->front = q_srt->rear = 0; memset(q_srt->queue_data, 0, sizeof(q_srt->queue_data)); printf("queue init success\n"); return 0; } int queue_read(queue_srt *q_srt, int *value) { //判断队列是否为空 if (q_srt->front == q_srt->rear) { printf("the queue is empty\n"); return 1; } *value = q_srt->queue_data[q_srt->front]; q_srt->front = (q_srt->front + 1) % queue_max_length; return 0; } int queue_write(queue_srt *q_srt, int value) { //牺牲一个队列元素用来判断队列满 if (((q_srt->rear + 1)%queue_max_length) == q_srt->front) { printf("the queue is full\n"); return 1; } q_srt->queue_data[q_srt->rear] = value; q_srt->rear = (q_srt->rear + 1) % queue_max_length; return 0; } int queue_length(queue_srt *q_srt) { //计算队列长度 int length = (q_srt->rear - q_srt->front + queue_max_length) % queue_max_length; return length; } #endif #if 0 //通过计数器来判断队列满和队列空 typedef struct queue_srt { int front; int rear; int queue_data[queue_max_length]; int cnt; //计数器,用来记录队列长度 } queue_srt; int queue_init(queue_srt *q_srt) { q_srt->front = q_srt->rear = 0; q_srt->cnt = 0; memset(q_srt->queue_data, 0, sizeof(q_srt->queue_data)); printf("queue init success\n"); return 0; } int queue_read(queue_srt *q_srt, int *value) { //通过判断计数器来识别队列是否已经空 if (q_srt->cnt == 0) { printf("the queue is empty\n"); return 1; } *value = q_srt->queue_data[q_srt->front]; q_srt->front = (q_srt->front + 1) % queue_max_length; q_srt->cnt--;//读完数据,计数器减1 return 0; } int queue_write(queue_srt *q_srt, int value) { //通过判断计数器来识别队列是否已经满 if (q_srt->cnt == queue_max_length) { printf("the queue is full\n"); return 1; } q_srt->queue_data[q_srt->rear] = value; q_srt->rear = (q_srt->rear + 1) % queue_max_length; q_srt->cnt++; //写完数据,计数器加1 return 0; } int queue_length(queue_srt *q_srt) { //通过计数器获取队列长度 int length = q_srt->cnt; return length; } #endif #if 0 //通过一个读写标志来判断队列满和队列空 typedef struct queue_srt { int front; int rear; int queue_data[queue_max_length]; char rw_flag; //读写标志 w:表示写完队列 r:表示读完队列 } queue_srt; int queue_init(queue_srt *q_srt) { q_srt->front = q_srt->rear = 0; q_srt->rw_flag = 'r'; //初始为 r ,表示队列为空 memset(q_srt->queue_data, 0, sizeof(q_srt->queue_data)); printf("queue init success\n"); return 0; } int queue_read(queue_srt *q_srt, int *value) { //通过读写标志判断队列是否为空 if ((q_srt->front == q_srt->rear) && (q_srt->rw_flag == 'r')) { printf("the queue is empty\n"); return 1; } *value = q_srt->queue_data[q_srt->front]; q_srt->front = (q_srt->front + 1) % queue_max_length; q_srt->rw_flag = 'r'; //读完队列,读写标志设置r return 0; } int queue_write(queue_srt *q_srt, int value) { //通过读写标志判断队列是否为空 if ((q_srt->front == q_srt->rear) && (q_srt->rw_flag == 'w')) { printf("the queue is full\n"); return 1; } q_srt->queue_data[q_srt->rear] = value; q_srt->rear = (q_srt->rear + 1) % queue_max_length; q_srt->rw_flag = 'w'; //写完队列,读写标志设置w return 0; } int queue_length(queue_srt *q_srt) { int length = 0; if ((q_srt->front == q_srt->rear) && (q_srt->rw_flag == 'w')) //通过读写标志判断是否队满 { length = queue_max_length; } else { length = (q_srt->rear - q_srt->front + queue_max_length) % queue_max_length; } return length; } #endif int main(void) { int action = 0; int value = 0; queue_srt testqueue; queue_init(&testqueue); while (1) { printf("1:write 2:read 3:get length\n"); printf("enter action:"); scanf("%d", &action); switch(action) { case 1: printf("write queue to data:"); scanf("%d", &value); queue_write(&testqueue, value); break; case 2: if (1 == queue_read(&testqueue, &value)) continue; printf("read data from queue: %d\n", value); break; case 3: value = queue_length(&testqueue); printf("the length of queue is: %d\n", value); break; default: printf("no such action\n"); } } return 0; }
动态循环队列
下面展示一个动态循环队列,队列元素采用动态内存分配,举例如下:
#include#include #include <string.h> #include #define queue_max_length 5 typedef struct queue_srt { int *front; //指向队列头 int *rear; //指向队列尾 int *queue_data; //指向队列动态内存 int *end; //指向队列存储空间之后 } queue_srt; int queue_init(queue_srt *q_srt) { q_srt->queue_data = (int *) (malloc)(sizeof(int) * queue_max_length); if (NULL == q_srt) { printf("malloc mem failed,queue init failed\n"); return 1; } q_srt->front = q_srt->rear = q_srt->queue_data; //指向队列首部 q_srt->end = q_srt->queue_data + queue_max_length; //指向队列存储空间之后 printf("queue init success\n"); return 0; } int queue_read(queue_srt *q_srt, int *value) { if (q_srt->front == q_srt->rear) //判断队列是否为空 { printf("the queue is empty\n"); return 1; } *value = *(q_srt->front); if ((q_srt->front + 1) >= q_srt->end) //判断队头指针是否到队列尾 { q_srt->front = q_srt->queue_data; } else { q_srt->front = q_srt->front + 1; } return 0; } int queue_write(queue_srt *q_srt, int value) { //牺牲一个队列元素用来识别队列满 int *q_tmp = (q_srt->rear + 1 >= q_srt->end)? (q_srt->queue_data) : (q_srt->rear + 1); if (q_tmp == q_srt->front) { printf("the queue is full\n"); return 1; } *(q_srt->rear) = value; q_srt->rear = q_tmp; return 0; } int queue_length(queue_srt *q_srt) { int length = 0; length = (q_srt->rear - q_srt->front + queue_max_length) % queue_max_length;return length; //获取队列长度 } int main(void) { int action = 0; int value = 0; queue_srt testqueue; queue_init(&testqueue); while (1) { printf("1:write 2:read 3:get length\n"); printf("enter action:"); scanf("%d", &action); switch(action) { case 1: printf("write queue to data:"); scanf("%d", &value); queue_write(&testqueue, value); break; case 2: if (1 == queue_read(&testqueue, &value)) continue; printf("read data from queue: %d\n", value); break; case 3: value = queue_length(&testqueue); printf("the length of queue is: %d\n", value); break; default: printf("no such action\n"); } } free(testqueue.queue_data); return 0; }
链式队列
链式队列主要有两个特点:1、队列是动态的,即队列元素采用动态内存分配 2、队列采用链表的逻辑来组织,因此队列的长度没有限制,这是跟循环队列最大的差别
编写一个带头结点的链式队列,举例如下:
#include#include typedef struct LKNODE { int data; //保存节点数据 struct LKNODE *next; //指向下一个节点 } LKNODE; typedef struct LINKQUEUE //队列结构体,采用头尾指针构建链式队列 { LKNODE *front; //头指针,指向队列头结点 LKNODE *rear; //尾指针,指向队列尾节点 }LINKQUEUE; //初始化链式队列 int InitLinkQueue(LINKQUEUE *lkqueue) { LKNODE *head = (LKNODE*) malloc(sizeof(LKNODE)); //创建头结点 if (NULL == head) { printf("creat head node failed,init linkqueue failed\n"); return 1; } head->next = NULL; //这里要head节点的next设置为NULL,避免头尾指针的next指向未知 //头尾指针指向头结点 lkqueue->front = lkqueue->rear = head; return 0; } //节点入队 int WriteLinkQueue(LINKQUEUE *lkqueue, int value) { //创建新节点 LKNODE *newnode = (LKNODE *)malloc(sizeof(LKNODE)); if (NULL == newnode) { printf("creat newnode failed,write queue failed\n"); return 1; } newnode->data = value; newnode->next = NULL; //这里要指向空,避免队列为空后,头结点的next指针指向未知 //尾指针连接新节点 lkqueue->rear->next = newnode; //尾指针指向新节点 lkqueue->rear = newnode; return 0; } //节点出队 int ReadLinkQueue(LINKQUEUE *lkqueue, int *value) { //LKNODE *temp = NULL; LKNODE *readnode = NULL; //判断队列是否为空队列 if (lkqueue->front == lkqueue->rear) { printf("link queue is empty\n"); return 1; } //获取读取节点值 readnode = lkqueue->front->next; *value = readnode->data;
//头结点连接读取节点的下一个节点 lkqueue->front->next = readnode->next; //如果readnode是最后一个节点,则头结点的next会指向NULL //判断读取的节点是不是最后一个节点,防止释放读取节点后尾指针指向空 if (readnode == lkqueue->rear) { lkqueue->rear = lkqueue->front; //队列为空,尾指针指向头结点 } //释放读取节点 free(readnode); return 0; } //获取队列长度 int GetLinkQueueLen(LINKQUEUE *lkqueue) { int length = NULL; LKNODE *temp = NULL; //创建临时节点,因为头尾指针的位置不能动 temp = lkqueue->front->next; //获取头结点的下一个节点 if (NULL == temp) //判断队列是否为空 { length = 0; } else { while(temp != lkqueue->rear) //判断是不是读到尾节点 { length++; temp = temp->next; } length++; //加上最后一个节点 } return length; } //清空队列 void ClearLinkQueue(LINKQUEUE *lkqueue) { int value; int ret; while(1) { //读取队列元素,释放动态内存,防止内存泄漏 ret = ReadLinkQueue(lkqueue, &value); if (1 == ret) //判断队列是否读完 { printf("clear the link queue success\n"); break; } } } //销毁队列 void DestroyLinkQueue(LINKQUEUE *lkqueue) { //先清空队列,释放队列的动态内存 ClearLinkQueue(lkqueue); //释放头结点的动态内存 free(lkqueue->front); //头尾指针指向空 lkqueue->front = lkqueue->rear = NULL; printf("destroy the link queue success\n"); } //遍历队列 void TraverseLinkQueue(LINKQUEUE *lkqueue) { int value; int ret = 0; int num = 0; LKNODE *temp = lkqueue->front->next; while(1) { if (NULL != temp) //判断队列是否为空 { num++; printf("%d node data is:%d\n", num, temp->data); temp = temp->next; } else { printf("traverse link queue success\n"); break; } } } int main() { int action = 0; int value = 0; int ret = 0; LINKQUEUE testlkqueue = {NULL, NULL}; InitLinkQueue(&testlkqueue); //初始化链式队列 while(1) { printf("**********\n1 write 2 read 3 getlength 4 Traverse 5 Clear link queue 6 destroy link queue\n**********\n"); printf("enter action:"); scanf("%d", &action); switch(action) { case 1: printf("enter the value:"); scanf("%d", &value); WriteLinkQueue(&testlkqueue, value); break; case 2: ret = ReadLinkQueue(&testlkqueue, &value); if (1 != ret) { printf("the readnode data is:%d\n", value); } break; case 3: ret = GetLinkQueueLen(&testlkqueue); printf("the length of link queue is:%d\n", ret); break; case 4: TraverseLinkQueue(&testlkqueue); break; case 5: ClearLinkQueue(&testlkqueue); break; case 6: DestroyLinkQueue(&testlkqueue); return 0; break; default: break; } } return 0; }