当前位置: 首页 > news >正文

栈和队列应用实操

目录

有效的括号(力扣)

思路:

代码形成(栈的结构):

结果:

用队列实现栈

思路:

代码实现:

结果:

注意:

结论:销毁空队列是完全安全的,函数会正常执行(不会触发断言失败),最终只是将队列的指针和大小重置为初始空状态,没有内存泄漏风险。

用栈实现队列

思路:​编辑

代码实现:

结果:

核心思想:

优势:

*设计循环队列

思路:

代码实现:

结果:

优点:


有效的括号(力扣)

https://leetcode.cn/problems/valid-parentheses/description/

思路:

代码形成(栈的结构):

//定义栈的结构
typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top;//有效数据个数int capacity;//容量
}ST;
//初始化
void StackInit(ST* ps)
{assert(ps);ps->arr = NULL;ps->top = ps->capacity = 0;
}//入栈----栈顶
void StackPush(ST* ps, STDataType x)
{if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc");exit(1);}//空间申请成功ps->arr = tmp;ps->capacity = newCapacity;}ps->arr[ps->top++] = x;
}//栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈----栈顶
void StackPop(ST* ps)
{assert(!STEmpty(ps));--ps->top;
}//出栈顶数据,元素不会删除
STDataType StackTop(ST* ps)
{assert(!STEmpty(ps));return ps->arr[ps->top - 1];}//获取栈中有效元素个数
int StackSize(ST* ps)
{return ps->top;
}
//销毁
void StackDestory(ST* ps)
{if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}//---------以上是栈的结构----------
bool isValid(char* s) {ST st;StackInit(&st);char*pi=s;while(*pi!='\0'){//左括号入栈if(*pi=='('||*pi=='['||*pi=='{'){StackPush(&st,*pi);}else{//先判断栈为空情况if(STEmpty(&st)){StackDestory(&st);return false;}//右括号然后取栈顶与*pi匹配char top=StackTop(&st);if((top=='('&&*pi!=')')||(top=='['&&*pi!=']')||(top=='{'&&*pi!='}')){StackDestory(&st);return false;}StackPop(&st);}pi++;}bool ret=STEmpty(&st);StackDestory(&st);return ret;
}

结果:

  • 若要 “销毁在判断之前”,只需交换两者的调用顺序,但结果无实际意义(永远返回true)。
  • 实际开发中,几乎所有场景都需要 “先判断再销毁”,以获取有价值的状态信息,再安全释放资源。

用队列实现栈

https://leetcode.cn/problems/implement-stack-using-queues/description/

思路:

代码实现:

typedef int QDataType;
//队列结点的结构
typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QueueNode;
//队列的结构
typedef struct Queue
{QueueNode* phead;QueueNode* ptail;int size; //队列中有效数据个数
}Queue;
//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;//初始化不能遗漏
}//入队——队尾
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = NULL;if (pq ->phead!= NULL)//队列非空{pq->ptail->next = newnode;pq->ptail = newnode;}else//队列为空{pq->phead = pq->ptail = newnode;}pq->size++;
}//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}//出队——队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));if (pq->phead == pq->ptail)//只有一个节点,头尾都置为空{free(pq->phead);pq->phead=pq->ptail = NULL;}else{QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}pq->size--;
}//取队头数据
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}
int QueueSize(Queue* pq)
{return pq->size;
}
//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail =NULL;pq->size =0;
}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) {//将不为空队列前size-1个数据移到另一个队列//再将最后一个数据出队列Queue*emp=&obj->q1;Queue*noneEmp=&obj->q2;if(QueueEmpty(&obj->q2)){emp=&obj->q2;noneEmp=&obj->q1;}while(QueueSize(noneEmp)>1){QueuePush(emp,QueueFront(noneEmp));QueuePop(noneEmp);}int top=QueueFront(noneEmp);QueuePop(noneEmp);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);obj=NULL;
}/*** Your MyStack struct will be instantiated and called as such:* MyStack* obj = myStackCreate();* myStackPush(obj, x);* int param_2 = myStackPop(obj);* int param_3 = myStackTop(obj);* bool param_4 = myStackEmpty(obj);* myStackFree(obj);
*/

