用队列实现栈

91 阅读6分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第22天,点击查看活动详情


大家好,我是芒果,一名非科班的在校大学生。对C/C++、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

题目

leetcode-cn.com/problems/im…


image-20220209220120880


思路:

栈:先进后出

队列:先进先出

保持一个队列为空,另一个队列存放数据

image-20220209220137722


image-20220209220203475

队列中的数据导入到另一个队列:元素插入的顺序不变


接口1:栈的结构

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

里面包含两个队列

image-20220209220212272


接口2:初始化

MyStack* myStackCreate() {
    //动态开辟一个栈结构体
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));
    //使用我们自己实现的接口初始化两个队列
    QueueInit(&st->q1);
    QueueInit(&st->q2);
    return st;
}

错误写法

MyStack* myStackCreate() {
 MyStack st ;
 return &st;
}

st是局部变量,出了函数,空间就被销毁了。st的地址是野指针,返回了已经销毁的栈空间的地址


接口3:插入数据

永远保持一个队列为空

image-20220209220221923

// 入数据
void myStackPush(MyStack* obj, int x) {
       //往不为空的队列中入数据
        if(!QueueEmpty(&obj->q1))
        {
            QueuePush(&obj->q1,x);
        }
        else
        {
            QueuePush(&obj->q2,x);
        }
}

接口4: 删除数据

image-20220209220233097

//出数据
int myStackPop(MyStack* obj) 
{
    //把有数据的队列的数据导入到空队列
​
    //先假设某一个队列不为空,假设队列q1不为空,q2为空
    Queue* noemptyQ = &obj->q1; 
    Queue* emptyQ = &obj->q2;
​
    //如果q2不为空,说明假设反了
    if(!QueueEmpty(&obj->q2))
    {
        emptyQ = &obj->q1;
        noemptyQ = &obj->q2;
    }
    //把不空队列的数据导入到空队列,直到不空队列只剩一个元素
    while(QueueSize(noemptyQ) > 1)
    {
        QueuePush(emptyQ,QueueFront(noemptyQ));
        QueuePop(noemptyQ);
    }
    int top = QueueFront(noemptyQ);
    QueuePop(noemptyQ);
    return top;
}

接口5:返回栈顶数据

image-20220209220243112

int myStackTop(MyStack* obj) 
{
    //那个队列有数据,就返回哪个队列的队尾数据
    if(!QueueEmpty(&obj->q1))
    {
        return QueueBack(&obj->q1);
    }
    else
    {
        return QueueBack(&obj->q2);
    }
}

接口6:判断栈是否为空

如果两个队列都为空->栈为空

image-20220209220305198

bool myStackEmpty(MyStack* obj) {
    //如果两个队列都为空->栈为空
    return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2); 
}

接口7:释放空间

image-20220209220319311

先调用我们自己写的接口:释放两个队列

然后再释放我们动态开辟的栈结构体

image-20220209220332641

void myStackFree(MyStack* obj)
{
    //先释放两个队列
    QueueDestory(&obj->q1);
    QueueDestory(&obj->q2);
    //再释放动态开辟的栈结构体
    free(obj);
}

实现的接口

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


MyStack* myStackCreate() {
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));

    QueueInit(&st->q1);
    QueueInit(&st->q2);
    return st;
}

// 入数据
void myStackPush(MyStack* obj, int x) {
       //往不为空的队列中入数据
        if(!QueueEmpty(&obj->q1))
        {
            QueuePush(&obj->q1,x);
        }
        else
        {
            QueuePush(&obj->q2,x);
        }
}
//出数据
int myStackPop(MyStack* obj) 
{
    //把有数据的队列的数据导入到空队列

    //先假设某一个队列不为空,假设队列q1不为空,q2为空
    Queue* noemptyQ = &obj->q1; 
    Queue* emptyQ = &obj->q2;

    //如果q2不为空,说明假设反了
    if(!QueueEmpty(&obj->q2))
    {
        emptyQ = &obj->q1;
        noemptyQ = &obj->q2;
    }
    //把不空队列的数据导入到空队列,直到不空队列只剩一个元素
    while(QueueSize(noemptyQ) > 1)
    {
        QueuePush(emptyQ,QueueFront(noemptyQ));
        QueuePop(noemptyQ);
    }
    int top = QueueFront(noemptyQ);
    QueuePop(noemptyQ);
    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)
{
    //先释放两个队列
    QueueDestory(&obj->q1);
    QueueDestory(&obj->q2);
    //再释放动态开辟的栈结构体
    free(obj);
}

