目录
一.栈
1.栈的定义
2.顺序栈的功能实现
2.1.顺序栈的定义
2.2.顺序栈的初始化
2.3.顺序栈的判空
2.4.顺序栈的入栈
2.5.顺序栈的出栈
2.6.顺序栈的取栈顶元素
2.7.顺序栈的求栈的大小
2.8.顺序栈的销毁
2.9.完整程序
Stack.h
Stack.c
test.c
二.队列
1.队列的定义
2.链式队列的功能实现
2.1.链式队列的定义
2.2.链式队列的初始化
2.3.链式队列的判空
2.4.链式队列的入队
2.5.链式队列的出队
2.6.链式队列的取队头元素
2.7.链式队列的取队尾元素
2.8.链式队列的求队列的大小
2.9.链式队列的销毁
2.10.完整程序
Queue.h
Queue.c
test.c
三.栈与队列刷题
题一:有效的括号
题二:用队列实现栈
题三:用栈实现队列
题四:设计循环队列
一.栈
1.栈的定义
栈是只允许在一端进行插入或删除操作的线性表
逻辑结构:与普通线性表相同
数据的运算:插入,删除操作有区别
特点:后进先出,即Last In First Out(LIFO)
几个重要术语:
- 空栈;
- 栈顶:允许插入和删除的一端
- 栈底:不允许插入和删除的一端
- 栈顶元素
- 栈底元素
2.顺序栈的功能实现
2.1.顺序栈的定义
typedef int STDataType;typedef struct Stack
{STDataType* a;//动态开辟数组int top;//标识栈顶位置int capacity;//栈可以容纳的数据个数
}ST;
首先,定义动态数组a,采用动态数组的方式主要是便于后期容量的扩充;然后,定义一个变量top用于标识栈顶位置;最后,定义一个变量capacity用于统计栈可以容纳的数据个数。
2.2.顺序栈的初始化
void StackInit(ST* ps)
{//判空assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}
首先,将指向栈中数据内存空间的指针a初始化为NULL;然后,将指向栈顶元素的变量top初始化为0;最后,再将标识栈当前容量的capacity初始化为0即可。
调试分析:
2.3.顺序栈的判空
bool StackEmpty(ST* ps)
{//判空assert(ps);return ps->top == 0;
}
判断一个栈是否为空,若为空则返回true,否则返回false。这里需要特别说明的一点是,我们将top指向栈顶元素的下一个位置,而非指向栈顶元素本身。
注意:
- 当top初始化为:top=0;此时top指向栈顶元素的下一个位置
- 当top初始化为:top=-1;此时top指向栈顶元素
调试分析:
2.4.顺序栈的入栈
void StackPush(ST* ps, STDataType x)
{//判空assert(ps);//判断容量是否已满//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1if (ps->top == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//为空就开辟四个空间,不为空,就扩容至二倍STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("malloc fail\n");exit(-1);}//将新开辟的内存空间的首地址tmp赋值给aps->a = tmp;//更新capacityps->capacity = newCapacity;}//入栈ps->a[ps->top] = x;//top指向栈顶元素的下一个位置ps->top++;
}
在入栈之前,首先需要对顺序栈的空间容量进行检查,这里使用三目运算符进行判断:若当前容量capacity为空,则开辟4个数据的内存空间;若当前容量非空但已满,则将capacity的大小扩容至2*capacity。然后调用realloc函数开辟新的内存空间,并返回新的内存空间的起始地址tmp。接着将新开辟的内存空间的起始地址tmp赋值给a,让a指向这片新开辟的空间。最后,将x插入栈顶位置,并让top继续指向栈顶元素的下一个位置。
调试分析:
2.5.顺序栈的出栈
void StackPop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//出栈ps->top--;
}
在出栈之前,首先需要调用StackEmpty(ps)函数来判断栈是否为空,若不为空,则可以出栈。出栈操作就是将top减一,需要注意的是,出栈的数据还残留在内存中,只是逻辑上被删除了。
调试分析:
2.6.顺序栈的取栈顶元素
STDataType StackTop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//取栈顶元素return ps->a[ps->top - 1];
}
在取栈顶元素之前,首先需要调用函数StackEmpty(ps)判断栈是否为空,若栈不为空则可以取栈顶元素。因为top指向栈顶元素的下一个位置,所以top需要先进行减一,再取栈顶元素。
调试分析:
2.7.顺序栈的求栈的大小
int StackSize(ST* ps)
{//判空assert(ps);return ps->top;
}
因为top指向栈顶元素的下一个位置,而top的下标又是从0开始的,所以top所在位置的下标就是所求栈的大小,也就是栈中元素个数。
调试分析:
2.8.顺序栈的销毁
void StackDestory(ST* ps)
{//判空assert(ps);//realloc开辟的空间,需要调用free释放free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}
对于栈的销毁,首先需要释放由realloc动态申请开辟的内存空间,这里主要搭配free函数进行释放。然后将top和capacity初始化为0。
调试分析:
2.9.完整程序
Stack.h
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* a;//动态开辟数组int top;//标识栈顶位置int capacity;//栈可以容纳的数据个数
}ST;//初始化
void StackInit(ST* ps);//销毁
void StackDestory(ST* ps);//入栈
void StackPush(ST* ps, STDataType x);//出栈
void StackPop(ST* ps);//取栈顶元素
STDataType StackTop(ST* ps);//判空
bool StackEmpty(ST* ps);//栈大小
int StackSize(ST* ps);
Stack.c
#define _CRT_SECURE_NO_WARNINGS 1#include"Stack.h"//初始化
void StackInit(ST* ps)
{//判空assert(ps);ps->a = NULL;ps->top = 0;ps->capacity = 0;
}//销毁
void StackDestory(ST* ps)
{//判空assert(ps);//realloc开辟的空间,需要调用free释放free(ps->a);ps->a = NULL;ps->top = ps->capacity = 0;
}//入栈
void StackPush(ST* ps, STDataType x)
{//判空assert(ps);//判断容量是否已满//top标识的是最后一个数据的下一个位置,如果想要指向最后一个数据,初始时top=-1if (ps->top == ps->capacity){int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;//为空就开辟四个空间,不为空,就扩容至二倍STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);if (tmp == NULL){printf("malloc fail\n");exit(-1);}//将新开辟的内存空间的首地址tmp赋值给aps->a = tmp;//更新capacityps->capacity = newCapacity;}//入栈ps->a[ps->top] = x;//top指向栈顶元素的下一个位置ps->top++;
}//出栈
void StackPop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//出栈ps->top--;
}//取栈顶元素
STDataType StackTop(ST* ps)
{//判空assert(ps);//判断栈是否为空assert(!StackEmpty(ps));//取栈顶元素return ps->a[ps->top - 1];
}//判空
bool StackEmpty(ST* ps)
{//判空assert(ps);return ps->top == 0;
}//栈大小
int StackSize(ST* ps)
{//判空assert(ps);return ps->top;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1#include"Stack.h"void TestStack()
{ST st;//初始化StackInit(&st);//入栈StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);//出栈while (!StackEmpty(&st)){//读取栈顶元素printf("%d ",StackTop(&st));//出栈StackPop(&st);}printf("\n");//销毁StackDestory(&st);
}int main()
{TestStack();return 0;
}
二.队列
1.队列的定义
队列是只允许在一端进行插入(入队),在另一端删除的线性表(出队)。
队列的特点:先进先出,即First In First Out(FIFO)。
几个重要术语:
- 空队列
- 队头:允许删除的一端
- 队尾:允许插入的一端
- 队头元素
- 队尾元素
2.链式队列的功能实现
2.1.链式队列的定义
typedef int QDataType;//链式队列结点
typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QNode;//链式队列
typedef struct Queue
{QNode* head;//队列的头QNode* tail;//队列的尾
}Queue;
2.2.链式队列的初始化
void QueueInit(Queue* pq)
{//判空assert(pq);//不带哨兵位pq->head = pq->tail = NULL;
}
这里实现的是不带头结点的链式队列,初始时front和rear都指向NULL。
调试分析:
2.3.链式队列的判空
bool QueueEmpty(Queue* pq)
{//判空assert(pq);//看队头元素是否为NULLreturn pq->head == NULL;
}
判断队列是否为空,只需要看队头元素是否为NULL。
调试分析:
2.4.链式队列的入队
void QueuePush(Queue* pq, QDataType x)
{//判空assert(pq);//创建新结点newnodeQNode* newnode = (QNode*)malloc(sizeof(QNode));//判空if (newnode == NULL){perror("malloc fail\n");exit(-1);}newnode->data = x;newnode->next = NULL;//链表为空if (pq->tail == NULL){//在空队列中插入第一个元素//修改队头队尾指针pq->head = pq->tail = newnode;}else{//链表不为空pq->tail->next = newnode;//新结点插入到tail结点之后pq->tail = newnode;//修改tail指针}
}
在入队之前,需要调用malloc函数开辟一个新结点newnode。对于不带头结点的情况,第一个元素入队时要特殊处理。因为一开始这两个指针都是指向NULL的,所以插入第一个元素时对这两个指针都要进行修改。
调试分析:
2.5.链式队列的出队
void QueuePop(Queue* pq)
{//判空assert(pq);//判断队列是否为空assert(!QueueEmpty(pq));//只含一个结点if (pq->head->next == NULL){free(pq->head);//释放最后一个结点pq->head = pq->tail = NULL;//将队头与队尾指针都置空}else//含多个结点{QNode* next = pq->head->next;//next为队头结点的下一个结点free(pq->head);//释放队头结点pq->head = next;//修改头指针}
}
在出队之前,首先需要判断队列是否为空,若队列为空,则无法进行出队操作。当只剩下最后一个元素未出队时需特殊处理。首先需要调用free函数释放首元素,然后将队头指针与队尾指针都置为NULL。当还剩多个元素时,首先找到队头结点的下一个结点,然后调用free函数释放掉队头结点,最后将队头指针head指向next,更新队头元素。
调试分析:
2.6.链式队列的取队头元素
QDataType QueueFront(Queue* pq)
{//判空assert(pq);//判断队列是否为空assert(!QueueEmpty(pq));//取队头元素return pq->head->data;
}
在取队头元素之前,首选需要调用函数QueueEmpty(pq)判断队列是否为空,若为空,则无法取队头元素。若队列不为空,则取队头元素。
调试分析:
2.7.链式队列的取队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);//判断队列是否为空assert(!QueueEmpty(pq));//取队尾元素return pq->tail->data;
}
在取队尾元素之前,首选需要调用函数QueueEmpty(pq)判断队列是否为空,若为空,则无法取队尾元素。若队列不为空,则取队尾元素。
调试分析:
2.8.链式队列的求队列的大小
int QueueSize(Queue* pq)
{//判空assert(pq);//设置指针变量cur,用于遍历队列QNode* cur = pq->head;int size = 0;//遍历队列while (cur){++size;cur = cur->next;}return size;
}
在求队列的元素个数时,首先设置指针变量cur,并使其指向队头元素,然后设置一个变量size,用于统计元素个数。让指针变量cur进入while循环遍历整个队列,每遍历一个元素,size就自增1,直到cur走到队尾,则跳出循环,并返回size大小。
调试分析:
2.9.链式队列的销毁
void QueueDestory(Queue* pq)
{//判空assert(pq);//设置指针变量cur,用于遍历队列QNode* cur = pq->head;//遍历队列while (cur){QNode* next = cur->next;free(cur);cur = next;}//将队头指针,队尾指针均置为空pq->head = pq->tail = NULL;
}
首先设置指针变量cur,并使其指向队头元素,然后让指针变量cur进入while循环遍历整个队列,每遍历一个元素,就将其前一个元素释放掉,直到cur走到队尾,则跳出循环。在销毁整个链表之后要将队头指针与队尾指针均置为NULL。
调试分析:
2.10.完整程序
Queue.h
#pragma once#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>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);//取队头元素
QDataType QueueFront(Queue* pq);//取队尾元素
QDataType QueueBack(Queue* pq);//判空
bool QueueEmpty(Queue* pq);//总计元素个数
int QueueSize(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);//设置指针变量cur,用于遍历队列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);//创建新结点newnodeQNode* newnode = (QNode*)malloc(sizeof(QNode));//判空if (newnode == NULL){perror("malloc fail\n");exit(-1);}newnode->data = x;newnode->next = NULL;//链表为空if (pq->tail == NULL){//在空队列中插入第一个元素//修改队头队尾指针pq->head = pq->tail = newnode;}else{//链表不为空pq->tail->next = newnode;//新结点插入到tail结点之后pq->tail = newnode;//修改tail指针}
}//出队
void QueuePop(Queue* pq)
{//判空assert(pq);//判断队列是否为空assert(!QueueEmpty(pq));//只含一个结点if (pq->head->next == NULL){free(pq->head);//释放最后一个结点pq->head = pq->tail = NULL;//将队头与队尾指针都置空}else//含多个结点{QNode* next = pq->head->next;//next为队头结点的下一个结点free(pq->head);//释放队头结点pq->head = next;//修改头指针}
}//取队头元素
QDataType QueueFront(Queue* pq)
{//判空assert(pq);//判断队列是否为空assert(!QueueEmpty(pq));//取队头元素return pq->head->data;
}//取队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);//判断队列是否为空assert(!QueueEmpty(pq));//取队尾元素return pq->tail->data;
}//判空
bool QueueEmpty(Queue* pq)
{//判空assert(pq);//看队头元素是否为NULLreturn pq->head == NULL;
}//总计元素个数
int QueueSize(Queue* pq)
{//判空assert(pq);//设置指针变量cur,用于遍历队列QNode* cur = pq->head;int size = 0;//遍历队列while (cur){++size;cur = cur->next;}return size;
}
test.c
#define _CRT_SECURE_NO_WARNINGS 1#include"Queue.h"void TestQueue()
{Queue q;//初始化QueueInit(&q);//入队QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePush(&q, 5);//出队while (!QueueEmpty(&q)){//取对头元素printf("%d ",QueueFront(&q));//出队QueuePop(&q);}printf("\n");//销毁QueueDestory(&q);
}int main()
{test();return 0;
}
三.栈与队列刷题
题一:有效的括号
题目描述:
给定一个只包括'(',')','{','}','[',']'的字符串s,判断字符串是否有效
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合
- 左括号必须以正确的顺序闭合
分析:
我们可以把左括号依次压入栈中,越往后被压入的左括号越先被弹出栈进行匹配。每出现一个右括号,就“消耗”一个左括号进行匹配检查,这个过程对应出栈操作。扫描一连串括号的过程中若发现下列情况都说明括号序列不合法,终止操作。
- 弹出栈的左括号与刚刚遇到要检查的右括号不匹配;
- 扫描到右括号时发现栈空了(右括号单身);
- 处理完所有括号后,栈非空(右括号单身)。
实现:
bool isValid(char* s)
{ST st;//初始化StackInit(&st);while (*s){if (*s == '(' || *s == '[' || *s == '{')//是左括号{//入栈StackPush(&st, *s);++s;}else//是右括号{//判断栈是否为空,可能出现字符串只含右括号的情况if (StackEmpty(&st)){//销毁StackDestory(&st);return false;}//若栈不为空,则读取栈顶元素并出栈STDataType top = StackTop(&st);// 读取栈顶元素StackPop(&st);//出栈//看栈顶元素与当前字符是否匹配if ((top == '{' && *s == '}') || (top == '(' && *s == ')') || (top == '[' && *s == ']')){//匹配++s;}else{//不匹配则销毁StackDestory(&st);return false;}}}//匹配完之后,判断栈是否为空,可能出现字符串只含左括号的情况bool ret = StackEmpty(&st);//销毁StackDestory(&st);return ret;
}
题二:用队列实现栈
题目描述:
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push,top,pop和empty)。
实现MyStack类:
void push(int x):将元素x压入栈顶
void pop():移除并返回栈顶元素
int top():返回栈顶元素
boolean empty():如果栈是空的,返回true;否则,返回false
分析:
队列queue1保存原始输入数据,队列queue2作为临时队列缓存数据。当进行stack_pop操作时,先将queue1里除最后一个元素外全部出队,并将出队的数据保存在临时队列queue2里,然后保存queue1的最后一个元素,最后再将queue2里的全部元素出队,且出队的元素重新放进queue1里,返回保存的queue1最后的元素。
实现:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>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);//取队头元素
QDataType QueueFront(Queue* pq);//取队尾元素
QDataType QueueBack(Queue* pq);//判空
bool QueueEmpty(Queue* pq);//总计元素个数
int QueueSize(Queue* pq);/**********用队列实现栈**********///构造一个包含两个队列的栈
typedef struct
{Queue q1;Queue q2;
}MyStack;//初始化栈
MyStack* myStackCreate()
{//调用malloc为栈开辟内存空间MyStack* obj = (MyStack*)malloc(sizeof(MyStack));//初始化两个队列QueueInit(&obj->q1);QueueInit(&obj->q2);return obj;
}//入栈
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);//假设q1队列为空,q2队列不为空Queue* emptyQ = &obj->q1;Queue* nonEmptyQ = &obj->q2;//若q1队列不为空,则将q2队列设为空,q1队列设为非空if (!QueueEmpty(&obj->q1)){emptyQ = &obj->q2;nonEmptyQ = &obj->q1;}//把非空队列的数据导入到空队列,也就是将非空队列的前n-1个数据导入至另一个空队列while (QueueSize(nonEmptyQ) > 1){QueuePush(emptyQ,QueueFront(nonEmptyQ));//取非空队列对头的数据插入到空队列中去QueuePop(nonEmptyQ);//出队}int top = QueueFront(nonEmptyQ);//取非空队列的队头元素QueuePop(nonEmptyQ);//出队return top;
}//取栈顶元素
int myStackTop(MyStack* obj)
{//判空assert(obj);//若q1队列不为空,则取q1队尾元素if (!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{//若q2队列不为空,则取q2队尾元素return QueueBack(&obj->q2);}
}//判断栈是否为空
bool myStackEmpty(MyStack* obj)
{//判空assert(obj);//只有当两个队列均为空时,才表示栈为空return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}//销毁栈
void myStackFree(MyStack* obj)
{//判空assert(obj);//销毁队列q1和q2QueueDestory(&obj->q1);QueueDestory(&obj->q2);//释放栈free(obj);
}
题三:用栈实现队列
题目描述:
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push,pop,peek,empty)
实现MyQueue类:
void push(int x):将元素x推到队列的末尾
int pop():从队列的开头移除并返回元素
int peek():返回队列开头的元素
boolean empty():如果队列为空,返回true;否则,返回false
分析:
用两个栈实现一个队列,设置其中一个栈pushst专门入数据,另一个栈popst专门出数据。若要入队,则进pushst栈;若要出队,首先看popst栈是否为空,如果为空,就先把pushst栈的数据转移过来,然后出队,如果不为空,则直接出popst栈的数据。
实现:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int STDataType;typedef struct Stack
{STDataType* a;//动态开辟数组int top;//标识栈顶位置int capacity;//栈可以容纳的数据个数
}ST;//初始化
void StackInit(ST* ps);//销毁
void StackDestory(ST* ps);//入栈
void StackPush(ST* ps, STDataType x);//出栈
void StackPop(ST* ps);//取栈顶元素
STDataType StackTop(ST* ps);//判空
bool StackEmpty(ST* ps);//栈大小
int StackSize(ST* ps);/**********用栈实现队列**********///构造一个包含两个栈的队列
typedef struct
{ST pushst;ST popst;
}MyQueue;//初始化队列
MyQueue* myQueueCreate()
{//调用malloc为栈开辟内存空间MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));//初始化两个栈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);//出队前首先要判断popst栈是否为空if (StackEmpty(&obj->popst)){//如果popst栈为空,则把pushst栈的数据拷贝过来while (!StackEmpty(&obj->pushst)){//将pushst栈的栈顶元素压入popst栈StackPush(&obj->popst, StackTop(&obj->pushst));//pushst栈的栈顶元素出栈StackPop(&obj->pushst);}}//读取popst栈的栈顶元素int front = StackTop(&obj->popst);//pushst栈的栈顶元素出栈StackPop(&obj->popst);return front;
}//取队头元素
int myQueuePeek(MyQueue* obj)
{//判空assert(obj);//当popst栈为空if (StackEmpty(&obj->popst)){//如果popst栈为空,则把pushst栈的数据拷贝过来while (!StackEmpty(&obj->pushst)){//将pushst栈的栈顶元素压入popst栈StackPush(&obj->popst, StackTop(&obj->pushst));//pushst栈的栈顶元素出栈StackPop(&obj->pushst);}}//读取popst栈的栈顶元素return StackTop(&obj->popst);
}//判空队列是否为空
bool myQueueEmpty(MyQueue* obj)
{//判空assert(obj);//只有当两个栈均为空时,才表示队列为空return StackEmpty(&obj->pushst) && StackEmpty(&obj->popst);
}//销毁队列
void myQueueFree(MyQueue* obj)
{//判空assert(obj);//销毁pushst栈和popst栈StackDestory(&obj->pushst);StackDestory(&obj->popst);//释放队列free(obj);
}
题四:设计循环队列
题目描述:
设计你的循环队列实现。循环队列是一种线性数据结构,其操作表现基于FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为"环形缓冲器"
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k):构造器,设置队列长度为k
Front:从队首获取元素。如果队列为空,返回-1
Rear:获取队尾元素。如果队列为空,返回-1
enQueue(value):向循环队列插入一个元素,如果成功插入则返回真
deQueue():从循环队列中删除一个元素。如果成功删除则返回真
isEmpty():检查循环队列是否为空
isFull():检查循环队列是否已满
分析:
1.采用数组或者链表都可以,但是数组缓存利用率更高,所以这里主要采用数组的方式。
2.用模运算将存储空间在逻辑上变成“环状”。当发现rear指针要指向MaxSize时,不应该让它指向MaxSize而是应该让它指向数组下标为0的位置。
3.队列判空:Q.rear==Q.front;队列判满:队尾指针的下一个位置是对头,即(Q.rear+1)%MaxSize==Q.front
实现:
//循环队列的定义
typedef struct
{int* a;//动态开辟数组int k;//当前有效元素个数int head;//队头int tail;//队尾
}MyCircularQueue;//循环队列的初始化
MyCircularQueue* myCircularQueueCreate(int k)
{//为队列开辟一块动态内存空间MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));//为数组开辟一个包含(k+1)个元素的内存空间obj->a = (int*)malloc(sizeof(int) * (k + 1));//队头,队尾起始都指向数组下标为0的位置obj->head = obj->tail = 0;//当前有效元素个数设置为k个obj->k = k;return obj;
}//判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{//判空assert(obj);return obj->head == obj->tail;
}//判断队列是否已满
bool myCircularQueueIsFull(MyCircularQueue* obj)
{//判空assert(obj);int next = obj->tail + 1;//当tail指向数组最后一个下标的下一个位置时,则将tail指向数组下标为0的位置if (next == obj->k + 1){next = 0;}//队尾指针的下一个位置是对头,则表示队列已满return next == obj->head;
}//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{//判空assert(obj);//检查队列是否已满if (myCircularQueueIsFull(obj)){return false;}//未满则将value插入队尾obj->a[obj->tail] = value;obj->tail++;//队尾指针后移//当tail指向数组最后一个下标的下一个位置时,则将tail指向数组下标为0的位置if (obj->tail == obj->k + 1){obj->tail = 0;}//obj->tail%=(k+1);return true;
}//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{//判空assert(obj);//检查队列是否为空if (MyCircularQueueIsEmpty(obj)){return false;}//若不空,则队头指针后移++obj->head;//当head指向数组最后一个下标的下一个位置时,则将head指向数组下标为0的位置if (obj->head == obj->k + 1){obj->head = 0;}return true;
}//取队头元素
int myCircularQueueFront(MyCircularQueue* obj)
{//判空assert(obj);//检查队列是否为空if (myCircularQueueIsEmpty(obj)){return -1;}//若不为空,则取队头元素return obj->a[obj->head];
}//取队尾元素
int myCircularQueueRear(MyCircularQueue* obj)
{//判空assert(obj);//检查队列是否为空if (myCircularQueueIsEmpty(obj)){return -1;}//因为tail指向队尾元素的下一个位置,所以要取tail前一位置的下标int pre = obj->tail - 1;//若tail在数组起始位置,则前一位置的下标为数组的末尾位置if (obj->tail == 0){pre = obj->k;}//int pre = obj->tail - 1 + obj->k + 1;//pre %= (obj->k+1);//取队尾元素return obj->a[pre];
}//销毁
void myCircularQueueFree(MyCircularQueue* obj)
{//判空assert(obj);//释放动态开辟的数组free(obj->a);//释放队列free(obj);
}