结果:

注意:

当队列本身为空(即 pq->phead == NULL,队列中没有元素)时,调用 QueueDestroy 会怎样?

此时的执行流程如下:

  1. assert(pq); 会通过(因为 pq 是有效的指针,非 NULL)。判空一般使用在出数据或插入某位置时,然后assert(pq)即可满足
  2. QueueNode* pcur = pq->phead; 会让 pcur 指向 NULL(因为队列为空时 phead 是 NULL)。
  3. while (pcur) 循环条件不成立(pcur 是 NULL),循环体不会执行。
  4. 执行 pq->phead = pq->ptail = NULL; 和 pq->size = 0;(相当于再次重置空队列的状态,虽然此时已经是默认空状态)。

结论
销毁空队列是完全安全的,函数会正常执行(不会触发断言失败),最终只是将队列的指针和大小重置为初始空状态,没有内存泄漏风险。

这符合销毁操作的设计原则:即使容器为空,销毁操作也应该能安全执行,避免用户在调用销毁前还要额外判断容器是否为空。

用栈实现队列

https://leetcode.cn/problems/implement-queue-using-stacks/description/

思路:

代码实现:

//定义栈的结构
typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top;//有效数据个数int capacity;//容量
}ST;
//初始化
void StackInit(ST* ps)
{assert(ps);ps->arr = NULL;ps->top = ps->capacity = 0;
}//入栈----栈顶
void StackPush(ST* ps, STDataType x)
{if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc");exit(1);}//空间申请成功ps->arr = tmp;ps->capacity = newCapacity;}ps->arr[ps->top++] = x;
}//栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈----栈顶
void StackPop(ST* ps)
{assert(!STEmpty(ps));--ps->top;
}//出栈顶数据,元素不会删除
STDataType StackTop(ST* ps)
{assert(!STEmpty(ps));return ps->arr[ps->top - 1];}//获取栈中有效元素个数
int StackSize(ST* ps)
{return ps->top;
}//销毁
void StackDestory(ST* ps)
{assert(ps);if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}//-------栈的结构---------
typedef struct {ST pushst;ST popst;
} MyQueue;MyQueue* myQueueCreate() {MyQueue*pq=(MyQueue*)malloc(sizeof(MyQueue));StackInit(&pq->pushst);StackInit(&pq->popst);return pq;
}void myQueuePush(MyQueue* obj, int x) {//直接将x插入pushst栈中StackPush(&obj->pushst,x);
}//检测popst是否为空,若不为空,直接出栈顶
//为空,将pushst中数据导入popst,再出栈顶
int myQueuePop(MyQueue* obj) {if(STEmpty(&obj->popst)){while(!STEmpty(&obj->pushst)){StackPush(&obj->popst,StackTop(&obj->pushst));StackPop(&obj->pushst);}}int top= StackTop(&obj->popst);StackPop(&obj->popst);return top;
}//取队头
int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->popst)){while(!STEmpty(&obj->pushst)){StackPush(&obj->popst,StackTop(&obj->pushst));StackPop(&obj->pushst);}}return StackTop(&obj->popst);
}bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);
}void myQueueFree(MyQueue* 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. 分工明确
    • pushst 栈专门用于处理入队操作(myQueuePush
    • popst 栈专门用于处理出队(myQueuePop)和取队头(myQueuePeek)操作
  2. 数据迁移:当需要出队或取队头时,如果 popst 为空,就将 pushst 中的所有元素依次弹出并压入 popst,此时 popst 中元素的顺序与原队列顺序一致
  3. 空队列判断:当两个栈都为空时,队列才为空

优势:

  1. 高效性:每个元素最多被压栈和出栈各两次(从 pushst 到 popst),整体时间复杂度为 O (1) )
  2. 内存管理完善myQueueFree 函数正确销毁两个栈并释放队列结构体,避免内存泄漏
  3. 边界处理合理:通过 STEmpty 判断栈是否为空,确保操作的安全性

*设计循环队列

https://leetcode.cn/problems/design-circular-queue/description/

思路:

  1. 空间设计:申请 k+1 个空间(k 为队列最大容量),用一个位置作为 “空标记”,区分队列空和满的状态。
  2. 指针定义
    • front:指向队头元素(初始为 0)。
    • rear:指向队尾元素的下一个位置(初始为 0)。
  3. 空满判断
    • 空队列:front == rear
    • 满队列:(rear + 1) % (capacity + 1) == frontcapacity 为 k)。

