文章目录
- 前言
- 一、用队列实现栈
- 二、用栈实现队列
- 总结
梦想不是别人给你的目标,靠自己的意志去实现的才是梦想… ——《食梦者》
前言
嗨喽喽!大家好哇。欢迎小伙伴们来到我的博客!!
在前面已经分享了栈与队列两种数据结构的特点与其相关的代码实现。那么今天将来分享一下使用其中一种数据结构来实现另外一种数据结构解决方法吧(虽然在实际场景中并没有什么卵用,但主要是存在一定的教学意义)。即使用队列实现栈与使用栈实现队列。那么就赶快开始吧!!
一、用队列实现栈
首先呈上在刷题网站力扣上的相关算法题的链接:【用队列实现栈】。
由题意可知,我们要使用队列这一数据结构来实现栈即其相关的基本操作的方法。那么此时我们就可以使用CV大法,将我们先前手搓的队列直接COPY到题目当中(当然,没有实现过栈的小伙伴最好先手撕一遍)!!
先给出队列的相关代码:
Queue:
typedef int QDataType;
typedef struct QListNode
{
struct QListNode* _pNext;//指向队列的下一个
QDataType _data;
}QNode;
typedef struct Queue
{
QNode* _front;//指向队头
QNode* _rear;//指向队尾
int _size;
}Queue;
void QueueInit(Queue* q)
{
assert(q);
q->_front = q->_rear = NULL;
q->_size = 0;
}
void QueueDestroy(Queue* q)
{
assert(q);
QNode* del;
while (q->_front != q->_rear)
{
del = q->_front;
q->_front = q->_front->_pNext;
free(del);
del = NULL;
}
free(q->_rear);
q->_front = q->_rear = NULL;
}
void QueuePush(Queue* q, QDataType x)
{
assert(q);
QNode* node = (QNode*)malloc(sizeof(QNode));
if (node == NULL)
{
perror("malloc fail!");
exit(1);
}
node->_data = x;
node->_pNext = NULL;
if (q->_rear)//队列不为空
{
q->_rear->_pNext = node;
}
else//队列为空
{
q->_front = node;
}
q->_rear = node;
q->_size++;
}
void QueuePop(Queue* q)
{
assert(q && q->_size);
if (q->_front->_pNext)//队列不仅一个元素
{
QNode* next = q->_front->_pNext;
free(q->_front);
q->_front = NULL;
q->_front = next;
}
else//队列仅一个元素
{
free(q->_front);
q->_front = q->_rear = NULL;
}
q->_size--;
}
QDataType QueueFront(Queue* q)
{
assert(q && q->_front);
return q->_front->_data;
}
QDataType QueueBack(Queue* q)
{
assert(q && q->_rear);
return q->_rear->_data;
}
bool QueueEmpty(Queue* q)
{
assert(q);
return q->_size == 0;
}
int QueueSize(Queue* q)
{
assert(q);
return q->_size;
}
我们知道队列的特性是先进先出,而栈的特性则是后进先出。为了再现栈后进先出的特性,由于队列无论是出队列,还是入队列,其先后顺序是一致的。因此我们可以创建两个队列,始终将数据入非空的队列,模仿入栈。再通过将非空队列中除队尾的元素全部入到空队列(一边入队,一边让原队列的数据出队),然后让仅剩下一个元素的队列出队,模仿出栈,至于访问栈顶元素,就可以直接访问非空的队列的尾元素即可。
知道了问题的解决方法,那么接下来就可以手撕代码:
首先使用两个队列来声明模拟的栈:
typedef struct {
Queue q1;
Queue q2;
} MyStack;
然后是模拟的栈的初始化:
MyStack* myStackCreate() {
MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));
QueueInit(&mystack->q1);
QueueInit(&mystack->q2);
return mystack;
}
入栈,通过对非空的队列进行入队操作来模仿:
void myStackPush(MyStack* obj, int x) {
if (!QueueEmpty(&obj->q1))//q1不为空
{
QueuePush(&obj->q1, x);
}
else //q2不为空
{
QueuePush(&obj->q2, x);
}
}
出栈,通过假设法来找到空与非空队列,然后将非空队列中除队尾的元素都入到空队列中,模拟找到栈顶元素,保存下来,再让其出队,始终让至少一个队列为空,最后返回保存的元素:
int myStackPop(MyStack* obj) {
//假设法
Queue* empty = &obj->q1;
Queue* nonempty = &obj->q2;
if (!QueueEmpty(empty))//q1不为空
{
empty = &obj->q2;
nonempty = &obj->q1;
}
//不为空的前size-1导走,剩下的为栈顶
while (QueueSize(nonempty) > 1)//当非空队列仅剩下一个元素时退出
{
QueuePush(empty, QueueFront(nonempty));//将非空队列的头元素放到空队列中
QueuePop(nonempty);
}
int ret = QueueFront(nonempty);
QueuePop(nonempty);
return ret;
}
获取栈顶元素,直接返回非空队列的队尾元素即可:
int myStackTop(MyStack* obj) {
if (!QueueEmpty(&obj->q1))
{
return QueueBack(&obj->q1);
}
else
{
return QueueBack(&obj->q2);
}
}
栈的判空,看两队列是否都为空即可:
bool myStackEmpty(MyStack* obj) {
if (QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2))
{
return true;
}
else
{
return false;
}
}
最后是模拟栈的销毁:
void myStackFree(MyStack* obj) {
QueueDestroy(&obj->q1);
QueueDestroy(&obj->q2);
free(obj);
}
之后提交即可,过了!!
二、用栈实现队列
首先呈上在刷题网站力扣上的相关算法题的链接:【用栈实现队列】
既然成功使用队列实现了栈,那么接下来就反过来,使用栈实现队列。
同样的,先使用CV大法将先前实现过的栈拷贝到题中。
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
pst->top = 0;
pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
if (pst->capacity == 0)
return;
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
void STPush(ST* pst, STDataType x)
{
assert(pst);
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
pst->a = tmp;
pst->capacity = newcapacity;
}
pst->a[pst->top] = x;
pst->top++;
}
void STPop(ST* pst)
{
assert(pst && pst->top);
pst->top--;
}
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
STDataType STTop(ST* pst)
{
assert(pst && pst->top);
return pst->a[pst->top - 1];
}
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
与上一题类似的是,这里我们也需要使用到两个栈来进行模拟。
但不同的是,这里需要让其中一个栈始终模拟入队列,让另一个栈始终模拟出队列,从而实现队列的基本操作。即通过始终让数据入到模拟入队列的栈中,来模仿入队操作。出队列时,将模拟入队的栈中的数据,全部入到模拟出队列的栈中,再对模拟出队列的栈进行出栈操作。获取队头元素,模拟出队列的栈的栈顶元素即为队头,当然若模拟出队列的栈为空,则需将模拟出队列的栈中的元素入到模拟入队列的栈中再进行操作。
了解了问题的解决方法,接下来就是紧张刺激的手撕代码环节:
首先,通过定义两个栈(模拟入队的栈与模拟出队的栈,以下分别用pushst和popst表示)来声明模拟的队列:
typedef struct {
ST pushst;//模拟入队的栈
ST popst;//模拟出队的栈
} MyQueue;
然后是模拟队列的初始化:
MyQueue* myQueueCreate() {
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushst);
STInit(&obj->popst);
return obj;
}
入队列,直接嵌套一个入栈的方法(对pushst入栈),一行代码解决:
void myQueuePush(MyQueue* obj, int x) {//入队
STPush(&obj->pushst, x);
}
出队列,通过将pushst中的元素全部入到popst,直到pushst为空,保存popst的栈顶元素,并删除栈顶元素,最后返回该栈顶元素即可:
int myQueuePop(MyQueue* obj) {//出队
if (STEmpty(&obj->popst))//若popst为空,将pushst压入栈popst中
{
while (!STEmpty(&obj->pushst))
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
int front = STTop(&obj->popst);
STPop(&obj->popst);
return front;
}
获取队头元素,先判断popst是否为空,为空就先将pushst中的元素全部入到popst中,最后返回popst的栈顶元素即可:
int myQueuePeek(MyQueue* obj) {
if (STEmpty(&obj->popst))//若popst为空,将pushst压入栈popst中
{
while (!STEmpty(&obj->pushst))
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
return STTop(&obj->popst);
}
模拟队列的判空操作,判断两个栈是否都为空即可:
bool myQueueEmpty(MyQueue* obj) {
return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}
最后是模拟队列的销毁操作:
void myQueueFree(MyQueue* obj) {
STDestroy(&obj->pushst);
STDestroy(&obj->popst);
free(obj);
}
之后提交,过了!!
总结
若本篇博客对小伙伴有帮助的话,希望能点个赞!
完!!!ヾ( ̄▽ ̄)ByeBye