目录
前言
上一章我们实现栈和队列,也提到了栈与队列是存在相互的关系的,那是什么呢我们可以看看力扣的两到题目:
我们可以发现栈居然能实现队列!!!相反队列也是可以实现栈的,那接下来我们就实现以下两个功能吧,当然还有循环队列我们待会也会提到。
一、用栈实现队列
1.题目理解
简单来说我们需要用两个栈利用先进后出的原理来实现队列的功能。
2.思路
首先假设我们在一个栈中入栈了4个元素,要出栈也是最上面的元素先出,显然不能达到队列的先进先出的要求的,那么什么情况可以让他们数字倒过来,以至于达成我们想要的效果呢?其实我们可以多创建一个栈,将开始进栈的元素出栈到新栈里那就可以将元素倒过来了。
那么接下来有两种思路实现下一步,先说第一种,现在我们将s1的元素转移到s2里,这是为了实现栈底元素先出栈想出的办法,那么 s1里就空了,我们可以为了数据进入数据顺序不变,可以将s2的元素倒回s1里,这种方法也是可以的,但是进行插入数据的话时间复杂度是O(2n),虽说最后简化为O(n),但是两次循环不太好,那有其他的解决办法吗,当然有那么接下来就说说思路二吧。
思路二的话也很好理解,当我们数据都在s1入栈时,那么s2就用来出元栈用的,那么我们需要出栈就在s2出,一旦s2为空时,就将s1的元素全部倒到s2中,这样的话我们只会用一次循环即可,在时间复杂度上是比较优的。
我们思路有那就开始实现,这边小编就用方法一来实现以上的功能吧,有兴趣的同学也可以自己尝试用思路二来实现的。
3.代码编写
typedef int SLDataType;
typedef struct Stack {
SLDataType* a;
int top;//栈顶
int capacity;//容量
}Stack;
//初始化栈
void StackInit(Stack* ps);
//入栈
void StackPush(Stack* ps, SLDataType x);
//出栈
void StackPop(Stack* ps);
//获取栈顶元素
SLDataType StackTop(Stack* ps);
//栈中有效数据范围
int StackSize(Stack* ps);
//检测栈是否为空
bool StackEmpty(Stack* ps);
//栈销毁
void StackDestory(Stack* ps);
void StackInit(Stack* ps) {
assert(ps);
ps->a = NULL;
ps->top = -1;
ps->capacity = 0;
}
void StackPush(Stack* ps, SLDataType x) {
assert(ps);
if (ps->top + 1 == ps->capacity) {//增容
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* p = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));
if (p == NULL) {
perror("realloc p");
return;
}
ps->a = p;
ps->capacity = newcapacity;
}
ps->a[++ps->top] = x;
}
void StackPop(Stack* ps) {
assert(ps);
assert(ps->top > -1);
ps->top--;
}
SLDataType StackTop(Stack* ps) {
assert(ps);
assert(ps->top > -1);
return ps->a[ps->top];
}
int StackSize(Stack* ps) {
assert(ps);
return ps->top + 1;
}
bool StackEmpty(Stack* ps) {
assert(ps);
return ps->top == -1;
}
void StackDestory(Stack* ps) {
assert(ps);
if (ps->a) {
free(ps->a);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
}
typedef struct {
Stack sout;
Stack sin;
} MyQueue;
MyQueue* myQueueCreate() {
MyQueue* mq = (MyQueue*)malloc(sizeof(MyQueue));
StackInit(&mq->sout);
StackInit(&mq->sin);
return mq;
}
void myQueuePush(MyQueue* obj, int x) {
StackPush(&obj->sin,x);
}
int myQueuePop(MyQueue* obj) {
while(StackSize(&obj->sin)>1){
StackPush(&obj->sout,StackTop(&obj->sin));
StackPop(&obj->sin);
}
int top = StackTop(&obj->sin);
StackPop(&obj->sin);
while(StackSize(&obj->sout)){
StackPush(&obj->sin,StackTop(&obj->sout));
StackPop(&obj->sout);
}
return top;
}
int myQueuePeek(MyQueue* obj) {
return (&obj->sin)->a[0];
}
bool myQueueEmpty(MyQueue* obj) {
return StackEmpty(&obj->sin);
}
void myQueueFree(MyQueue* obj) {
StackDestory(&obj->sin);
StackDestory(&obj->sout);
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.题目理解
其实看了这题目会发现它跟上一个题目几乎一模一样的,但是要实现的话会有些稍稍的不一样,接下来我们讲讲思路吧。
2.思路
我们想要实现队列实现栈的话就不能用第一题的思路,因为这完全就是两道题,那么思路的话,我们只需要将出队的n-1个元素全面出到第二个队列里,留下队尾最后一个元素,那么我们单独弹出,就能实现堆的后进先出功能,如果不太明白可以看看下面图,会好理解些,只要我们每一次像栈一样出栈,用以上的思路就可以完成啦。
3.代码编写
typedef int DataType;
typedef struct QListNode {
DataType val;
struct QListNode* next;
}QNode;
typedef struct Queue {
QNode* phead;
QNode* plist;
int size;
}Queue;
//初始化队列
void QListInit(Queue* q) {
assert(q);
q->phead = q->plist = NULL;
q->size = 0;
}
//队列入队列
void QListPush(Queue* q, DataType x) {
assert(q);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL) {
perror("malloc newnode");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
if (q->phead == NULL) {
q->phead = q->plist = newnode;
}
else {
q->plist->next = newnode;
q->plist = newnode;
}
q->size++;
}
//队列出队列
void QListPop(Queue* q) {
assert(q);
assert(q->phead);
QNode* del = q->phead;
if (del->next == NULL) {
free(del);
q->phead = q->plist = NULL;
}
else {
QNode* next = del->next;
free(del);
del = NULL;
q->phead = next;
}
q->size--;
}
//获取队列头部元素
DataType QueueFront(Queue* q) {
assert(q);
assert(q->phead);
return q->phead->val;
}
//获取队列尾部元素
DataType QueueBack(Queue* q) {
assert(q);
assert(q->plist);
return q->plist->val;
}
//获取队列的元素个数
int QueueSize(Queue* q) {
assert(q);
assert(q->phead);
return q->size;
}
//检测队列为空 空=TRUE
bool QueueEmpty(Queue* q) {
assert(q);
return q->phead == NULL;
}
//销毁队列
void QListDestory(Queue* q) {
assert(q);
QNode* tail = q->phead;
while (tail) {
QNode* next = tail->next;
free(tail);
tail = next;
}
q->phead = q->plist = NULL;
q->size = 0;
}
typedef struct {
Queue q1;
Queue q2;
} MyStack;
MyStack* myStackCreate() {
MyStack* qst = (MyStack*)malloc(sizeof(MyStack));
QListInit(&qst->q1);
QListInit(&qst->q2);
return qst;
}
void myStackPush(MyStack* obj, int x) {
if(!QueueEmpty(&obj->q1))
QListPush(&obj->q1,x);
else
QListPush(&obj->q2,x);
}
int myStackPop(MyStack* obj) {
Queue* empty = &obj->q1;
Queue* nonempty = &obj->q2;
if(!(QueueEmpty(empty))){
empty = &obj->q2;
nonempty = &obj->q1;
}
while(QueueSize(nonempty) > 1){
QListPush(empty,QueueFront(nonempty));
QListPop(nonempty);
}
int top = QueueFront(nonempty);
QListPop(nonempty);
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) {
QListDestory(&obj->q1);
QListDestory(&obj->q2);
free(obj);
}
/**
* 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);
*/
总结
以上是栈和队列相互关系的讲解啦,有点可笑的是我们用栈实现队列,用队列实现栈,确实很抽象哈哈哈,但是这也是我们需要掌握的知识点,感谢大家观看喔。