用两个队列实现栈

今天解决的问题其实没有什么实用意义,但是可以用来练习,让你更加熟悉对于栈和队列的应用。

其实栈和队列的区别就是先进先出和先进后出的区别嘛

我们目前要用两个队列去实现一个栈,也就是要把先进先出变成先进后出。那其实我们在进行pop操作的时候,因为队列要删除第一个元素,而我们想要的效果是只删除最后一个元素(虽然叫做“最后一个元素”,但是对于栈来说其实是栈顶)。那既然我们有俩队列,就完全可以把除了最后一个元素以外的元素用push操作插入到另外一个队列中,当然插入一个原队列就要pop掉一个元素。对于最后一个元素,我们不用插入,直接pop。这样就实现了类似于栈的删除操作。

下面是具体实现的代码(由于C语言没有内置的队列,只能自己把之前实现的队列复制上去)(C++不用自己写)

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int QDataType;

typedef struct QueueNode//结点
{
	struct QueueNode* next;
	QDataType val;
}QNode;

typedef struct Queue//保存头尾指针的结构体
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;

}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = NULL;
	while (pq->phead)
	{
		cur = pq->phead->next;
		free(pq->phead);//我们释放的是这块空间,而不是phead这个名字,因此如果想让phead再继续指向其它空间直接赋给他就可以了
		pq->phead = cur;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;

}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return;
	}
	newnode->next = NULL;
	newnode->val = x;
	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
    }
	else
	{
		pq->ptail->next = newnode;//这一步一定别少了,不然直接把尾结点置为newnode链表就相当于断开了,前面的都找不到了,所以要先把newnode连上,再把尾指针给它
		pq->ptail = newnode;
	}
	pq->size++;
	
}
void QueuePop(Queue* pq)//删除队列首结点
{
	assert(pq);
	if (pq->phead == pq->ptail)
	{
		if (pq->phead == NULL)//没有结点队列为空
		{
			perror("Pop Failed");
		}
		else//只有一个结点
		{
			free(pq->phead);
			pq->phead = pq->ptail = NULL;
		}
	}
	else//有多个结点
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	
	if (pq->size == 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}


typedef struct {
    Queue q1;
    Queue q2;
    
} MyStack;


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))
    {
        QueuePush(&obj->q1,x);
    }
    else{
        QueuePush(&obj->q2,x);
    }
    
}

int myStackPop(MyStack* obj)//哪一个不为空,就把前size-1的元素移到另外一个队列中,把最后一个元素pop掉
{
    Queue* Empty=&obj->q1;
    Queue* NonEmpty=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        Empty=&obj->q2;
        NonEmpty=&obj->q1;
    }
    while(QueueSize(NonEmpty)>1)
    {
        QueuePush(Empty,QueueFront(NonEmpty));
        QueuePop(NonEmpty);
    }
    int top = QueueFront(NonEmpty);
    QueuePop(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) {
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
    return;
}

/**
 * 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);
*/

下面是一些小细节

1.在实现 myStackTop这个函数时,因为我们首先要判断哪一个是空队列,哪一个是非空队列,所以我用了“假设法”,也就是这种方法、

当然也可以用if-else语句,但是觉得很麻烦,这样写一劳永逸。

2.我之前一直对结构体没有太多归属感,其实就是用的不熟练,今天的结构体其实是一层又一层这样去使用的,比如MyStack这个结构体里面,有两个队列,而这两个队列其实是用另外一个包含头尾指针的结构体来表示的,而且我们如果想要插入节点,节点也是结构体,我天,好多结构体。而且今天又清晰了一个以前有点模糊的点,就是结构体的名不能代表他的地址,要想找他的地址必须用&,当然如果本来这个量就是结构体指针的话那就直接用就ok。主要是不要跟数组搞混,因为数组的名字是代表这个数组的起始地址的,不用对数组名用&。

比如一个arr[10]

arr:表示数组首地址

arr[5]:表示这个值

&arr[5]:取这个元素的地址

&arr:不能这样写,这个不对。表示地址直接arr

3.复习malloc的使用

当你需要在程序运行期间创建大小不确定或者需要变化的数据结构,比如链表、树等,而这些数据的大小在编译时无法确定,这时就需要使用 malloc 来获取所需的内存空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值