目录
一、 队列的概念以及结构
二、 队列的实现
三、习题
3.1 选择题
3.2 用队列实现栈
3.3 用栈实现队列
3.4 设计循环队列
多爱自己一点!
栈的使用场景:括号匹配、逆波兰表达式求解等的一些问题。还有把递归改为非递归(第一种方法,循环;第二种方法:栈)
队列的使用场景:公平排队;广度优先遍历;抽号机;
#pragma once
#include
#include
#include
#include
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestory(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
bool QueueEmpty(Queue* pq);
size_t QueueSize(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->next = NULL;
newnode->data = x;
if (pq->tail == NULL)//链表为空的情况
{
assert(pq->head == NULL);//双重保证链表为空
pq->head = pq->tail = newnode;
}
else
{
QNode* prev = pq->tail;
prev->next = newnode;
pq->tail = newnode;
}
}
//错误删除代码
//void QueuePop(Queue* pq)
//{
// assert(pq);
// assert(pq->head && pq->tail);
// QNode* next = pq->head->next;
// free(pq->head);//在这里,如果删除最后一个元素的时候,tail指向的空间释放,tail就会成为野指针
// pq->head = next;
//}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head && pq->tail);
QNode* next = pq->head->next;
if (next == NULL)
{
free(pq->head);
pq->head =pq->tail = next;
}
else
{
free(pq->head);
pq->head = next;
}
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
size_t QueueSize(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
size_t size = 0;
while (cur)
{
size++;
cur = cur->next;
}
return size;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(pq->tail);
return pq->tail->data;
}
注意:(1)size_t 就是unsigned int(2)当出现a->b->c的时候,就要注意最后面的时候,是否会出现野指针。
//定义一个栈的结构体,栈里面是两个队列
typedef struct {
Queue q1;//q1和q2分别是结构体
Queue q2;
} MyStack;
//相当于初始化,由于需要返回的是结构体指针,所以不能在函数里创建一个结构体,返回结构体的地址,因为出了这个函数,又被销毁了。所以应该malloc一个结构体。
MyStack* myStackCreate() {
MyStack* pst = (MyStack*)malloc(sizeof(MyStack));//结构体的地址
assert(pst);
QueueInit(&pst->q1);//->的优先级>&的优先级
QueueInit(&pst->q2);//两个队列初始化完成,就表示栈也初始化完成
return pst;
}
void myStackPush(MyStack* obj, int x) {
assert(obj);
//在不为空的队列里面插入数,
if (!QueueEmpty(&obj->q1))
{
QueuePush(&obj->q1,x);
}
else
{
QueuePush(&obj->q2,x);
}
}
//删除栈顶的数据,并返回栈顶的数据
int myStackPop(MyStack* obj) {
assert(obj);
Queue* emptyQ = &obj->q1;
Queue* nonEmptyQ = &obj->q2;
if (!QueueEmpty(&obj->q1))
{
emptyQ = &obj->q2;
nonEmptyQ = &obj->q1;
}
while (QueueSize(nonEmptyQ) > 1)
{
int front = QueueFront(nonEmptyQ);
QueuePush(emptyQ, front);
QueuePop(nonEmptyQ);
}
int top = QueueFront(nonEmptyQ);
QueuePop(nonEmptyQ);
return top;
}
//栈顶的数据,就是队列的队尾的数据
int myStackTop(MyStack* obj) {
assert(obj);
if (!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
//两个队列都为空,才能证明栈为空
bool myStackEmpty(MyStack* obj) {
assert(obj);
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
void myStackFree(MyStack* obj) {
assert(obj);
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
}
typedef struct {
ST pushST;//定义结构体
ST popST;
} MyQueue;
//初始化
MyQueue* myQueueCreate() {
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
assert(obj);
StackInit(&obj->pushST);
StackInit(&obj->popST);
return obj;
}
void myQueuePush(MyQueue* obj, int x)
{
assert(obj);
StackPush(&obj->pushST,x);
}
//
int myQueuePop(MyQueue* obj)
{
assert(obj);
if (StackEmpty(&obj->popST))
{
while (!StackEmpty(&obj->pushST))
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);//把数据倒过去之后,需要删掉pushST里的数据
}
}
int front = StackTop(&obj->popST);
StackPop(&obj->popST);
return front;
}
//返回队列开头的元素,如果popST没有元素就导入元素,有数据就直接导入
int myQueuePeek(MyQueue* obj)
{
assert(obj);
if (StackEmpty(&obj->popST))
{
while (!StackEmpty(&obj->pushST))
{
StackPush(&obj->popST, StackTop(&obj->pushST));
StackPop(&obj->pushST);//把数据倒过去之后,需要删掉pushST里的数据
}
}
return StackTop(&obj->popST);
}
bool myQueueEmpty(MyQueue* obj)
{
assert(obj);
return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
}
//两层free,是因为free是把指向的空间free掉,仅仅free掉地址内容存放的空间,并没有free掉地址所指向的内容,但是obj里面的指针又指向了别的空间。
void myQueueFree(MyQueue* obj)
{
assert(obj);
StackDestory(&obj->pushST);
StackDestory(&obj->popST);
free(obj);
obj = NULL;
}
/**
* 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);
*/
注意:(1)用来删除的那个栈,删除完毕之后,才能导入新的数据。否则会导致出栈的数据顺序,不是我们想要的。(2)两层free,是因为free是把指向的空间free掉,仅仅free存放该地址内容的空间,并没有free掉地址所指向的空间,但是obj里面的指针又指向了别的空间。
链接:力扣
typedef struct {
int* a;
int head;
int tail;
int k;
} MyCircularQueue;
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
assert(obj);
obj->a = (int*)malloc((k+1)*sizeof(int));
assert(obj->a);
obj->head = obj->tail = 0;
obj->k = k;
return obj;
}
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
assert(obj);
assert(obj->a);
if (myCircularQueueIsFull(obj))
{
return false;
}
else
{
if ((obj->head) == (obj->tail))
{
obj->a[obj->head]= obj->a[obj->tail] = value;
}
else
{
obj->a[obj->tail] = value;
}
if (obj->tail == obj->k)
{
obj->tail = 0;
}
else
{
obj->tail++;
}
return true;
}
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
assert(obj);
assert(obj->a);
if (myCircularQueueIsEmpty(obj))
{
return false;
}
else
{
if (obj->head == obj->k)
{
obj->head = 0;
}
else
{
obj->head++;
}
return true;
}
}
int myCircularQueueFront(MyCircularQueue* obj)
{
assert(obj);
assert(obj->a);
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
return obj->a[obj->head];
}
}
int myCircularQueueRear(MyCircularQueue* obj) {
assert(obj);
assert(obj->a);
if (myCircularQueueIsEmpty(obj))
{
return -1;
}
else
{
if (obj->tail == 0)
{
return obj->a[obj->k];
}
else
{
return obj->a[obj->tail - 1];
}
}
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
assert(obj);
assert(obj->a);
return obj->head == obj->tail;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) {
assert(obj);
assert(obj->a);
if ((obj->head == 0) && (obj->tail == obj->k))
{
return true;
}
else
{
return (obj->tail) + 1 == obj->head;
}
}
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);
*/
注意:(1)当head==tail的时候,说明队列为空。但是循环队列没有一个位置的时候,满的情况下,head也是等于tail的。所以,为了避免空和满混淆,无法区分,我们一般多开一个空间,head==tail时是空,tail下一个位置是head时,就是满。【链表的话,队尾的值不太好取出来,所以用数组】
理论知识:
环形队列:它是不增容的,容量固定。