☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼

准备好了么

目录:

一·用两个队列实现栈:

1·思路: 

2·画图理解:

3·代码解答: 

二·用两个栈实现队列:

 1·思路:

  2·画图理解:

3·代码解答:

三·设计循环队列: 

1·思路: 

2·画图理解:

3·代码解答:


一·用两个队列实现栈:

源:oj链接:. - 力扣(LeetCode)​​​​​​

1·思路: 

我们首先调用创建好的队列代码,然后假设令这两个队列作为一个栈,由于我们画图可以得出一个结论:

①当有两个空队列的时候,我们push时随便push,一直往不为空的队列里面push。

②当我们要移除并返回栈顶元素的时候,我们要把不为空的队列里n-1个元素push到另一个空的队列里面,最后得到将要pop与return的元素。

③只返回栈顶元素只需要返回不为空队列的尾指针指向的元素即可。

④判空的话,就是两个队列都为空才行。

2·画图理解:

3·代码解答: 

typedef int qdatatype;
typedef struct Qnode {struct Qnode* next;qdatatype data;
}Qnode;
typedef struct queue {int size;Qnode* head;Qnode* tail;
}queue;void Queueinit(queue* p) {assert(p);p->head =p->tail = NULL;p->size = 0;
}void Queuedestroy(queue* p) {assert(p);Qnode* cur = p->head;while (cur) {Qnode* next = cur->next;free(cur);cur = next;}p->size = 0;p->tail = p->head = NULL;
}void Queuebackpush(queue* p, qdatatype x) {assert(p);Qnode* newnode = (Qnode*)malloc(sizeof(Qnode));if (newnode == NULL) {perror(malloc);return;}newnode->next = NULL;newnode->data = x;if (p->tail == NULL) {p->head = p->tail = newnode;}else {p->tail->next = newnode;p->tail = newnode;}p->size++;}void Queuefrontpop(queue* p) {assert(p);assert(p->size > 0);if (p->head->next == NULL) {free(p->head);p->head = p->tail = NULL;}else {Qnode* next = p->head->next;free(p->head);p->head = next;}p->size--;
}qdatatype Queuefrontdata(queue* p) {assert(p);assert(p->size > 0);return p->head->data;
}qdatatype Queuebackdata(queue* p) {assert(p);assert(p->size > 0);return p->tail->data;
}int Queuesize(queue* p) {assert(p);return p->size;
}bool QueueEmpty(queue* p) {assert(p);return p->size == 0;
}//以上是引用的队列的创建typedef struct {queue p1;queue p2;} MyStack;//这里定义了一个结构体类型:我的栈:里面放了两个队列结构体类型的变量MyStack* myStackCreate() {MyStack*pst=(MyStack*)malloc(sizeof(MyStack));Queueinit(&(pst->p1));Queueinit(&(pst->p2));return pst;}//在这里将MyStack结构体里面的两个队列初始化,并得到MyStack类型的指针void myStackPush(MyStack* obj, int x) {if( !QueueEmpty(&(obj->p1))){Queuebackpush(&(obj->p1),x);}else{Queuebackpush(&(obj->p2),x);}//这里我们在两个队列里面插入数据,故由于让看起来像栈的形式,我们就找有数据的队列插入新的数据即
//找不为空的队列插入}int myStackPop(MyStack* obj) {//这里我们用假设法来得到空与非空队列queue*empty=&(obj->p1);queue*noempty=&(obj->p2);if(!QueueEmpty(&(obj->p1))){//如果假设不成立执行下面noempty=&(obj->p1);empty=&(obj->p2);}//以上操作我们就可以得到noempty empty 分别为空与非空的队列while( Queuesize(noempty)>1){Queuebackpush(empty,Queuefrontdata(noempty));Queuefrontpop(noempty);}//下面防止找不到要得到的栈顶元素,我们在pop之前要先保存一下int data=Queuefrontdata(noempty);Queuefrontpop(noempty);return data;}//首先我们要找到空的队列,然后把非空队列获得头部元素进行插到原来空的队列里,插入n-1个int myStackTop(MyStack* obj) {if( !QueueEmpty(&(obj->p1))){return Queuebackdata(&(obj->p1));}else{return Queuebackdata(&(obj->p2));}//这里返回栈顶元素即就是我们不为空的队列的队尾元素}bool myStackEmpty(MyStack* obj) {return QueueEmpty(&(obj->p1))&&QueueEmpty(&(obj->p2));
}void myStackFree(MyStack* obj) {Queuedestroy(&(obj->p1));Queuedestroy(&(obj->p2));free(obj);obj=NULL;
//这里由于我们在 MyStack里开辟了两个队列的链表类型空间故需要先释放掉,
//再释放obj}

测试: 

​
int main()
{MyStack* st = myStackCreate();myStackPush(st, 1);myStackPush(st, 2);int ret = myStackTop(st);printf("%d",ret);mystackFree(st);return 0;
}​

二·用两个栈实现队列:   

源:oj链接:. - 力扣(LeetCode)​​​​​​

 1·思路:

这里首先建立两个栈作为MyQueue,大致思路和两个队列实现栈相差不大,这里只不过是调用的事先创建的队列改成栈了,此外再删去元素的时候会多排序一次。

①push:还是找空的栈然后,依次入栈

②pop:类似于上面的实现,但是当我们把n-1个元素放到另一个空栈里面顺序与原来是相反的故需要颠倒一下,此时就要再入一次栈入到原栈里,(先保存再返回,再删除)。

③peek:由于我每次使用函数的时候,这两个栈必然有一个空有一个非空,只需要返回非空栈的下标为0的元素即可。

④empty:这里判空也是两个均为空。

  2·画图理解:

3·代码解答:

typedef int typedata;
typedef struct stack {typedata* a;int top;int capacity;} ST;void STinit(ST* p) {assert(p);p->a = NULL;p->capacity = p->top = 0;
}
void STpush(ST* p,typedata x) {assert(p);//扩容if (p->top == p->capacity) {int newcapacity = p->capacity == 0 ? 4 : (p->capacity) * 2;typedata* tmp = (typedata*)realloc(p->a, sizeof(typedata)*newcapacity);if (tmp == NULL) {perror("realloc");return;}p->a = tmp;p->capacity = newcapacity;}p->a[p->top] = x;p->top++;
}
void STpop(ST* p) {assert(p);assert(p->top);p->top--;}typedata STTop(ST* p) {assert(p);assert(p->top);return p->a[p->top - 1];}
bool STempty(ST* p) {assert(p);return p->top == 0;
}
int STsize(ST* p) {assert(p);return p->top;
}
void STdestroy(ST* p) {assert(p);free(p->a);p->a = NULL;p->capacity = p->top = 0;
}//以上是对栈的实现typedef struct {ST s1;ST s2;
} MyQueue;MyQueue* myQueueCreate() {MyQueue* q=(MyQueue* )malloc(sizeof(MyQueue));STinit(&(q->s1));STinit(&(q->s2));return q;
//在MyQueue内开辟两个栈的空间}void myQueuePush(MyQueue* obj, int x) {if(!STempty(&(obj->s1))){STpush(&(obj->s1),x);}else{STpush(&(obj->s2),x);}//这里也是找到非空栈进行插入数据
}int myQueuePop(MyQueue* obj) {ST*empty=&(obj->s1);ST*noempty=&(obj->s2);if(!STempty(&(obj->s1))){noempty=&(obj->s1);empty=&(obj->s2);}//通过假设法得到真实的非空与空的栈while(STsize(noempty)>1){STpush(empty,STTop(noempty));STpop(noempty);}//首先第一个循环是得到n-1个数据到原来空的栈里面,但是顺序是反的int data=STTop(noempty);STpop(noempty);//在这里保存队列即栈的第一个元素,并把它popwhile(STsize(empty)>0){STpush(noempty,STTop(empty));STpop(empty);}//通过再一个循环把它重新插入到原来的栈里,顺序恢复return data;}int myQueuePeek(MyQueue* obj) {if(!STempty(&(obj->s1))){return obj->s1.a[0];}else{return  obj->s2.a[0];}
}
//直接返回栈的首元素即队首元素
bool myQueueEmpty(MyQueue* obj) {return STempty(&(obj->s1))&&STempty(&(obj->s2));
}void myQueueFree(MyQueue* obj) {STdestroy(&(obj->s1));STdestroy(&(obj->s2));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);
*/

 测试:

int main() {MyQueue* m = myQueueCreate();myQueuePush(m, 1);myQueuePush(m, 2);myQueuePush(m, 3);int ret = myQueuePop(m);printf("%d ", ret);int n=myQueuePeek(m);printf("%d ", n);return 0;
}

三·设计循环队列:
 

 

源:oj链接:. - 力扣(LeetCode)​​​​​​

1·思路: 

设计循环队列,首先把它设计成一个数组的形式来循环利用,这里会涉及到判空与判满如何进行,

那么我们有两种方法解决,一个是进出队列用size记录,一个是额外多开辟一个空间,,我们选择用后者。比如设置长度为k,那么就要开辟k+1个空间。而多出来的空间我们不放数据用tail指向这个空的空间。(下面的head与tail实则是下标,把它假想为指针)

 ①MyCircularQueue:创建一个结构体存放队列要开辟的长度k,头尾指针以及int类型的指针a。

②myCircularQueueFront:找头,可以直接对a[head]去访问。

③myCircularQueueRear:访问尾指针前一个数据的话由于可能会存在tail为0,那么上一个就是-1,明显是不可能的,故需要转化,可以把它tail-1直接变成k,也可以tail-1后+(k+1)%(k+1).

④myCircularQueueEnQueue:插入数据先判是否full,没有的话,就放入tail++,而可能存在为k+1.那么就把它变为0,或者直接(++tail)%(k+1)。

