1.概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)。
入队列:进行插入操作的一端称为队尾。
出队列:进行删除操作的一端称为队头。
2.队列的实现
队列同样可以使用数组和链表实现,但是要在头部出数据,这时使用链表就比较好,直接删除头节点就好。
代码实现:
Queue.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType val;
}QNode;
typedef struct Queue
{
QNode* phead;//队头
QNode* ptail;//队尾
int size;//接口有一个统计元素的个数,便于记录
}Queue;
//队的初始化和销毁
void QueueInit(Queue* pq);
void QueueDestroy(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);
Queue.c
#include"Queue.h"
//队的初始化和销毁
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
//队尾插入
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
return;
}
newnode->next = NULL;
newnode->val = x;
//链表尾插
if (pq->ptail == NULL)
{
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
//队头删除
void QueuePop(Queue* pq)
{
assert(pq && pq->size );
//只有一个节点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//多个节点
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
//取队头和队尾数据
QDataType QueueFront(Queue* pq)
{
assert(pq && pq->phead);
return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
assert(pq && pq->ptail);
return pq->ptail->val;
}
//队列的元素个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
//判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
test.c
#include"Queue.h"
int main()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 11);
QueuePush(&q, 22);
QueuePush(&q, 33);
QueuePush(&q, 44);
printf("%d\n", QueueFront(&q));
printf("%d\n", QueueBack(&q));
printf("%d\n", QueueSize(&q));
QueuePop(&q);
printf("%d\n", QueueFront(&q));
QueuePop(&q);
printf("%d\n", QueueFront(&q));
QueuePop(&q);
printf("%d\n", QueueFront(&q));
QueuePop(&q);
QueueEmpty(&q);
return 0;
}
3.队列OJ
(1)用队列实现栈
大体思路如下,这个过程要始终保持一个队列为空进行导,始终向有数据的那个队列入数据
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack*pst=(MyStack*)malloc(sizeof(MyStack));
QueueInit(&(pst->q1));
QueueInit(&(pst->q2));
return pst;
}
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&(obj->q1)))
{
QueuePush(&(obj->q1),x);
}
else
{
QueuePush(&(obj->q2),x);
}
}
int myStackPop(MyStack* obj) {
//假设法
Queue*empty=&(obj->q1);
Queue*nonEmpty=&(obj->q2);
if(!QueueEmpty(&(obj->q1)))
{
empty=&(obj->q2);
nonEmpty=&(obj->q1);
}
//将不为空的size-1导走到空的队列,将最后一个删除就是栈顶数据
while(QueueSize(nonEmpty)>1)
{
QueuePush(empty,QueueFront(nonEmpty));
QueuePop(nonEmpty);
}
int top=QueueFront(nonEmpty);
QueuePop(nonEmpty);
return top;
}
int myStackTop(MyStack* obj) {
if(!QueueEmpty(&(obj->q1)))
{
return QueueBack(&(obj->q1));
}
else
{
return QueueBack(&(obj->q2));
}
}
bool myStackEmpty(MyStack* obj) {
return QueueEmpty(&(obj->q1))&&QueueEmpty(&(obj->q2));
}
void myStackFree(MyStack* obj) {
QueueDestroy(&(obj->q1));
QueueDestroy(&(obj->q2));
free(obj);
}
(2)循环队列. - 力扣(LeetCode)
下面是一个用数组实现的队列
如果要出队,那么所有的元素都要移动,时间复杂度就大大提高了。但是队头不一定在下标为0的地方,这也解决了这个问题
为了避免只有一个元素时,队头和队尾重合处理的麻烦,所以引入两个指针,front指针指向队头元素,rear指针指向队尾的下一个元素,这样就可以检查空队列,即当front==rear时
a1,a2出队列时,front指针指向下标为2的地方,rear指针的位置不变
继续进数据a5 ,这时就会存在数组越界的问题,其实下标为0和1的位置仍然为空,这种现象就叫做“假溢出”
解决假溢出的方法就是后面满了就从头开始,也就是头尾相接的循环
综上,给出循环队列的定义:队列这种头尾相接的顺序结构
这里又有一个问题,这时队列为空和满时都是front等于rear,如何区分呢?
这里给出的一个方法就是,额外开一个空间,始终留一个空间,当rear的下一个等于front时就为满
typedef struct {
int *a;
int front;
int rear;
int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
//多开一个空间解决假溢出的问题
obj->a=(int*)malloc(sizeof(int)*(k+1));
obj->front=obj->rear=0;
obj->k=k;
return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front==obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->rear+1)%(obj->k+1)==obj->front;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
obj->a[obj->rear]=value;
obj->rear++;
obj->rear=(obj->rear)%(obj->k+1);
return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
obj->front++;
obj->front=(obj->front)%(obj->k+1);
return true;
}
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
else
return obj->a[(obj->rear+obj->k)%(obj->k+1)];
//==obj->a[obj->rear-1+obj->k+1]% (obj->k+1)
}
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
/**
* Your MyCircularQueue struct will be instantiated and called as such:
* MyCircularQueue* obj = myCircularQueueCreate(k);
* bool param_1 = myCircularQueueEnQueue(obj, value);
* bool param_2 = myCircularQueueDeQueue(obj);
* int param_3 = myCircularQueueFront(obj);
* int param_4 = myCircularQueueRear(obj);
* bool param_5 = myCircularQueueIsEmpty(obj);
* bool param_6 = myCircularQueueIsFull(obj);
* myCircularQueueFree(obj);
*/
(3)用栈实现队列
思路:用两个栈pushst和popst,pushst用来入数据,由于队列的特征是先进先出,所以我们可以通过popst栈进行导一下,在进行pop,这样数据就是先进先出
typedef struct {
ST pushst;
ST popst;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue*obj=(MyQueue*)malloc(sizeof(MyQueue));
STInit(&(obj->pushst));
STInit(&(obj->popst));
return obj;
}
int myQueuePeek(MyQueue* obj) {
if(STEmpty(&(obj->popst)))//导数据
{
while(!STEmpty(&(obj->pushst)))
{
int top=STTop(&(obj->pushst));
STPush(&(obj->popst),top);
STPop(&(obj->pushst));
}
}
return STTop(&(obj->popst));
}
void myQueuePush(MyQueue* obj, int x) {
STPush(&(obj->pushst),x);
}
int myQueuePop(MyQueue* obj) {
int front=myQueuePeek(obj);
STPop(&(obj->popst));
return front;
}
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&(obj->pushst))&&STEmpty(&(obj->popst));
}
void myQueueFree(MyQueue* obj) {
STDestroy(&(obj->pushst));
STDestroy(&(obj->pushst));
free(obj);
}
/**
* Your MyQueue struct will be instantiated and called as such:
* MyQueue* obj = myQueueCreate();
* myQueuePush(obj, x);
* int param_2 = myQueuePop(obj);
* int param_3 = myQueuePeek(obj);
* bool param_4 = myQueueEmpty(obj);
* myQueueFree(obj);
*/