【数据结构与算法】栈和队列的综合应用:1.用栈实现队列 2.用队列实现栈 3.设计循环队列

🔥小龙报:个人主页

🎬作者简介:C++研发,嵌入式,机器人等方向学习者

❄️个人专栏:《C语言》《【初阶】数据结构与算法》
永远相信美好的事情即将发生

文章目录


前言

栈和队列作为数据结构的基础线性表,核心差异在于 "先进后出" 与 "先进先出" 的特性,但实际开发中常需要灵活转换二者的行为模式。本文聚焦栈和队列的经典综合应用:用栈模拟队列、用队列实现栈,以及高频面试考点 ------ 设计循环队列。通过拆解算法原理、实现完整代码,帮助读者吃透底层逻辑,掌握线性表的灵活运用思路。


一、用栈实现队列

1.1题目

链接:用栈实现队列

1.2 算法原理

首先就是要先定义栈结构创建两个栈:一个栈专门入队 + 一个栈专门出队;

出队:先检查出队栈是否有元素,如果有就直接出队,如果没有就把入队栈的元素全部push到出队栈,然后出队栈弹出栈顶元素即可;

入队:直接入入队栈即可

1.3 代码

复制代码
typedef int StkDataType;

typedef struct stack
{
	StkDataType* a; //数组
	int top;
	int capacity;
}stack;

//初始化
void StkInit(stack* stk)
{
	stk->a = NULL;
	stk->top = 0;
	stk->capacity = 0;
}

//销毁
void StkDestory(stack* stk)
{
	free(stk->a);
	stk->a = NULL;
	stk->top = 0;
	stk->capacity = 0;
}

//插入
void StkPush(stack* stk, StkDataType x)
{
	assert(stk);
	if (stk->top == stk->capacity)
	{
		int newcapacity = stk->capacity == 0 ? 4 : 2 * stk->capacity;
		StkDataType* temp = (StkDataType*)realloc(stk->a, newcapacity * sizeof(StkDataType));
		if (temp == NULL)
		{
			printf("fail!\n");
			exit(-1);
		}
		stk->a = temp;
		stk->capacity = newcapacity;
	}
	stk->a[stk->top++] = x;
}


//弹出
void StkPop(stack* stk)
{
	assert(stk && stk->top > 0);
	stk->top--;
}

//大小
int StkSize(stack* stk)
{
	return stk->top;
}

//栈顶
StkDataType StkTop(stack* stk)
{
	assert(stk && stk->top > 0);
	return stk->a[stk->top - 1];
}

//判空
bool StkEmpty(stack* stk)
{
	assert(stk);
	return stk->top == 0;
}


typedef struct 
{
    stack stk1; //入队
    stack stk2; //出队

} MyQueue;


MyQueue* myQueueCreate() 
{
    MyQueue* psk = (MyQueue*)malloc(sizeof(MyQueue));
    StkInit(&(psk->stk1));
    StkInit(&(psk->stk2));
    return psk;
}

void myQueuePush(MyQueue* obj, int x) 
{
  StkPush(&(obj->stk1),x);
}

int myQueuePop(MyQueue* obj) 
{
    //出队栈不为空
    if(!StkEmpty(&(obj->stk2)))
    {
        int ret = StkTop(&(obj->stk2));
        StkPop(&(obj->stk2));
        return ret;
    }
    else
    {
        int size = StkSize(&(obj->stk1));
        while(size--)
        {
            int top = StkTop(&(obj->stk1));
            StkPush(&(obj->stk2),top);
            StkPop(&(obj->stk1));
        }
        int ret = StkTop(&(obj->stk2));
         StkPop(&(obj->stk2));
        return ret;
    }
    
}

int myQueuePeek(MyQueue* obj) 
{
    //出队栈不为空
    if(!StkEmpty(&(obj->stk2)))
    {
        int ret = StkTop(&(obj->stk2));
        return ret;
    }
    else
    {
        int size = StkSize(&(obj->stk1));
        while(size--)
        {
            int top = StkTop(&(obj->stk1));
            StkPush(&(obj->stk2),top);
            StkPop(&(obj->stk1));
        }
        int ret = StkTop(&(obj->stk2));
        return ret;
    }
}

bool myQueueEmpty(MyQueue* obj) 
{
    return (StkEmpty(&(obj->stk1)) && StkEmpty(&(obj->stk2)));
}

void myQueueFree(MyQueue* obj) 
{
    StkDestory(&(obj->stk1));
    StkDestory(&(obj->stk2));
    free(obj);
    obj = NULL;
}

二、用队列实现栈