代码实现:

typedef struct {int* arr;int front;//队头int rear;//队尾int capacity;//循环队列空间大小
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue*pq=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));//申请k+1个空间pq->arr=(int*)malloc(sizeof(int)*(k+1));pq->front=pq->rear=0;pq->capacity=k;return pq;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front==obj->rear;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->rear+1)%(obj->capacity+1)==obj->front;
}
//向循环队列插入一个元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj)){return false;}//没有满obj->arr[obj->rear++]=value;obj->rear%=obj->capacity+1;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){
return false;}//非空++obj->front;obj->front%=obj->capacity+1;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}return obj->arr[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}int prev=obj->rear-1;if(obj->rear==0){prev=obj->capacity;}return obj->arr[prev];
}void myCircularQueueFree(MyCircularQueue* obj) {if(obj->arr)free(obj->arr);free(obj);obj=NULL;
}/*** 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. 高效性:所有操作(入队、出队、取头尾)的时间复杂度均为 O (1)
  2. 边界处理完善:空队列取元素返回 -1,满队列入队返回 false,符合常规设计。
  3. 逻辑清晰:通过 front 和 rear 的移动及取模运算,完美实现循环特性。
http://www.dtcms.com/a/322334.html

相关文章:

  • RAGFoundry:面向检索增强生成的模块化增强框架
  • 深入剖析Spring MVC核心原理:从请求到响应的魔法解密
  • 如何在linux(CentOS7)上面安装 jenkins?
  • linux php版本降级,dnf版本控制
  • 【LeetCode 热题 100】(五)普通数组
  • 贪心----1.买卖股票的最佳时机
  • 【JS-8-Json】深入理解JSON语法及Java中的JSON操作
  • AutoML 的下半场——从“模型选择”到“端到端业务闭环”
  • 集成电路学习:什么是RQT图形用户界面工具
  • USRP X310 X410 参数对比
  • 区块链密码学简介
  • 【洛谷题单】--分支结构(三)
  • Meta AI水印计划的致命缺陷——IEEE Spectrum深度文献精读
  • CodeBuddy AI·编程新维度
  • GitHub第三方登录全解析:OAuth 2.0流程详解(适合初学者)
  • C++实现MATLAB矩阵计算程序
  • 计算机网络:深入了解CIDR地址块如何利用VLSM进行子网划分的过程
  • Kotlin反射详解
  • 学习Java的Day28
  • STM32CubeMX(十三)FatFs文件系统(SPI驱动W25Qxx)
  • 软考 系统架构设计师系列知识点之杂项集萃(119)
  • 传输线模拟经验谈
  • 计算机网络:CIDR地址块如何划分子网
  • 亚马逊新品起量的平衡术:轻运营与精细化的协同增长逻辑
  • 数据挖掘2.6 Perceptron Modeling 感知器建模
  • go语言常量2
  • NestJS Config 入门教程
  • HUAWEI交换机命令基础
  • mfc按钮点击事件没有触发,且程序卡死
  • 服装工厂计件软件与外贸公司开发方案