目录

前言

1.用队列实现栈

2.用栈实现队列

3.循环队列


前言

前面在学习了栈和队列的实现之后,相信大家对栈和队列的结构和使用方式都有了一些理解。

下面我们就来进行一些练习,这这章的练习相对于原来在难度上有了一些提升。

原来的题只需要实现一个接口,而今天的练习题需要实现多个接口。

1.用队列实现栈

225. 用队列实现栈 - 力扣(LeetCode) (leetcode-cn.com)

栈:后进先出队列:先进先出 

方法:

创建两个队列,用来来回导数据。

入栈:将数据放入不为空的队列。

出栈:将不为空的队列中的数据以出队入栈的方式放入空队列,直到该队列中只剩一个数据。

这个数据就是要出栈的数据,直接将他pop掉即可。

栈顶:返回队尾。

检测栈是否为空:两个队列都为空,则栈为空。

口说无凭,请看图:

如图:

队列1中有4个数,以1,2,3,4顺序入队,所以1为队头,4为队尾。

根据队列先进先出规则,1应该先出队。而这里我们要实现栈,即后进先出,即应该先出队尾数据。

所以我们将除队尾数据全部先导入队列2中,这样队列1中就只剩下队尾数据。

注意:队列1中的数据导入队列2中后,数据的相对位置是不变的。

即:

最后出栈,就可得到栈顶数据。

代码如下:

typedef int QDataType;
typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QueueNode;typedef struct Queue
{QueueNode* head;QueueNode* tail;
}Queue;//初始化
void QueueInit(Queue* pq);//销毁
void QueueDestroy(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);//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->head = pq->tail = NULL;
}//销毁
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* cur = pq->head;while (cur){QueueNode* next = cur->next;free(cur);cur = next;}
}//入队
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc");exit(-1);}newnode->data = x;newnode->next = NULL;if (pq->head == NULL){pq->head = pq->tail = newnode;}else{pq->tail->next = newnode;pq->tail = newnode;}
}//出队
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QueueNode* next = pq->head->next;free(pq->head);pq->head = next;if (pq->head == NULL){pq->tail = NULL;}}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);return pq->head == NULL;
}int QueueSize(Queue* pq)
{assert(pq);QueueNode* cur = pq->head;int size = 0;while (cur){size++;cur = cur->next;}return size;
}
//因为C语言中没有直接可以调用的栈和队列,所以需要将自己实现的队列拷贝到前面typedef struct {//在栈中创建两个队列Queue q1;Queue q2;
} MyStack;bool myStackEmpty(MyStack* obj);
MyStack* myStackCreate() {MyStack*obj = (MyStack*)malloc(sizeof(MyStack));//动态开辟存放两个队列结构体QueueInit(&obj->q1);//初始化两个队列QueueInit(&obj->q2);return obj;//需返回该结构体地址
}void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1))//入栈时向不为空的队列插入数据,都为空这里默认向队列2中插入{QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}}int myStackPop(MyStack* obj) {Queue*empty = &obj->q1;//出栈时需在不为空的队列里出,所以先找不为空的队列Queue*noempty = &obj->q2;//默认队列1不为空if(!QueueEmpty(&obj->q1))//如果队列1不为空,则交换两队列{empty = &obj->q2;noempty = &obj->q1;}while(QueueSize(noempty)>1)//将不为空队列中除队尾数据全部放入空队列中{QueuePush(empty,QueueFront(noempty));QueuePop(noempty);}int pop = QueueFront(noempty);//剩下的最后一个数据QueuePop(noempty);//将数据Pop掉return pop;
}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);
}

2.用栈实现队列

232. 用栈实现队列 - 力扣(LeetCode) (leetcode-cn.com)

方法:

与上题类似,创建两个栈,将数据入栈与入队相同。栈1用来入数据,栈2用来出数据。

不过出队时,队头的数据位于栈底,所以还是要将除栈底的数据全部导入另一个栈中。

