队列和栈的相互实现(一)

本文算是对队列和栈的更深层次的理解,用他们自身的性质来完成相互间的转换,在此之前必须清楚的了解他们的实现以及应用。

目录

用队列实现栈

队列编写: 

栈的实现分析:

 定义栈和栈的初始化

元素入栈(入队) 

元素出栈(出队) 

返回栈顶元素

判断栈是否为空和栈销毁


做题链接:225. 用队列实现栈 - 力扣(LeetCode) (leetcode-cn.com)


用队列实现栈

我们之前已经用单链表实现过队列,我们知道他的性质是先进先出,那怎么用两个队列来实现栈呢,用两个先进先出能否完成后进先出的栈。下面我们来完成第一个题:用队列实现栈

队列编写: 

读题可知,我们必须先将队列定义完成,此题是在有队列的前提下完成的,前面已经实现过队列了,那我们直接完成队列的编写:

typedef int QDatatype;

//定义单链表结点
typedef struct QueueNode
{
	QDatatype data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

void QueueInit(Queue* pq);

void QueueDestory(Queue* pq);

void QueuePush(Queue* pq,QDatatype x);

void QueuePop(Queue* pq);

bool QueueEmpty(Queue* pq);

size_t QueueSize(Queue* pq);

QDatatype QueueFront(Queue* pq);

QDatatype QueueBack(Queue* pq);



//队列初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

//队列销毁
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

//队尾插入
void QueuePush(Queue* pq, QDatatype x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	if (pq->head == NULL)
	{
		assert(pq->tail==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
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}

}

//判断是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL&&pq->tail == NULL;
}

//队列元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

//对头元素
QDatatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);
	return pq->head->data;
}

//队尾元素
QDatatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail);
	return pq->tail->data;
}

栈的实现分析:

队列是先进先出,对头尾进行操作,那两个队列如何实现一个后进显出的栈呢?这样说大家还可能不知道这到底要干啥,简单来说:

我给队列中入队1 2 3 4 5,怎么样才能让他出队5 4 3 2 1,在两个队列中实现。

图解如下:

 定义栈和栈的初始化

定义:栈由两个封装好的队列实现。

初始化:先给栈开辟空间,判断是否开辟成功,之后对栈内的两个队列进行初始化,用到我们定义队列时的初始化函数。

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


MyStack* myStackCreate() {
    MyStack* pst=(MyStack*)malloc(sizeof(MyStack));
    assert(pst);
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}

元素入栈(入队) 

对两个队列是否为空进行判断,将数据入队至非空队列,保留空队,方便数据来回导入。

void myStackPush(MyStack* obj, int x) {
    assert(obj);
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

元素出栈(出队) 

定义空和非空两个队列,每次对1 2队列进行判断赋给他们,方便后面的操作。

每次将非空队列的前Size-1个元素导入空队列中,删除非空队列的原数据,出队最后一个元素完成出栈。

注意细节:返回栈顶元素,栈顶即为非空队列的队尾元素,返回后再对非空队尾元素即栈顶进行出栈。

int myStackPop(MyStack* obj) {
    assert(obj);
    //判断空和非空是哪个
    Queue* emptyQ=&obj->q1;
    Queue* nonemptyQ=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        emptyQ=&obj->q2;
        nonemptyQ=&obj->q1;
    }

    //把非空队列前N个数据导入空队列
    //剩下一个删除实现后进先出
    while(QueueSize(nonemptyQ)>1)
    {
        QueuePush(emptyQ,QueueFront(nonemptyQ));
        QueuePop(nonemptyQ);
    }
    int top=QueueFront(nonemptyQ);
    QueuePop(nonemptyQ);
    return top;
}

返回栈顶元素

判断找出非空队列,直接反对非空队列队尾元素即可,队尾即栈顶。

int myStackTop(MyStack* obj) {
    assert(obj);
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

判断栈是否为空和栈销毁

判断:栈有两个队列,两个队列都为空栈才为空,直接ruturn即可。

销毁:栈中两队列,两队列还指向两块空间,销毁时先销毁队列,防止出现内存泄漏问题。

bool myStackEmpty(MyStack* obj) {
    assert(obj);
    return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
    assert(obj);
    QueueDestory(&obj->q1);
    QueueDestory(&obj->q2);
    free(obj);
}

猜你喜欢

转载自blog.csdn.net/weixin_53316121/article/details/123925994