系列文章目录
🎈 🎈 我的CSDN主页 :OTWOL的主页,欢迎!!!👋🏼👋🏼
🎉🎉我的C语言初阶合集 :C语言初阶合集,希望能帮到你!!!😍 😍
🔍🔍我的C语言进阶合集 :我的C语言进阶合集,期待你的点击!!!🌈🌈
🎉🎉我的数据结构与算法合集 :数据结构与算法合集,点进去看看吧!!! 🎊🎊
😍 👋🏼🎉🎊创作不易,欢迎大家留言、点赞加收藏!!! 🥳😁😍
文章目录
一、有效的括号
(1)题目描述
下面是该题的链接🔗
(2)代码示例
写一个 顺序 栈:
c
// 定义一个类型别名,将 char 类型重命名为 STDataType,方便后续代码中使用,提高代码可读性和可维护性
typedef char STDataType;
// 定义一个结构体,用于表示栈结构
// 其中包含一个指向存储栈元素的数组指针 a,栈顶索引 top,以及栈的容量 capacity
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
// 初始化栈的函数
// 接收一个指向栈结构体的指针 ps,用于对栈进行初始化操作
void STInit(ST* ps)
{
// 断言传入的指针不为空,若为空则表示出现了错误情况,因为后续要通过该指针操作栈结构体
assert(ps);
// 初始设置栈的容量为 4
ps->capacity = 4;
// 初始时 栈顶索引 为 0,表示栈为空
ps->top = 0;
// 动态分配内存来存储栈元素,根据初始容量分配相应大小的内存空间
// 将分配的内存地址强转为 STDataType* 类型后赋值给临时指针 tmp
STDataType* tmp = (STDataType*)malloc(ps->capacity * sizeof(STDataType));
// 如果内存分配失败(返回的指针为NULL)
if (tmp == NULL)
{
// 输出错误信息,提示malloc分配内存失败
perror("malloc fail");
// 直接返回,不再进行后续初始化操作
return;
}
// 将成功分配内存的地址赋值给栈结构体中的数组指针 a,使得栈可以使用这块内存来存储元素
ps->a = tmp;
}
// 销毁栈的函数,用于释放栈所占用的内存资源等清理工作
void STDestroy(ST* ps)
{
// 断言传入的指针不为空,确保操作的合法性
assert(ps);
// 释放栈中存储元素的数组所占用的内存空间
free(ps->a);
// 将栈结构体中的数组指针 a 置为 NULL,防止出现野指针情况
ps->a = NULL;
// 将 栈顶索引 和 容量 都重置为 0,恢复到初始的 "空" 状态表示
ps->top = ps->capacity = 0;
}
// 向栈中压入元素的函数
// 接收一个指向栈结构体的指针 ps 以及要压入的元素 x(类型为 STDataType)
void STPush(ST* ps, STDataType x)
{
// 断言传入的指针不为空,保证后续操作能正常进行
assert(ps);
// 检查栈是否已满(即当前元素个数等于栈的容量),如果已满则需要进行扩容操作
if (ps->capacity == ps->top)
{
// 使用 realloc 对栈的存储空间进行扩容,扩容为原来的 2 倍大小
// 将重新分配后的内存地址强转为STDataType*类型后赋值给临时指针 tmp
STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
// 如果扩容失败(返回的指针为 NULL)
if (tmp == NULL)
{
// 输出错误信息,提示 realloc 扩容失败
perror("realloc fail");
// 直接返回,不进行元素压入操作了
return;
}
// 将扩容后成功分配的内存地址赋值给栈结构体中的数组指针 a,更新栈的存储空间
ps->a = tmp;
// 将栈的容量更新为原来的 2 倍
ps->capacity *= 2;
}
// 将元素 x 存放到栈顶位置,然后栈顶索引 top 自增 1,指向下一个空闲位置
ps->a[ps->top++] = x;
}
// 判断栈是否为空的函数
// 接收一个指向栈结构体的指针 ps
bool STEmpty(ST* ps)
{
// 断言传入的指针不为空,确保操作的合法性
assert(ps);
// 通过判断 栈顶索引 是否为 0 来确定栈是否为空,返回相应的布尔值
return (ps->top == 0);
}
// 获取栈中元素个数的函数
// 接收一个指向栈结构体的指针 ps
int STSize(ST* ps)
{
// 断言传入的指针不为空,确保操作的合法性
assert(ps);
// 直接返回 栈顶索引 的值,因为 栈顶索引 的值就代表了当前栈中元素的个数
return (ps->top);
}
// 弹出栈顶元素的函数(只是将 栈顶索引 减 1,逻辑上表示栈顶元素被移除了)
// 接收一个指向栈结构体的指针 ps
void STPop(ST* ps)
{
// 断言传入的指针不为空,保证操作的合法性
assert(ps);
// 断言栈不能为空,若为空则不能进行弹出操作,否则会出现错误情况
assert(!STEmpty(ps));
// 将 栈顶索引 减 1,实现逻辑上的弹出栈顶元素操作
--ps->top;
}
// 获取栈顶元素的函数(但不会弹出栈顶元素)
// 接收一个指向栈结构体的指针 ps
STDataType STTop(ST* ps)
{
// 断言传入的指针不为空,保证操作的合法性
assert(ps);
// 断言栈不能为空,若为空则没有栈顶元素可供获取,会出现错误情况
assert(!STEmpty(ps));
// 返回栈顶元素的值(通过 栈顶索引 减 1 的索引来获取,因为数组下标从 0 开始)
return (ps->a[ps->top - 1]);
}
判断有效括号
c
// 判断给定的括号字符串s是否有效(左右括号匹配)的函数
bool isValid(char* s)
{
// 创建一个栈结构体实例 st
ST st;
// 初始化栈st
STInit(&st);
// 遍历字符串s,直到遇到字符串结束符'\0'
while(*s)
{
// 如果当前字符是左括号('(' 或 '[' 或 '{')
if(*s == '(' || *s == '[' || *s == '{')
{
// 将该左括号压入栈st中
STPush(&st,*s);
}
else
{
// 如果栈为空,说明没有与之匹配的左括号了,括号字符串无效
if(STEmpty(&st))
{
// 销毁栈,释放资源
STDestroy(&st);
return false;
}
// 如果栈不为空,说明有对应的左括号
else
{
// 获取栈顶元素
STDataType top = STTop(&st);
// 弹出栈顶元素(因为已经获取到了,并且后续要检查匹配情况,所以可以弹出)
STPop(&st);
// 检查当前右括号与获取到的栈顶左括号是否匹配,如果不匹配则括号字符串无效
if( *s == ')' && top!= '('
|| *s == ']' && top!= '['
|| *s == '}' && top!= '{')
{
// 销毁栈,释放资源
STDestroy(&st);
return false;
}
}
}
// 移动指针到下一个字符继续检查
++s;
}
// 遍历完字符串后,检查栈是否为空,如果为空则说明所有括号都匹配,返回true,否则返回false
bool ret = STEmpty(&st);
// 销毁栈,释放资源
STDestroy(&st);
return ret;
}
二、用队列实现栈
(1)题目描述
下面是该题的链接🔗
(2)代码示例
写一个 链式 队列:
c
// 定义一个类型别名,将 int 类型重命名为 QDataType,方便后续代码中使用,增强代码可读性和可维护性
typedef int QDataType;
// 定义结构体 QNode,表示队列中的节点
// 其中包含一个数据域 data,用于存储节点的数据(类型为 QDataType,即int)
// 以及一个指针域 next,用于指向下一个节点,从而构成链式结构
typedef struct QNode
{
QDataType data;
struct QNode* next;
}QNode;
// 定义结构体 Queue,用于表示队列
// 包含指向队头节点的指针 head、指向队尾节点的指针 tail,以及记录队列中元素个数的 size
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
// 初始化队列的函数
// 接收一个指向 Queue 结构体的指针 q,用于对队列进行初始化操作
void QInit(Queue* q)
{
// 断言传入的指针不为空,若为空则表示出现了错误情况,因为后续要通过该指针操作队列结构体
assert(q);
// 初始化时,队头和队尾指针都设为 NULL,表示队列为空
q->head = q->tail = NULL;
// 队列中元素个数初始化为 0
q->size = 0;
}
// 销毁队列的函数,用于释放队列及其节点所占用的内存资源等清理工作
void QDestroy(Queue* q)
{
// 断言传入的指针不为空,确保操作的合法性
assert(q);
// 从队头开始遍历队列的每个节点
QNode* cur = q->head;
while (cur)
{
// 保存当前节点的下一个节点指针,方便后续释放当前节点内存后能继续遍历下一个节点
QNode* next = cur->next;
// 释放当前节点所占用的内存空间
free(cur);
// 将指针更新为下一个节点的指针,继续循环释放下一个节点内存
cur = next;
}
// 释放完所有节点后,将队头和队尾指针都置为 NULL,表示队列已被销毁
q->head = q->tail = NULL;
// 队列元素个数也重置为 0
q->size = 0;
}
// 判断队列是否为空的函数
// 接收一个指向 Queue 结构体的指针 q
bool QEmpty(Queue* q)
{
// 断言传入的指针不为空,确保操作的合法性
assert(q);
// 通过判断队列元素个数是否为 0 来确定队列是否为空,返回相应的布尔值
return (q->size == 0);
}
// 获取队列中元素个数的函数
// 接收一个指向 Queue 结构体的指针 q
int QSize(Queue* q)
{
// 断言传入的指针不为空,确保操作的合法性
assert(q);
// 直接返回队列中元素个数 size 的值
return (q->size);
}
// 向队列中插入元素的函数(队尾插入操作)
// 接收一个指向 Queue 结构体的指针 q 以及要插入的元素 x(类型为 QDataType,即 int)
void QPush(Queue* q, QDataType x)
{
// 断言传入的指针不为空,保证后续操作能正常进行
assert(q);
// 申请一个新的队列节点空间
QNode* newnode = (QNode*)malloc(sizeof(QNode));
// 如果内存分配失败(返回的指针为 NULL)
if (newnode == NULL)
{
// 输出错误信息,提示 malloc 分配内存失败
perror("malloc fail");
// 直接返回,不再进行元素插入操作
return;
}
// 给新节点的数据域赋值为要插入的元素 x
newnode->data = x;
// 新节点的 next 指针初始化为 NULL,因为它目前是队尾节点,后面没有其他节点
newnode->next = NULL;
// 如果队列当前为空(队头指针为 NULL)
if (q->head == NULL)
{
// 断言队尾指针也必然为 NULL,确保队列状态的一致性
assert(q->tail == NULL);
// 将队头和队尾指针都指向新创建的节点,因为此时队列中只有这一个节点
q->head = q->tail = newnode;
}
else
{
// 将当前队尾节点的 next 指针指向新节点,即将新节点连接到队列末尾
q->tail->next = newnode;
// 更新队尾指针为新插入的节点,使其始终指向队尾
q->tail = q->tail->next;
}
// 队列元素个数自增 1,表示插入了一个新元素
++q->size;
}
// 从队列中弹出元素的函数(队头删除操作)
// 接收一个指向 Queue 结构体的指针 q
void QPop(Queue* q)
{
// 断言传入的指针不为空,保证操作的合法性
assert(q);
// 断言队列不能为空,若为空则不能进行弹出操作,否则会出现错误情况
assert(!QEmpty(q));
// 进行队头删除操作
// 如果队列中只有一个元素(元素个数为 1)
if (q->size == 1)
{
// 释放队头节点所占用的内存空间
free(q->head);
// 将队头和队尾指针都置为 NULL,因为此时队列已为空
q->head = q->tail = NULL;
}
else
{
// 保存队头节点的下一个节点指针,方便后续更新队头指针
QNode* next = q->head->next;
// 释放当前队头节点所占用的内存空间
free(q->head);
// 更新队头指针为之前保存的下一个节点指针,实现队头节点的删除
q->head = next;
}
// 队列元素个数自减 1,表示弹出了一个元素
--q->size;
}
// 获取队列队头元素的函数
// 接收一个指向 Queue 结构体的指针 q
QDataType QFront(Queue* q)
{
// 断言传入的指针不为空,保证操作的合法性
assert(q);
// 断言队列不能为空,若为空则没有队头元素可供获取,会出现错误情况
assert(!QEmpty(q));
// 返回队头节点的数据域的值,即获取队头元素
return (q->head->data);
}
// 获取队列队尾元素的函数(但不会删除队尾元素)
// 接收一个指向 Queue 结构体的指针 q
QDataType QBack(Queue* q)
{
// 断言传入的指针不为空,保证操作的合法性
assert(q);
// 断言队列不能为空,若为空则没有队尾元素可供获取,会出现错误情况
assert(!QEmpty(q));
// 返回队尾节点的数据域的值,即获取队尾元素
return (q->tail->data);
}
用队列实现栈
c
// 定义一个结构体 MyStack,它内部包含两个 Queue 类型的成员 q1 和 q2
// 这里是通过组合两个队列来模拟实现栈的功能
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
// 创建一个 MyStack 结构体实例的函数,并进行初始化操作
// 返回指向新创建的 MyStack 结构体的指针
MyStack* myStackCreate()
{
// 动态分配内存来存储一个 MyStack 结构体大小的空间
MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
// 如果内存分配失败(返回的指针为 NULL)
if(obj == NULL)
{
// 输出错误信息,提示 malloc 分配内存失败
perror("malloc fail");
// 返回 NULL,表示创建失败
return NULL;
}
// 初始化 MyStack 结构体中的 q1 队列
QInit(&(obj->q1));
// 初始化 MyStack 结构体中的 q2 队列
QInit(&(obj->q2));
// 返回指向初始化好的 MyStack 结构体的指针
return obj;
}
// 向模拟栈( MyStack 结构体表示)中压入元素的函数
// 参数 obj 是指向 MyStack 结构体的指针,x 是要压入的元素(int 类型)
void myStackPush(MyStack* obj, int x)
{
// 如果 q1 队列不为空
if(!QEmpty(&(obj->q1)))
{
// 将元素 x 压入q1队列
QPush(&(obj->q1),x);
}
else
{
// 否则将元素 x 压入 q2 队列
QPush(&(obj->q2),x);
}
}
// 从模拟栈( MyStack 结构体表示)中弹出元素的函数
// 参数 obj 是指向 MyStack 结构体的指针,返回弹出的栈顶元素( int 类型)
int myStackPop(MyStack* obj)
{
// 先假设 q1 队列为空队列,q2 队列为非空队列(后续会根据实际情况调整)
Queue* emptyq = &(obj->q1);
Queue* noemptyq = &(obj->q2);
// 如果实际情况是 q1 队列不为空
if(!QEmpty(&(obj->q1)))
{
// 则交换假设,让 q2 队列为空队列,q1 队列为非空队列
emptyq = &(obj->q2);
noemptyq = &(obj->q1);
}
// 将非空队列(除了最后一个元素外)中的元素依次转移到空队列中
while(QSize(noemptyq) > 1)
{
// 取出 非空队列 的队头元素,并将其压入 空队列
QPush(emptyq,QFront(noemptyq));
// 弹出非空队列的队头元素(完成转移操作)
QPop(noemptyq);
}
// 获取非空队列此时剩下的最后一个元素(即 栈顶 元素)
int top = QFront(noemptyq);
// 弹出这个最后元素,完成 出栈 操作
QPop(noemptyq);
// 返回弹出的 栈顶元素
return top;
}
// 获取模拟栈( MyStack 结构体表示)的栈顶元素的函数(但不弹出元素)
// 参数 obj 是指向 MyStack 结构体的指针,返回栈顶元素( int 类型)
int myStackTop(MyStack* obj)
{
// 如果 q1 队列不为空
if(!QEmpty(&(obj->q1)))
{
// 返回 q1 队列的队尾元素作为栈顶元素(因为模拟栈的栈顶元素相当于非空队列的队尾元素)
return QBack(&(obj->q1));
}
else
{
// 否则返回 q2 队列的队尾元素作为栈顶元素
return QBack(&(obj->q2));
}
}
// 判断模拟栈( MyStack 结构体表示)是否为空的函数
// 参数 obj 是指向 MyStack 结构体的指针,返回布尔值表示栈是否为空
bool myStackEmpty(MyStack* obj)
{
// 当 q1 队列和 q2 队列都为空时,模拟栈为空,返回 true,否则返回false
return QEmpty(&(obj->q1)) && QEmpty(&(obj->q2));
}
// 释放模拟栈( MyStack 结构体表示)所占用的内存资源的函数
// 参数 obj 是指向 MyStack 结构体的指针
void myStackFree(MyStack* obj)
{
// 先销毁 MyStack 结构体中的 q1 队列,释放其占用的内存
QDestroy(&(obj->q1));
// 再销毁 MyStack 结构体中的 q2 队列,释放其占用的内存
QDestroy(&(obj->q2));
// 释放 MyStack 结构体本身所占用的内存空间
free(obj);
}
三、用栈实现队列
(1)题目描述
下面是该题的链接🔗
(2)代码示例
写一个 顺序 栈:
c
// 定义一个类型别名,将 char 类型重命名为 STDataType,方便后续代码中使用,提高代码可读性和可维护性
typedef char STDataType;
// 定义一个结构体,用于表示栈结构
// 其中包含一个指向存储栈元素的数组指针 a,栈顶索引 top,以及栈的容量 capacity
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
// 初始化栈的函数
// 接收一个指向栈结构体的指针 ps,用于对栈进行初始化操作
void STInit(ST* ps)
{
// 断言传入的指针不为空,若为空则表示出现了错误情况,因为后续要通过该指针操作栈结构体
assert(ps);
// 初始设置栈的容量为 4
ps->capacity = 4;
// 初始时 栈顶索引 为 0,表示栈为空
ps->top = 0;
// 动态分配内存来存储栈元素,根据初始容量分配相应大小的内存空间
// 将分配的内存地址强转为 STDataType* 类型后赋值给临时指针 tmp
STDataType* tmp = (STDataType*)malloc(ps->capacity * sizeof(STDataType));
// 如果内存分配失败(返回的指针为NULL)
if (tmp == NULL)
{
// 输出错误信息,提示malloc分配内存失败
perror("malloc fail");
// 直接返回,不再进行后续初始化操作
return;
}
// 将成功分配内存的地址赋值给栈结构体中的数组指针 a,使得栈可以使用这块内存来存储元素
ps->a = tmp;
}
// 销毁栈的函数,用于释放栈所占用的内存资源等清理工作
void STDestroy(ST* ps)
{
// 断言传入的指针不为空,确保操作的合法性
assert(ps);
// 释放栈中存储元素的数组所占用的内存空间
free(ps->a);
// 将栈结构体中的数组指针 a 置为 NULL,防止出现野指针情况
ps->a = NULL;
// 将 栈顶索引 和 容量 都重置为 0,恢复到初始的 "空" 状态表示
ps->top = ps->capacity = 0;
}
// 向栈中压入元素的函数
// 接收一个指向栈结构体的指针 ps 以及要压入的元素 x(类型为 STDataType)
void STPush(ST* ps, STDataType x)
{
// 断言传入的指针不为空,保证后续操作能正常进行
assert(ps);
// 检查栈是否已满(即当前元素个数等于栈的容量),如果已满则需要进行扩容操作
if (ps->capacity == ps->top)
{
// 使用 realloc 对栈的存储空间进行扩容,扩容为原来的 2 倍大小
// 将重新分配后的内存地址强转为STDataType*类型后赋值给临时指针 tmp
STDataType* tmp = (STDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(STDataType));
// 如果扩容失败(返回的指针为 NULL)
if (tmp == NULL)
{
// 输出错误信息,提示 realloc 扩容失败
perror("realloc fail");
// 直接返回,不进行元素压入操作了
return;
}
// 将扩容后成功分配的内存地址赋值给栈结构体中的数组指针 a,更新栈的存储空间
ps->a = tmp;
// 将栈的容量更新为原来的 2 倍
ps->capacity *= 2;
}
// 将元素 x 存放到栈顶位置,然后栈顶索引 top 自增 1,指向下一个空闲位置
ps->a[ps->top++] = x;
}
// 判断栈是否为空的函数
// 接收一个指向栈结构体的指针 ps
bool STEmpty(ST* ps)
{
// 断言传入的指针不为空,确保操作的合法性
assert(ps);
// 通过判断 栈顶索引 是否为 0 来确定栈是否为空,返回相应的布尔值
return (ps->top == 0);
}
// 获取栈中元素个数的函数
// 接收一个指向栈结构体的指针 ps
int STSize(ST* ps)
{
// 断言传入的指针不为空,确保操作的合法性
assert(ps);
// 直接返回 栈顶索引 的值,因为 栈顶索引 的值就代表了当前栈中元素的个数
return (ps->top);
}
// 弹出栈顶元素的函数(只是将 栈顶索引 减 1,逻辑上表示栈顶元素被移除了)
// 接收一个指向栈结构体的指针 ps
void STPop(ST* ps)
{
// 断言传入的指针不为空,保证操作的合法性
assert(ps);
// 断言栈不能为空,若为空则不能进行弹出操作,否则会出现错误情况
assert(!STEmpty(ps));
// 将 栈顶索引 减 1,实现逻辑上的弹出栈顶元素操作
--ps->top;
}
// 获取栈顶元素的函数(但不会弹出栈顶元素)
// 接收一个指向栈结构体的指针 ps
STDataType STTop(ST* ps)
{
// 断言传入的指针不为空,保证操作的合法性
assert(ps);
// 断言栈不能为空,若为空则没有栈顶元素可供获取,会出现错误情况
assert(!STEmpty(ps));
// 返回栈顶元素的值(通过 栈顶索引 减 1 的索引来获取,因为数组下标从 0 开始)
return (ps->a[ps->top - 1]);
}
用栈实现队列:
c
// 定义一个结构体 MyQueue,它内部包含两个 ST 类型的结构体成员 pushst 和 popst
// 这里通过两个栈来模拟实现队列的功能
typedef struct
{
ST pushst;
ST popst;
} MyQueue;
// 创建一个 MyQueue 结构体实例的函数,并进行初始化操作
// 返回指向新创建的 MyQueue 结构体的指针
MyQueue* myQueueCreate()
{
// 动态分配内存来存储一个 MyQueue 结构体大小的空间
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
// 如果内存分配失败(返回的指针为 NULL)
if(obj == NULL)
{
// 输出错误信息,提示 malloc 分配内存失败
perror("malloc fail");
// 返回 NULL,表示创建失败
return NULL;
}
// 初始化 MyQueue 结构体中的 pushst 栈
STInit(&(obj->pushst));
// 初始化 MyQueue 结构体中的 popst 栈
STInit(&(obj->popst));
// 返回指向初始化好的 MyQueue 结构体的指针
return obj;
}
// 向模拟队列( MyQueue 结构体表示)中插入元素的函数
// 参数 obj 是指向 MyQueue 结构体的指针,x 是要插入的元素( int 类型)
void myQueuePush(MyQueue* obj, int x)
{
// 调用栈的入栈函数,将元素 x 压入 pushst 栈,模拟队列的入队操作
STPush(&(obj->pushst),x);
}
// 获取模拟队列( MyQueue 结构体表示)队头元素的函数
// 参数 obj 是指向 MyQueue 结构体的指针,返回队头元素( int 类型)
int myQueuePeek(MyQueue* obj)
{
// 如果 popst 栈为空
if(STEmpty(&(obj->popst)))
{
// 当 pushst 栈不为空时,将 pushst 栈中的元素依次弹出并压入popst 栈
while(!STEmpty(&(obj->pushst)))
{
// 取出 pushst 栈的栈顶元素
STDataType top = STTop(&(obj->pushst));
// 将该元素压入 popst 栈
STPush(&(obj->popst), top);
// 弹出 pushst 栈的栈顶元素
STPop(&(obj->pushst));
}
}
// 获取 popst 栈的栈顶元素,即为模拟队列的队头元素
int front = STTop(&(obj->popst));
// 返回队头元素
return front;
}
// 从模拟队列( MyQueue 结构体表示)中删除队头元素的函数
// 参数 obj 是指向 MyQueue 结构体的指针,返回被删除的队头元素( int 类型)
int myQueuePop(MyQueue* obj)
{
// 先调用 myQueuePeek 函数获取队头元素(同时也会在 popst 栈为空时进行元素转移操作)
int front = myQueuePeek(obj);
// 弹出 popst 栈的栈顶元素,模拟队列的出队操作
STPop(&(obj->popst));
// 返回被删除的队头元素
return front;
}
// 判断模拟队列(MyQueue结构体表示)是否为空的函数
// 参数 obj 是指向 MyQueue 结构体的指针,返回布尔值表示队列是否为空
bool myQueueEmpty(MyQueue* obj)
{
// 当 pushst 栈和 popst 栈都为空时,模拟队列为空,返回 true,否则返回 false
return STEmpty(&(obj->pushst)) && STEmpty(&(obj->popst));
}
// 释放模拟队列( MyQueue 结构体表示)所占用的内存资源的函数
// 参数 obj 是指向 MyQueue 结构体的指针
void myQueueFree(MyQueue* obj)
{
// 销毁 MyQueue 结构体中的 pushst 栈,释放其占用的内存
STDestroy(&(obj->pushst));
// 销毁 MyQueue 结构体中的 popst 栈,释放其占用的内存
STDestroy(&(obj->popst));
// 释放 MyQueue 结构体本身所占用的内存空间
free(obj);
}
四、设计循环队列
(1)题目描述
下面是该题的链接🔗
(2)代码示例
c
// 定义一个循环队列的结构体
typedef struct
{
int k; // 队列的容量(最大元素个数)
int* a; // 指向队列数组的指针
int head; // 队列的头部索引
int tail; // 队列的尾部索引
} MyCircularQueue;
// 创建一个循环队列的函数
MyCircularQueue* myCircularQueueCreate(int k)
{
// 分配内存空间给队列结构体
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
if(obj == NULL)
{
perror("malloc fail"); // 如果分配失败,输出错误信息
return NULL;
}
// 分配内存空间给队列数组,容量为 k+1(多一个空位用于区分空和满)
int* tmp = (int*)malloc((k+1) * sizeof(int));
if(tmp == NULL)
{
perror("malloc fail"); // 如果分配失败,输出错误信息
return NULL;
}
obj->a = tmp; // 将数组指针赋值给结构体的a成员
obj->k = k; // 设置队列的容量
obj->head = obj->tail = 0; // 初始化头尾索引为 0
return obj; // 返回创建的队列对象
}
// 判断队列是否为空的函数
bool myCircularQueueIsEmpty(MyCircularQueue* obj)
{
return (obj->head == obj->tail); // 如果头尾索引相同,则队列为空
}
// 判断队列是否已满的函数
bool myCircularQueueIsFull(MyCircularQueue* obj)
{
// 如果尾部索引加1后模(k+1)等于头部索引,则队列已满
return ((obj->tail + 1) % (obj->k + 1) == obj->head);
}
// 向队列中插入元素的函数
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
if(myCircularQueueIsFull(obj)) // 如果队列已满
return false;
else
{
obj->a[obj->tail++] = value; // 将元素插入尾部,并将尾部索引加 1
obj->tail %= (obj->k + 1); // 尾部索引模(k+1)以循环
return true;
}
}
// 从队列中删除元素的函数
bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj)) // 如果队列为空
return false;
else
{
++obj->head; // 将头部索引加 1
obj->head %= (obj->k + 1); // 头部索引模(k+1)以循环
return true;
}
}
// 获取队列头部元素的函数
int myCircularQueueFront(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj)) // 如果队列为空
return -1;
return obj->a[obj->head]; // 返回头部元素
}
// 获取队列尾部元素的函数
int myCircularQueueRear(MyCircularQueue* obj)
{
if(myCircularQueueIsEmpty(obj)) // 如果队列为空
return -1;
else
{
// 计算尾部元素的索引,考虑循环的情况
return (obj->a[(obj->tail-1 + obj->k + 1) % (obj->k + 1)]);
}
}
// 释放队列内存的函数
void myCircularQueueFree(MyCircularQueue* obj)
{
free(obj->a); // 释放队列数组的内存
free(obj); // 释放队列结构体的内存
}
END
每天都在学习的路上!
On The Way Of Learning