但不同的是,队列实现栈时,导过去后数据的顺序不变,所以需要来回导;而这里倒过去后数据顺序会颠倒,所以不需要再导回去,可以直接出队,直到该栈中没有数据。

口说无凭,请看图:

这样下一次再需要出队时,只需将栈2的栈顶Pop即可,直到栈2中没有数据,然后再从栈1中导数据。

代码如下:

typedef int STDataType;typedef struct Stack
{STDataType* a;int top;int capacity;
}Stack;//初始化
void StackInit(Stack*ps);//销毁
void StackDestroy(Stack* ps);//压栈
void StackPush(Stack* ps, STDataType x);//出栈
void StackPop(Stack* ps);//栈顶
STDataType StackTop(Stack* ps);//判断栈是否为空
bool StackEmpty(Stack* ps);//栈中元素个数
int StackSize(Stack* ps);void StackInit(Stack* ps)
{assert(ps);ps->a = NULL;ps->capacity = 0;ps->top = 0;
}void StackDestroy(Stack* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->top = 0;
}void StackPush(Stack* ps, STDataType x)
{assert(ps);if (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){perror("realloc");exit(-1);}ps->a = tmp;ps->capacity = newcapacity;}ps->a[ps->top] = x;ps->top++;
}void StackPop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));ps->top--;
}STDataType StackTop(Stack* ps)
{assert(ps);assert(!StackEmpty(ps));return ps->a[ps->top - 1];
}bool StackEmpty(Stack* ps)
{assert(ps);return ps->top == 0;
}int StackSize(Stack* ps)
{assert(ps);return ps->top;
}
//同样需拷贝自己实现的栈typedef struct {//创建两个栈,s1用来入数据,s2用来出数据Stack s1;Stack s2;
} MyQueue;MyQueue* myQueueCreate() {MyQueue*obj = (MyQueue*)malloc(sizeof(MyQueue));StackInit(&obj->s1);StackInit(&obj->s2);return obj;
}void myQueuePush(MyQueue* obj, int x) {StackPush(&obj->s1,x);//直接将数据Push到s1中
}int myQueuePop(MyQueue* obj) {if(StackEmpty(&obj->s2))//pop数据之前先检查s2中是否有数据,没有数据需从s1中导入{while(!StackEmpty(&obj->s1)){StackPush(&obj->s2,StackTop(&obj->s1));StackPop(&obj->s1);}}int pop = StackTop(&obj->s2);//直接pop栈顶数据即可StackPop(&obj->s2);return pop;
}int myQueuePeek(MyQueue* obj) {//返回队尾数据(即s2栈顶数据)if(StackEmpty(&obj->s2))//返回数据之前先检查s2中是否有数据,没有数据需从s1中导入{while(!StackEmpty(&obj->s1)){StackPush(&obj->s2,StackTop(&obj->s1));StackPop(&obj->s1);}}return StackTop(&obj->s2);
}bool myQueueEmpty(MyQueue* obj) {//两个栈都为空,队列才为空return StackEmpty(&obj->s1)&&StackEmpty(&obj->s2);
}void myQueueFree(MyQueue* obj) {StackDestroy(&obj->s1);StackDestroy(&obj->s2);free(obj);
}

3.循环队列

622. 设计循环队列 - 力扣(LeetCode) (leetcode-cn.com)

这道题的意思是:

设计一个队列,这个队列的大小是固定的,且队列头尾相连, 然后该队列能够实现题目中的操作。

那么是使用数组实现,还是用链表实现呢?我们接着往下看。

例如:用k表示数组大小

图中我们用数组实现。用head和tail表示队头队尾下标,每插入一个数据,在数组tail位置插入,然后再将tail++。

但是有个问题如果将head==tail认为是队列为空的条件,那么怎么判断队列满了呢?

面对这个问题我们这样解决:将数组的空间大小增加1,即数组比队列大1个空间,且多的这个空间不算在数组的容量中。

这样:当 head = tail 时队列为空,(tail+1)%(k+1) = head 时队列满了。

且以链表实现时也会多申请一个空间。

代码如下:数组实现