2.1 题目

链接:用队列实现栈

2.2 算法原理

首先就是要先定义队列结构创建两个队列:并保持一个队列一定为空

出栈:哪个栈不为空元素入哪个,出栈时把不为空的队列中的size - 1个元素入到为空的队列中,那么那么原本不为空的队列最后一个元素即为栈顶元素

2.3 代码

复制代码
typedef int QueueDataType;

typedef struct QueueNode
{
	QueueDataType val;
	struct QueueNode* next;
}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 QueueDestory(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* temp = cur->next;
		free(cur);
		cur = temp;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
	
}

//添加
void QueuePush(Queue* pq, QueueDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("开辟失败!");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;

	//尾插
	if (pq->ptail == NULL)
		pq->phead = pq->ptail = newnode;
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

//删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size > 0);

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

//队头
QueueDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->size > 0);
	return pq->phead->val;
}

//队尾
QueueDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->size > 0);
	return pq->ptail->val;

}

//判空
bool QueueIsEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

//队列大小
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

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


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

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

int myStackPop(MyStack* obj) 
{
    //判断谁为空队列 --- 假设法
    Queue* empty = &obj->q1;
    Queue* noempty = &obj->q2;
    if(!QueueIsEmpty(&obj->q1))
    {
        noempty = &obj->q1;
        empty = &obj->q2;
    }

    //让非空队列的前n-1个出队
    while(noempty->size > 1)
    {
        QueuePush(empty,QueueFront(noempty));
        QueuePop(noempty);
    }
    int ret = QueueFront(noempty);
    QueuePop(noempty);
    return ret;
}

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

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

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

三、设计循环队列

3.1 题目

链接:设计循环队列

3.2 算法原理

循环的关键是取模。

1.如何解决队列是否满?多开一个空间

定义两个变量head,tail,其中tail指向队尾的下一个位置

3.3 代码

复制代码
typedef struct 
{
    int* data;
    int head;
    int tail;
    int k;
} MyCircularQueue;

bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);

MyCircularQueue* myCircularQueueCreate(int k)
{
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->data = (int*)malloc((k + 1) * sizeof(int));
    obj->head = obj->tail = 0;
    obj->k = k;
    return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    if(myCircularQueueIsFull(obj))
        return false;
    else
    {
        obj->data[obj->tail] = value;
        obj->tail++;
        obj->tail %= (obj->k + 1);
        return true;
    }
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
   if(myCircularQueueIsEmpty(obj))
        return false;
    else
    {
        obj->head++;
        obj->head %= (obj->k + 1);
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj) 
{
   if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->data[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj) 
{
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->tail == 0 ? obj->data[obj->k]:obj->data[obj->tail - 1];
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
    return obj->head == obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
    return (obj->tail + 1) % (obj->k + 1) == obj->head;
}

void myCircularQueueFree(MyCircularQueue* obj) 
{
   free(obj->data);
   obj->data = NULL;
   free(obj); 
}

总结与每日励志

✨ 本文通过三个经典案例,拆解了栈与队列的核心转换逻辑:双栈模拟队列靠 "入队栈 + 出队栈" 分流操作,双队列实现栈靠 "空队列中转" 定位栈顶,循环队列则通过 "多开空间 + 取模" 解决首尾衔接问题。这些场景本质是对 "先进先出 / 后进先出" 特性的灵活重构。学习数据结构没有捷径,坚持拆解每一个逻辑细节,终能把复杂问题拆解为可落地的代码,永远相信坚持的力量!

相关推荐
dapeng28702 小时前
使用Fabric自动化你的部署流程
jvm·数据库·python
重生之我是Java开发战士2 小时前
【广度优先搜索】队列:N叉树的层序遍历,二叉树的锯齿形层序遍历,二叉树的最大宽度,在每个树行中找最大值
数据结构·算法·leetcode·广度优先
Aurorar0rua2 小时前
CS50 x 2024 Notes C - 01
c语言·学习方法
sxhcwgcy2 小时前
Spring.factories
java·数据库·spring
Mike117.2 小时前
GBase 8a 数据同步实践:从 T+1 同步、实时镜像到一写多读的落地思路
java·服务器·数据库
qq_416018722 小时前
移动平台C++开发指南
开发语言·c++·算法
eggwyw2 小时前
Redis 设置密码(配置文件、docker容器、命令行3种场景)
数据库·redis·docker
油丶酸萝卜别吃2 小时前
Redis 通常应用于哪些场景?
数据库·redis·缓存
王璐WL2 小时前
【C++】string的经典算法题
开发语言·c++·算法