总代码

typedef int QDataType;
//队列中的结构体
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QueueNode;

//存放指向结构体的两个指针
typedef struct Queue
{
	QueueNode* head;//指向链表的头
	QueueNode* tail;//指向链表的尾
}Queue;

//初始化
void QueueInit(Queue* pq);

//销毁
void QueueDestory(Queue* pq);

//队尾入数据
void QueuePush(Queue* pq, QDataType x);

//队头出数据
void QueuePop(Queue* pq);

//取队头数据
QDataType QueueFront(Queue* pq);

//取队尾数据
QDataType QueueBack(Queue* pq);

//队列的长度
int QueueSize(Queue* pq);

//判断队列是否为空
bool QueueEmpty(Queue* pq);

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

}

//销毁
void QueueDestory(Queue* pq)
{
	assert(pq);
	//链表要遍历销毁结点,不能直接销毁
	QueueNode* cur = pq->head;
	while (cur)
	{
		//保存下一个结点
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
}

//队尾入数据
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//相当于尾插
	
	//构建新结点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->next = NULL;
	newnode->data = x;
	//情况1:链表为空
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newnode;
	}

	//情况2:
	//tail newnode 
	else
	{
		pq->tail->next = newnode; //尾插
	//注意如果一开始没有数据,pq->tail = NULL,此时会出错,相当于对空指针解引用
	//所以没有数据时需要单独判断
		pq->tail = newnode;//指向新的尾
	}
	
}

//队头出数据
void QueuePop(Queue* pq)
{
	assert(pq);
	//情况1:链表为空
	/*if (pq->head == NULL)
	{
		return -1;
	}*/
	assert(!QueueEmpty(pq));

	//情况2
	QueueNode* next = pq->head->next;//保存下一个结点
	free(pq->head);//释放队头
	pq->head = next;//next成为新的队头

	//注意!!!!如果一直删,删最后一个时候 此时next为NULL的,如果不把tail也置空,就会造成野指针
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//链表为空
	/*if (pq->head == NULL)
	{
		return -1;
	}*/
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

//队列的长度
int QueueSize(Queue* pq)
{
	assert(pq);
	//方法1:遍历统计
	//方法2:定义结构体时添加一个size变量,入数据:size++ 出数据size -- 用size标志队列长度

	//遍历统计
	QueueNode* cur = pq->head;
	int count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	/*if (pq -> head == NULL)
	{
		return true;
	}
	else
	{
		return false;
	}*/
	return pq->head == NULL;
}


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


MyStack* myStackCreate() {
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));

    QueueInit(&st->q1);
    QueueInit(&st->q2);
    return st;
}

// 入数据
void myStackPush(MyStack* obj, int x) {
       //往不为空的队列中入数据
        if(!QueueEmpty(&obj->q1))
        {
            QueuePush(&obj->q1,x);
        }
        else
        {
            QueuePush(&obj->q2,x);
        }
}
//出数据
int myStackPop(MyStack* obj) 
{
    //把有数据的队列的数据导入到空队列

    //先假设某一个队列不为空,假设队列q1不为空,q2为空
    Queue* noemptyQ = &obj->q1; 
    Queue* emptyQ = &obj->q2;

    //如果q2不为空,说明假设反了
    if(!QueueEmpty(&obj->q2))
    {
        emptyQ = &obj->q1;
        noemptyQ = &obj->q2;
    }
    //把不空队列的数据导入到空队列,直到不空队列只剩一个元素
    while(QueueSize(noemptyQ) > 1)
    {
        QueuePush(emptyQ,QueueFront(noemptyQ));
        QueuePop(noemptyQ);
    }
    int top = QueueFront(noemptyQ);
    QueuePop(noemptyQ);
    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)
{
    //先释放两个队列
    QueueDestory(&obj->q1);
    QueueDestory(&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);
*/

\