typedef struct {int*arr;int head;//队头int tail;//队尾int capacity;//队列容量
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue*obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->arr = (int*)malloc(sizeof(int)*(k+1));//申请数组空间obj->head = 0;obj->tail = 0;obj->capacity = k;//队列大小return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);//插入数据
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if(myCircularQueueIsFull(obj))//检查容量{return false;}else{obj->arr[obj->tail] = value;//在队尾位置插入obj->tail++;//队尾向后移动obj->tail %= obj->capacity+1;//当队尾移动到数组最后一个元素后面的下标时,队尾回到数组下标为0的位置 return true;}
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))//检测队列是否为空{return false;}else{obj->head++;obj->head %= obj->capacity+1; 当队头移动到数组最后一个元素后面的下标时,队尾回到数组下标为0的位置 return true;}}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;//队列为空返回-1}else{return obj->arr[obj->head];//返回队头数据}
}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj)){return -1;//队列为空返回-1}else{return obj->arr[(obj->tail+obj->capacity)%(obj->capacity+1)];//返回下标为tail位置前面一个位置数据}
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->head == obj->tail;//head==tail队列为空
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->tail+1)%(obj->capacity+1) == obj->head;//为防止数组越界,不能直接将tail+1与head比较
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->arr);free(obj);
}

链表实现与数组实现类似,只是判断队列满的条件的不一样。

链表判满的条件时tail->next = head;

代码如下:

typedef struct ListNode1 ListNode1;
struct ListNode1{int data;ListNode1* next;
};typedef struct {ListNode1* front;//创建头尾指针指向队列头和尾ListNode1* tail;
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->front = NULL;ListNode1* cur = NULL;while (k > -1)//创建k+1个结点{if (obj->front == NULL){obj->front = (ListNode1*)malloc(sizeof(ListNode1));obj->front->next = NULL;cur = obj->front;}else{ListNode1* next = (ListNode1*)malloc(sizeof(ListNode1));cur->next = next;cur = next;}k--;}cur->next = obj->front;//将链表成环obj->tail = obj->front;//头尾指针复位return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj)){return false;}else{obj->tail->data = value;//插入数据将数据放入tail结点obj->tail = obj->tail->next;//tail结点向后移动return true;}
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return false;}else{obj->front = obj->front->next;//删除数据时直接将头指针向后移动,不用将该位置的数据删除return true;}
}int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}else{return obj->front->data;//返回头指针结点处的数据}
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj)){return -1;}else{ListNode1* cur = obj->front;//因为不知到队尾位置,只知道队尾的下一个位置,所以需要遍历链表寻找队尾。while (cur->next != obj->tail){cur = cur->next;}return cur->data;}
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->tail;//head==tail队列为空
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return obj->tail->next == obj->front;//tail->next == head 队列满了
}void myCircularQueueFree(MyCircularQueue* obj) {ListNode1* cur = obj->front->next;while (cur != obj->front)//释放链表结点{ListNode1* next = cur->next;free(cur);cur = next;}free(cur);free(obj);
}

如有错误,或更好的方法,可以在评论区一起交流哟~

你的点赞支持就是我创作的动力!

