ok呀,上一篇博客写了队列的相关知识,接下来就是我们提及过的,栈与队列的相互实现了。堆与这个问题咧,其实大家完全可以当一个知识扩展因为,这个问题也是没有太多的实践意义的,更多的是教学意义。所以咧。大家了解了解就行了。
栈实现队列
实现思路
我们想一想栈与队列有一个最根本的区别就是一个先进先出,一个先进后出。那么如果只用一个栈来实现队列应该是不大可能的。所以我们普遍是使用两个栈来实现队列,用两个队列来实栈。但是这个实现的思路是什么呢?
我们知道栈是后进先出的。那么我们要实现先进先出的话,那我们创建了两个栈,最好是一个为进栈,一个为出栈。这就好比因为我们一个栈顺序是和队列倒过来的,如果我再将我们的栈直接先倒过来,那么岂不是就要与队列相同吗?
这样大家看一下应该就很清楚了吧?因为这样我们在利用栈的后进先出的关系,这样我们就可以实现队列的输出关系的。
实现代码
那么接下来我们就直接用c语言来实现一下这个操作了。当然我们知道我们要用栈实现队列的话,我们肯定要先写一个栈。再加之我们之前已经写过了,所以我们直接cv过来。
写一个栈
#include<stdio.h>
#include<malloc.h>
#define STACK_INIT_SIZE 100 //栈的初始容量
#define STACKINCREMENT 10 //容量增量
#define OK 1
#define OVERFLOW -2
typedef int SDataType;
typedef int Status;
typedef struct{
SDataType *base; //栈底指针
SDataType *top; //栈顶指针
int StackSize; //当前已经分配的存储空间,以元素为单位
}SqStack;
//初始化顺序栈,构造一个空栈
Status InitStack(SqStack &S){
//分配存储空间
S.base = (SDataType *)malloc(STACK_INIT_SIZE*sizeof(SDataType));
if(!S.base){
//如果分配失败,则返回error
return OVERFLOW;
}
//S.top 始终指向栈顶元素的下一个位置
S.top = S.base; //初始状态下为空栈
S.StackSize = STACK_INIT_SIZE; //当前已经分配的存储容量为100个
return OK;
}
//入栈
Status Push(SqStack &s,SDataType e){
SDataType *p;
//首先判断栈是不是满的(上溢)
if(s.top-s.base == s.StackSize){
//追加空间
p = (SDataType *)realloc(s.base,(s.StackSize + STACKINCREMENT)*sizeof(SDataType));
if(!p){
//如果没有找到符合条件的存储空间,则返回error
return OVERFLOW;
}
//成功找到则使s.base指向p
s.base = p; //系统会将原来的内容复制过来
s.top = s.base + s.StackSize;
s.StackSize += STACKINCREMENT;
}
//先插入元素,然后使栈顶指针加 1
*(s.top) = e;
s.top++;
return OK;
}
//出栈
Status Pop(SqStack &s,SDataType &e){
//判断是否会发生下溢
if(s.top != s.base){
s.top--; //先将栈顶指针减 1
e = *(s.top);
}else{
return 0;
}
return e;
}
//判断是否为空栈
void judgeNull(SqStack &s){
if(s.top == s.base){
printf("此栈为空栈!\n");
}else{
printf("此栈不为空栈!\n");
}
}
//判断是否为满栈
void judgeFull(SqStack &s){
if(s.top-s.base == s.StackSize){
printf("栈满!\n");
}else{
printf("栈未满!\n");
}
}
int main(){
SqStack s;
SDataType element;
InitStack(s); //初始化栈
//将1-10入栈
for(int i=1;i<=10;i++){
Push(s,i);
}
judgeNull(s);
judgeFull(s);
printf("出栈:\n");
//只要栈不为空
while(s.top != s.base){
Pop(s,element); //出栈的元素用e接收
printf("%d ",element);
}
printf("\n");
judgeNull(s);
return 0;
}
两个栈
我们开始也说过了,我们要两个栈。那么下一步我们就要了还写两个栈。
//两个栈模拟实现队列
typedef struct
{
ST pushst;
ST popst;
} MyQueue;
栈的初始化
既然我们已经创建好两个栈了,那么接下来我们就要将栈初始化了。
//开辟空间并初始化
MyQueue* myQueueCreate()
{
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushst);
STInit(&obj->popst);
return obj;
}
入栈
然后我们前面也说过,我们创建了两个站,其中一个为入站,一个为出站,那么呢我们接下来要将数据插入到入栈里面。而且前面我们已经写过了一个栈的入栈。那么我们直接引用就可以了。
//将元素X推到队列的末尾
void myQueuePush(MyQueue* obj, int x)
{
STPush(&obj->pushst, x);
}
返回队头
那么我们首次需要用到先入先出的这个相关定义。其实就很简单,就分为两种情况。一种是我们的出栈里面有数据。一种是我们的出栈里面没有数据。如果有数据的话,我们直接将队列的头元素给返回出来就可以了。但是如果没有数据的话,我们就需要先倒一下数据,然后再返回。虽然大家可能会第一时间想到if和else当然这也是最常见的,我们也可以直接来一个if然后就可以了。我们直接判断我们的出栈是否有数据,如果没有,我们就直接倒然后在最末尾返回数据。如果有数据的话,他就不会进入到这个判断里面,然后我们就可以直接返回了。也就是说我们只需要判断里面是否有数据就可以了,然后再如果没有就进循环导数据,然后出数据。
//返回队列开头的元素
int myQueuePeek(MyQueue* obj)
{
if (STEmpty(&obj->popst))//如果出栈为空的话就进去循环导数据
{
//捯数据
while (!STEmpty(&obj->pushst))
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
return STTop(&obj->popst);//都要传出数据的所以直接整合了
}
头删
我们对于队列的头山的话,其实就只需要将我们出栈的头元素给删除掉,但是呢他又需要将头元素的值给返回。那么我们就需要创建一个临时变量来储存值,然后再头元素删除。
//从队列的开头移除并返回元素
int myQueuePop(MyQueue* obj)
{
int front = myQueuePeek(obj);//创建一个临时变量存储值
STPop(&obj->popst);//销毁头元素
return front;//返回头元素的值
}
判空
对于判空的话其实是比较简单的。因为我们在最开始写栈的时候,我们就已经写过判空了。我们就直接引用就可以了。当然如果我们一个一个判断的话是比较繁琐的,所以我们直接在返回的时候看到他们是否相同,这样就可以了。因为我在我们上面写在判空的时候就已经写过了。
//如果队列为空,返回true;否则,返回false
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&obj->popst) && STEmpty(&obj->pushst);//判断是否都为空
}
销毁
最后一个销毁队列。其实就很简单啦,我们因为是用栈实现队列的,所以我们就需要把两个栈销毁就可以了。当然还要记得释放里面的空间。
//销毁队列
void myQueueFree(MyQueue* obj)
{
STDestroy(&obj->popst);//销毁出栈
STDestroy(&obj->pushst);//销毁入栈
free(obj);//释放空间
}
完整代码
//C语言模拟实现栈
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
//初始化栈
void STInit(ST* ps);
//销毁栈
void STDestroy(ST* ps);
//入栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//获取栈顶元素
STDataType STTop(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool STEmpty(ST* ps);
void STInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void STDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = 0;
ps->top = 0;
}
void STPush(ST* 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 fail");
exit(-1);
}
ps->a = tmp;
ps->capacity = newCapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
void STPop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
STDataType STTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//=======================================================================
//两个栈模拟实现队列
typedef struct
{
ST pushst;
ST popst;
} MyQueue;
//开辟空间并初始化
MyQueue* myQueueCreate()
{
MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
STInit(&obj->pushst);
STInit(&obj->popst);
return obj;
}
//将元素X推到队列的末尾
void myQueuePush(MyQueue* obj, int x)
{
STPush(&obj->pushst, x);
}
//返回队列开头的元素
int myQueuePeek(MyQueue* obj)
{
if (STEmpty(&obj->popst))
{
//捯数据
while (!STEmpty(&obj->pushst))
{
STPush(&obj->popst, STTop(&obj->pushst));
STPop(&obj->pushst);
}
}
return STTop(&obj->popst);
}
//从队列的开头移除并返回元素
int myQueuePop(MyQueue* obj)
{
int front = myQueuePeek(obj);
STPop(&obj->popst);
return front;
}
//如果队列为空,返回true;否则,返回false
bool myQueueEmpty(MyQueue* obj)
{
return STEmpty(&obj->popst) && STEmpty(&obj->pushst);
}
//销毁队列
void myQueueFree(MyQueue* obj)
{
STDestroy(&obj->popst);
STDestroy(&obj->pushst);
free(obj);
}
队列实现栈
实现思路
我们也是先将一个队列里面塞入数据,然后再将它导出来。当然我们还是要确定一下。哪个为进?哪个为出?我们知道队列是先进先出的,但是我们的栈呢是需要先进后出。
对于队列实现上就不能像栈实现队列那样确定一个为入栈一个为出栈。我们需要时刻改变,我们就这样判断谁为空谁就会入栈,谁为谁就为出栈这样。就是当我们入队列的时候,我们的队列谁有数据就往谁里面插入。
实现代码
写一个队列
当然我们还是老样子,先写一个队列,因为我们以前写过了,接下来还是cv一下就可以了。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int QDateType;
typedef struct QueueNode
{
QDateType val;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
}Queue;
void QueueInti(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->tail = pq->head = NULL;
}
void QueuePush(Queue* pq, QDateType x)
{
assert(pq);
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
if (NULL == newNode)
{
printf("malloc error\n");
exit(-1);
}
newNode->val = x;
newNode->next = NULL;
if (pq->tail == NULL)
{
assert(pq->head == NULL);
pq->head = pq->tail = newNode;
}
else
{
pq->tail->next = newNode;
pq->tail = newNode;
}
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head && pq->tail);
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
QDateType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->val;
}
int QueueSize(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
int count = 0;
while (cur)
{
cur = cur->next;
count++;
}
return count;
}
两个队列
开始我们也说过了,实现队列实现站的话也需要两个队列,所以我们第一步创建两个队列。
//创建两个队列
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
队列初始化
好,当我们将队列创建好出来之后,我们就要将队列的初始化了。并且我们开始也是说过我们写过单个队列的程序。那么我们还是直接引用就可以了。
//两个队列的初始化
MyStack* myStackCreate()
{
MyStack* st = (MyStack*)malloc(sizeof(MyStack));//创建一个临时结构体
if (st == NULL)
return false;
QueueInit(&st->q1);//分别初始化
QueueInit(&st->q2);
return st;
}
入队列
我们前面说过我们看中了两个队列里面谁有数据就往谁里面插入。因为我们在前面已经写过了单个队列的相关代码了。那么我们接下来只需要判断谁不为空,就往谁里面插入就可以了。
//队列的入队列
void myStackPush(MyStack* obj, int x)
{
if (QueueSize(&obj->q1) != 0)//如果q1不为空的话,q1插入,反之q2插入
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
队列的头元素
当然还是少不了我们的头元素。在我们不知情的情况下,我们肯定不知道谁的队列里面有数据,所以我们这里我们也需要判断一下。判断好之后,但是我们也需要再思考一下。我们队列和栈的输入输出顺序是不一样的,那么是不是我们队列需要先再倒一下,然后再输出这样得到的答案才是我们想要的。
并且我们还需要知道我们从一个队列倒到另一个队列。是不是源队列导出一个元素,那么就要删除这个元素,不然的话只是导出来的话,两个队列都有元素了,这样就不满足我们一个队列有元素,一个队列无元素的基本原则了。
//返回栈的头元素
int myStackPop(MyStack* obj)
{
Queue* tmp = &obj->q1;//假设法,确定谁有数据
Queue* notmp = &obj->q2;
if (QueueSize(notmp) == 0)
{
tmp = &obj->q2;
notmp = &obj->q1;
}
while (QueueSize(notmp)>1)//倒数据,并且清楚数据
{
QueuePush(tmp, QueueFront(notmp));
QueuePop(notmp);
}
QDataType res = QueueFront(notmp);//一个临时变量,并且释放
QueuePop(notmp);
return res;
}
队列的尾元素
还会队列的尾元素也是一样的道理吧?我们只需要判断谁的元素有就可以了,然后再访问他的尾节点的元素,那么就很简单。
//返回队列尾元素
int myStackTop(MyStack* obj)
{//判断谁不为空,直接返回
if (QueueSize(&obj->q1) != 0)
return QueueBack(&obj->q1);
else
return QueueBack(&obj->q2);
}
队列判空
当大家看了上面的栈实现线队列的判空的话,那么队列判空其实也是很简单的。也是直接判断一个然后相与就可以了。
//队列的判空
bool myStackEmpty(MyStack* obj)
{
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
队列的销毁
当然实现了这些,我们就要实现最后的一个做指令对你的销毁了。但是我们也知道栈实现队列的销毁就很简单。这个只需要分开销毁并置空就可以了。那么队列的这个肯定也是一样的。只需要分开销毁,然后置空。
//队列的销毁
void myStackFree(MyStack* obj)
{
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
}
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
typedef int QDateType;
typedef struct QueueNode
{
QDateType val;
struct QueueNode* next;
}QueueNode;
typedef struct Queue
{
QueueNode* head;
QueueNode* tail;
}Queue;
void QueueInti(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
}
void QueueDestory(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->tail = pq->head = NULL;
}
void QueuePush(Queue* pq, QDateType x)
{
assert(pq);
QueueNode* newNode = (QueueNode*)malloc(sizeof(QueueNode));
if (NULL == newNode)
{
printf("malloc error\n");
exit(-1);
}
newNode->val = x;
newNode->next = NULL;
if (pq->tail == NULL)
{
assert(pq->head == NULL);
pq->head = pq->tail = newNode;
}
else
{
pq->tail->next = newNode;
pq->tail = newNode;
}
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->head && pq->tail);
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
QDateType QueueFront(Queue* pq)
{
assert(pq);
assert(pq->head);
return pq->head->val;
}
int QueueSize(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
int count = 0;
while (cur)
{
cur = cur->next;
count++;
}
return count;
}
//创建两个队列
typedef struct
{
Queue q1;
Queue q2;
} MyStack;
//两个队列的初始化
MyStack* myStackCreate()
{
MyStack* st = (MyStack*)malloc(sizeof(MyStack));//创建一个临时结构体
if (st == NULL)
return false;
QueueInit(&st->q1);//分别初始化
QueueInit(&st->q2);
return st;
}
//队列的入队列
void myStackPush(MyStack* obj, int x)
{
if (QueueSize(&obj->q1) != 0)//如果q1不为空的话,q1插入,反之q2插入
{
QueuePush(&obj->q1, x);
}
else
{
QueuePush(&obj->q2, x);
}
}
//返回栈的头元素
int myStackPop(MyStack* obj)
{
Queue* tmp = &obj->q1;//假设法,确定谁有数据
Queue* notmp = &obj->q2;
if (QueueSize(notmp) == 0)
{
tmp = &obj->q2;
notmp = &obj->q1;
}
while (QueueSize(notmp)>1)//倒数据,并且清楚数据
{
QueuePush(tmp, QueueFront(notmp));
QueuePop(notmp);
}
QDataType res = QueueFront(notmp);//一个临时变量,并且释放
QueuePop(notmp);
return res;
}
//返回队列尾元素
int myStackTop(MyStack* obj)
{//判断谁不为空,直接返回
if (QueueSize(&obj->q1) != 0)
return QueueBack(&obj->q1);
else
return QueueBack(&obj->q2);
}
//队列的判空
bool myStackEmpty(MyStack* obj)
{
return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}
//队列的销毁
void myStackFree(MyStack* obj)
{
QueueDestory(&obj->q1);
QueueDestory(&obj->q2);
free(obj);
}
总结
好了,以上就是关于栈与队列相互实现的相关知识吧。当然这些有其中有很多可以优化的地方,大家可以细心的观察一下,然后呢其中关于这个博客大家其实可以相当于课后拓展,因为确实这样没必要把他作为一个非常重要知识点,大家只需要知道如何使用就可以了。
最主要的还是需要大家熟练使用对业余战的单个的相关知识来,希望这篇不太对大家会有帮助,然后今天的讲解就到这里了。