队列
- 一.队列的概念及结构
- 二.顺序队列与链队列
- 1.顺序队列
- 2.链队列
- 三.链队列的实现
- 1.创建队列
- 2.初始化队列
- 3.入队
- 4.出队
- 5.获取队头元素
- 6.获取队尾元素
- 7.队列的大小
- 8.队列的判空
- 9.清空队列
- 10.销毁队列
- 四.队列的盲区
- 五.模块化源代码
- 1.Queue.h
- 2.Queue.c
- 3.test.c
- 六.栈和队列必做OJ题
一.队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先
进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
二.顺序队列与链队列
队列也可以数组和链表的结构实现,使用链表的结构实现更优
一些,因为如果使用数组的结构,
出队列在数组头上出数据,效率会比较低。
1.顺序队列
2.链队列
那是不是再用一个指针指向队尾就可以了呢?是的如下图:
这种链表结构完美的解决了尾插时效率低的问题。
三.链队列的实现
1.创建队列
先创建结构体(存放数据和先一个节点的地址)用来表示节点,再额外创建队列结构体(成员:队头指针和队尾指针),队头指针指向队列的队头,队尾指针指向队列的队尾。
typedef int QDataType;typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* head;QNode* tail;
}Queue;Queue q;//q代表链队列
2.初始化队列
将队头指针和队尾指针初始化为NULL。
void QueueInit(Queue* pq)
{assert(pq);//断言pq->head = pq->tail = NULL;
}
3.入队
先是创建一个新的节点,存放数据和NULL指针,入队分两种情况:
- 若队头和队尾指针都为NULL,则队头和队尾指针都指向新的节点。
- 若队头和队尾指针不为NULL,则向队尾插入新的节点,再更新队尾指针。
void QueuePush(Queue* pq, QDataType x)
{assert(pq);//创建入队的节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL)//开辟失败{perror("malloc fail!");exit(-1);}//开辟成功newnode->data = x;newnode->next = NULL;//入队操作if (pq->tail == NULL){pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}
}
4.出队
出队也有两种情况
- 队列只有一个节点,先删除队头,再让队头和队尾指针指向NULL,防止队尾指针变成野指针。
- 队列有多个节点,先保存队头的下一个节点指针,再删除队头,最后更新队头指针。
void QueuePop(Queue* pq)
{assert(pq);assert(pq->head != NULL);//队列不能为空//队列只有一个节点if (pq->tail->next == NULL){free(pq->head);pq->head = pq->tail = NULL;}//队列有多个节点else{QNode* next = pq->head->next;free(pq->head);pq->head = next;}
}
5.获取队头元素
QDataType QueueFront(Queue* pq)
{assert(pq);assert(pq->head != NULL);//队列不能为空return pq->head->data;
}
6.获取队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);assert(pq->tail != NULL);//队列不能为空return pq->tail->data;
}
7.队列的大小
定位队头节点,利用NULL这一条件遍历队列即可。
int QueueSize(Queue* pq)
{assert(pq);QNode* cur = pq->head;//定位队头节点int size = 0;while (cur != NULL){size++;cur = cur->next;//更新cur}return size;
}
8.队列的判空
判断队头指针是否为NULL即可。
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}
9.清空队列
定位队头节点,依次先后删除即可。
void QueueClear(Queue* pq)
{assert(pq);QNode* cur = pq->head;//定位队头节点while (cur != NULL){QNode* next = cur->next;free(cur);//逐个删除cur = next;}pq->head = pq->tail = NULL;
}
10.销毁队列
void QueueDestory(Queue* pq)
{assert(pq);QueueClear(pq);//注意不能释放pq,这不是动态开辟的地址,而是栈区的地址,所以清空与销毁没什么区别
}
四.队列的盲区
由于队列的特殊结构,只能遵循先进先出的原则,不允许随便遍历队列,否则就失去了队列的特点,只能用以下的代码得到数据:
while (!QueueEmpty(&q))
{printf("%d ", QueueFront(&q));QueuePop(&q);
}
五.模块化源代码
1.Queue.h
//#pragma once 防止头文件被重复包含
#ifndef __QUEUE_H__
#define __QUEUE_H__#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int QDataType;typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QNode;typedef struct Queue
{QNode* head;QNode* tail;
}Queue;void QueueInit(Queue* pq);//初始化队列void QueuePush(Queue* pq, QDataType x);//入队void QueuePop(Queue* pq);//出队QDataType QueueFront(Queue* pq);//获取队头元素QDataType QueueBack(Queue* pq);//获取队尾元素int QueueSize(Queue* pq);//队列大小bool QueueEmpty(Queue* pq);//队列判空void QueueClear(Queue* pq);//清空队列void QueueDestory(Queue* pq);//销毁队列
#endif
2.Queue.c
#define _CRT_SECURE_NO_WARNINGS 1#include"Queue.h"void QueueInit(Queue* pq)
{assert(pq);//断言pq->head = pq->tail = NULL;
}void QueuePush(Queue* pq, QDataType x)
{assert(pq);//创建入队的节点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL)//开辟失败{perror("malloc fail!");exit(-1);}//开辟成功newnode->data = x;newnode->next = NULL;//入队操作if (pq->tail == NULL){pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}
}void QueuePop(Queue* pq)
{assert(pq);assert(pq->head != NULL);//队列不能为空//队列只有一个节点if (pq->tail->next == NULL){free(pq->head);pq->head = pq->tail = NULL;}//队列有多个节点else{QNode* next = pq->head->next;free(pq->head);pq->head = next;}
}QDataType QueueFront(Queue* pq)
{assert(pq);assert(pq->head != NULL);//队列不能为空return pq->head->data;
}QDataType QueueBack(Queue* pq)
{assert(pq);assert(pq->tail != NULL);//队列不能为空return pq->tail->data;
}int QueueSize(Queue* pq)
{assert(pq);QNode* cur = pq->head;//定位队头节点int size = 0;while (cur != NULL){size++;cur = cur->next;//更新cur}return size;
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}void QueueClear(Queue* pq)
{assert(pq);QNode* cur = pq->head;//定位队头节点while (cur != NULL){QNode* next = cur->next;free(cur);//逐个删除cur = next;}pq->head = pq->tail = NULL;
}void QueueDestory(Queue* pq)
{assert(pq);QueueClear(pq);//注意不能释放pq,这不是动态开辟的地址,而是栈区的地址,所以清空与销毁没什么区别
}
3.test.c
#define _CRT_SECURE_NO_WARNINGS 1#include"Queue.h"enum //匿名枚举
{EXIT,PUSH,POP,FRONT,BACK,SIZE,EMPTY,CLEAR
};void Menu()
{printf("*************队列*************\n");printf("****1.入队 2.出队****\n");printf("****3.队头 4.队尾****\n");printf("****5.大小 6.判空****\n");printf("****7.清空 0.退出****\n");printf("******************************\n");
}int main()
{Queue q;QueueInit(&q);int select = 0;bool flag;QDataType value;do{Menu();printf("请选择您的操作:");scanf("%d", &select);switch (select){case EXIT:printf("退出队列!\n");break;case PUSH:printf("请输入您要入队的值:");scanf("%d", &value);QueuePush(&q, value);break;case POP:QueuePop(&q);break;case FRONT:value = QueueFront(&q);printf("队头元素值为:%d\n", value);break;case BACK:value = QueueBack(&q);printf("队尾元素值为:%d\n", value);break;case SIZE:printf("队列的大小为:%d\n", QueueSize(&q));break;case EMPTY:flag = QueueEmpty(&q);if (flag){printf("队列为空!\n");}else{printf("队列不为空!\n");}break;case CLEAR:QueueClear(&q);break;default:printf("输入错误,请重新输入!\n");break;}} while (select);QueueDestory(&q);return 0;
}
六.栈和队列必做OJ题
- 括号匹配问题
- 用队列实现栈
- 用栈实现队列
创作不易,如果能帮到你的话能赏个三连吗?感谢啦!!!