【数据结构】栈和队列OJ练习(栈和队列相互实现+循环队列实现)相关推荐

  1. 栈和队列OJ练习——栈实现队列,队列实现栈

    文章目录 ⭐栈实现队列

  2. 计算机二级循环队列知识点,考点!计算机二级考试公共基础知识冲刺复习笔记:栈、队列和循环队列...

    小编所收集到的相关计算机二级考试公共基础知识冲刺复习笔记:栈.队列和循环队列的资料 大家要认真阅读哦! 1.栈(Stack)又称堆栈. (1)栈是一种运算受限的线性表,其限制是仅允许在表的一端进行插入 ...

  3. 数据结构循环队列C++实现

    1.队列的概念 队列只允许在表的一端插入,另一端删除.允许插入的一端叫做队尾,允许删除的一端叫做对首.队列的特性叫"先进先出".和栈一样,队列的存储形式也有两种,基于数组的存储表示 ...

  4. 数据结构与算法 | 循环队列

    循环队列 实际中我们还会用到一种队列叫做循环队列,这种队列把存储空间前后连接起来,形成像环一样的结构,解决了内存空间浪费的问题 这里我们用顺序结构来实现,因为为了防止溢出的情况,这里我们需要多开一个数 ...

  5. mysql循环队列_数据结构:循环队列

    数据结构:循环队列 写在前面 数组表示的问题 对于队列最好的方法是使用链表实现,因为对于数组来说,队列可能会出现下面这种情况: 如图所示,不可以继续添加元素,否则会造成数组越界而遭致程序出错.然而此时 ...

  6. 数据结构与算法之循环队列的操作

    数据结构与算法之循环队列的操作 /* 循环队列的入队和出队算法设计 初始化循环队列 .打印队列.插入元素到循环队列.获取循环队列的首元素,元素不出队.出队.获取循环队列元素个数.判断循环队列的空和满. ...

  7. 【数据结构】队列(链队列、循环队列)的存储结构及基本运算(C语言)

    目录 1. 队列基本概念 2. 链队列 2.1 代码+注释 2.2 运行结果 3. 循环队列 3.1 代码+注释 3.2 运行结果 1. 队列基本概念 队列(Queue)是一种限定性线性表,它只允许在 ...

  8. 【数据结构】顺序循环队列及其实现(C语言)

    给定一个大小为MAXSIZE的数组储存一个队列,经过若干次的插入和删除以后,当队尾指针 rear = MAXSIZE 时,呈现队列满的状态,而事实上数组的前部可能还有空闲的位置.为了有效地利用空间,引 ...

  9. 数据结构笔记(十)-- 循环队列

    队列的顺序表示和实现 一.循环队列概述 循环队列 是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列. 队头指针(front) 指向队列的队头元素. 队尾指针(rear) 指向队 ...

最新文章

  1. ocr智能图文识别 tess4j 图文,验证码识别
  2. Exp9 Web安全基础
  3. mysql连接不上怎么重置密码错误_MySQL数据库连接不上、密码修改问题
  4. python运行外部程序_在Python中运行外部程序(可执行文件)?
  5. html 如何实现一条竖线边上有 刻度_Android H5交互Webview实现localStorage数据存储
  6. MySQL数据库表分区功能详解
  7. linux 家目录没有了,linux刀片服务器断电重启以后home目录下的用户文件夹丢失了...
  8. 计算机存储T,GB,MB,KB,B,bit
  9. algorithm头文件下的fill()
  10. java语言的运行平台,威力加强版
  11. Win10下安装gcc、g++、make
  12. 深度学习(二)神经网络中的卷积和反卷积原理
  13. 将序列设置为字段的默认值 - oracle
  14. python和前端哪个好_web前端和python学哪个出来工资高?
  15. nginx代理出现Provisional headers are shown
  16. 金士顿内存条真假测试软件,金士顿内存条真伪怎么看?金士顿内存条判别真伪的几种方法...
  17. muduo网络库:09---多线程服务器之(单线程、多线程服务器的适用场合)
  18. Nginx到底能干嘛?!Nginx是做什么用的?通俗易懂,前端必看!
  19. MySQL基本优化方案
  20. 闲人闲谈PS之二十一——SAP自定义程序增加附件上传下载功能

热门文章

  1. mongodb的capped Collection集合
  2. 测试设备硬件项目开发流程
  3. 谁说手机麦克风不能录制立体声?你只是缺少这一神器罢了
  4. 怎么让微信公众号更“湿”一些?
  5. Cannot currently show the desktop[已解决]
  6. 分享自己的CVTE实习生求职之旅
  7. matlab径向基函数插值,径向基函数(Radial Basis Function)插值
  8. PKCS#11标准解读-Cryptoki库如何工作(4)
  9. matlab conv实现,MATLAB卷积运算(conv)以及通用的卷积函数my_conv的实现
  10. 网站介绍三合一缩略图片kyuan源码 可自定义增加广告位