⑤ myCircularQueueDeQueue:这里我们画图可以发现删除数据只需要head++,而tail不用变化,但是head++,当过界时候把它变为0。

⑥myCircularQueueIsEmpty:头尾指针指向同一个处,即head==tail。

⑦myCircularQueueIsFull:画图可以知道每每当填满数据的时候tail+1==head(不包括越界改变的情况),故我们还是可以在tail到k+1,把它变为0,看是否与head相同,也可以(tail+1)%(k+1)看是否==head。

2·画图理解:

3·代码解答:


typedef struct {int head;int tail;int k;//要开辟的队列长度int *a;} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {if(k<0){return NULL;}MyCircularQueue*q=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));int *m=(int*)malloc(sizeof(int)*(k+1));q->head=q->tail=0;q->a=m;q->k = k;//将结构体里的k初始化为队列长度kreturn q;}
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);//这里由于提前调用未定义的函数故需要在前面声明bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj)){return false;}else{// obj->a[obj->tail]=value;// obj->tail++;//  if(obj->tail==obj->k+1){// obj->tail=0;//这里有一个情况当tail+1到达数组超过数组最大额度就变为0obj->a[obj->tail]=value;obj->tail++;obj->tail%=obj->k+1;//这里取模可以简化上面的判断}return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return false;}obj->head++;// obj->a[obj->tail++];// if(obj->tail==obj->k+1){//     obj->tail=0;// }if(obj->head==obj->k+1){obj->head=0;}return true;//这里我们要删出队列的第一个元素,那么只需要head++(这里也要考虑越界变0),然后tail不变即可}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}return obj->a[obj->head];}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;}
//     if(obj->tail==0){
//      return obj->a[obj->k];
//     }
//   else{
//     return obj->a[obj->tail-1];
//   }//这里是返回tail的上一个数据,故可能会出现tail为零那么tail-1=-1;实际为k//简化:return obj->a[(obj->tail-1+(obj->k+1))%(obj->k+1)];}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {if(obj->head==obj->tail){return true;}else{return false; }
}bool myCircularQueueIsFull(MyCircularQueue* obj) {// if(obj->tail==obj->k&&obj->head==0){//     return true;// }// if(obj->tail+1==obj->head){//     return true;// }//     return false;/*这里判断是否满,我们画图可以得到在多开辟一个空间的时候,每当tail+1=head就满了,考虑到可能tail+1为-1,故进行了讨论判断*///简化:return (obj->tail+1)%(obj->k+1)==obj->head;//%(k+1)可以将越界的下标变成-1
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);obj->a=NULL;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);
*/

测试:

int main() {MyCircularQueue* m = myCircularQueueCreate(3);myCircularQueueEnQueue(m, 1);myCircularQueueEnQueue(m, 2);myCircularQueueEnQueue(m, 3);myCircularQueueEnQueue(m, 4);printf("%d ", myCircularQueueRear(m));myCircularQueueIsFull(m);myCircularQueueDeQueue(m);myCircularQueueEnQueue(m, 4);printf("%d ", myCircularQueueRear(m));myCircularQueueFree(m);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/696598.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单页源码加密屋zip文件加密API源码

简介&#xff1a; 单页源码加密屋zip文件加密API源码 api源码里面的参数已改好&#xff0c;往服务器或主机一丢就行&#xff0c;出现不能加密了就是加密次数达到上限了&#xff0c;告诉我在到后台修改加密次数 点击下载

卓豪Zoho CRM怎么收费?多少钱一年?

卓豪Zoho CRM作为一款功能强大且高度可定制的企业级客户关系管理系统&#xff0c;其收费标准因版本不同而有所差异&#xff0c;旨在满足不同规模及需求的企业。Zoho CRM提供多种套餐选择&#xff0c;包括但不限于免费版、标准版、专业版、企业版以及旗舰版。每种版本都包含了核…

leetcode-缺失的第一个正整数-96

题目要求 思路 1.这里的题目要求刚好符合map和unordered_map 2.创建一个对应map把元素添加进去&#xff0c;用map.find(res)进行查找&#xff0c;如果存在返回指向该元素的迭代器&#xff0c;否则返回map::end()。 代码实现 class Solution { public:int minNumberDisappeare…

CANopen总线_CANOpen开源协议栈

CANopen是自动化中使用的嵌入式系统的通信协议栈和设备配置文件规范。就OSI 模型而言&#xff0c;CANopen 实现了以上各层&#xff0c;包括网络层。 CANopen 标准由一个寻址方案、几个小型通信协议和一个由设备配置文件定义的应用层组成。通信协议支持网络管理、设备监控和节点…

C++11智能指针之一(简介)

1 概述 从C11开始C语言越来向现代化语言转变。尤其是智能指针的引入&#xff0c;代码中不会直接使用new/delete了。C11智能指针有三种分别是&#xff1a;shared_ptr&#xff0c;weak_ptr 和unique_ptr 。 2 类图 3 共享指针(shared_ptr) 接口函数&#xff1a; shared_ptr 构…

物联网五层架构分析

物联网五层架构分析 随着科技的迅速发展&#xff0c;物联网&#xff08;IoT&#xff09;作为日常生活中不可或缺的一部分&#xff0c;已融入人们的生活和工作中。物联网五层架构&#xff0c;包括感知层、网络层、数据层、应用层和业务层&#xff0c;扮演着关键的角色。 感知层 …

七、e2studio VS STM32CubeIDE之显示中文编码

目录 一、概述/目的 二、查看和修改文件编码 三、eclipse编码格式 3.1 优先级 3.1.1 全局workspace 3.1.2 工程 3.1.3 文件 3.1.4 全局文件的content type 二、STM32CubeIDE设置显示中文编码 二、e2studio设置显示中文编码 七、e2studio VS STM32CubeIDE之显示中文编…

4. 从感知机到神经网络

目录 1. 从感知机到神经网络 1.1 区别 1.2 定义 2. 最简单的神经网络 2.1 层神经网络 2.2 数学表达式 3. 激活函数的引入 1. 从感知机到神经网络 1.1 区别 之前章节我们了解了感知机&#xff0c;感知机可以处理与门、非与门、或门、异或门等逻辑运算&#xff1b;不过在…

Python中进程类Process的方法与属性的使用示例

一、示例代码&#xff1a; from multiprocessing import Process import time import osdef child_1(interval):print(子进程&#xff08;%s&#xff09;开始执行&#xff0c;父进程为&#xff08;%s&#xff09; % (os.getpid(), os.getppid()))t_start time.time()time.sle…

【Linux 系统】多线程(生产者消费者模型、线程池、STL+智能指针与线程安全、读者写者问题)-- 详解

一、生产者消费者模型&#xff08;重点&#xff09; 如图&#xff0c;在生活中&#xff0c;学生就是消费者角色&#xff0c;工厂是真正的生产者角色&#xff0c;那么超市是什么呢&#xff1f;为什么需要超市&#xff1f;超市是交易场所。我们的家附近不一定有工厂&#xff0c;而…

C++进阶之路:何为引用、内联函数、auto与指针空值nullptr关键字

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

强化学习——马尔可夫奖励过程的理解

目录 一、马尔可夫奖励过程1.回报2.价值函数 参考文献 一、马尔可夫奖励过程 在马尔可夫过程的基础上加入奖励函数 r r r 和折扣因子 γ \gamma γ&#xff0c;就可以得到马尔可夫奖励过程&#xff08;Markov reward process&#xff09;。一个马尔可夫奖励过程由 < S , …