数据结构——栈与队列的专题

前言

本篇是围绕栈与队列来展开,需要知道一定有关它们的相关基础知识

栈的详解
队列的详解

还有一道基础的栈题——有效的括号

一、用队列实现栈

原题链接:用队列实现栈

在这里插入图片描述

解题思路:
首先创建一个队列结构

然后用两个队列去实现一个栈,每次始终保持一个队列为空

入栈操作相当于给非空队列进行入队操作
出栈操作相当于非空队列的队尾元素出队,此时需要把非空队列除最后一个元素之外的其余元素入队到空队列,然后出队最后一个队尾元素

在这里插入图片描述

在这里插入图片描述

//实现队列
typedef int QDateType;
typedef struct QueueNode
{
	QDateType val;
	struct QueueNode* next;
}QNode;

//管理队列的结构体
typedef struct Queue
{
	QNode* phead;//指向队头的指针
	QNode* ptail;//指向队尾的指针
	int size;//队列数据个数
}Queue;

//队列的初始化
void QueueInit(Queue* pq);
//队列的销毁
void QueueDestroy(Queue* pq);

//队尾插入数据
void QueuePush(Queue* pq, QDateType x);
//队头删除数据
void QueuePop(Queue* pq);

//取出队尾的数据
QDateType QueueBack(Queue* pq);
//取出队头的数据
QDateType QueueFront(Queue* pq);

//取出队列数据个数
int QueueSize(Queue* pq);

//判断置空
bool QueueEmpty(Queue* pq);


//队列的初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

//队列的销毁 
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}


//队尾插入数据
void QueuePush(Queue* pq, QDateType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;

	//没有结点时,头插
	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	//有结点时,尾插
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	//只有一个结点
	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--;
}

//取出队尾的数据
QDateType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}

//取出队头的数据
QDateType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}

//取出队列数据个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

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


typedef struct
{
    Queue p1;
    Queue p2;
} MyStack;


MyStack* myStackCreate()
{
    //开辟一个模拟栈
    MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
    //将队列初始化
    QueueInit(&(pst->p1));
    QueueInit(&(pst->p2));
    return pst;
}

void myStackPush(MyStack* obj, int x)
{
    //将数据插入非空队列
    if(!QueueEmpty(&(obj->p1)))
    {
        QueuePush(&(obj->p1), x);
    }
    else
    {
        QueuePush(&(obj->p2), x);
    }
}

int myStackPop(MyStack* obj)
{
    //假设法
    Queue* empty = &(obj->p1);
    Queue* nonempty = &(obj->p2);
    if(!QueueEmpty(&(obj->p1)))
    {
       empty = &(obj->p2);
       nonempty = &(obj->p1);
    }
    //将size-1个数据移动到空队列
    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->p1)))
    {
        return QueueBack(&(obj->p1));
    }
    else
    {
        return QueueBack(&(obj->p2));
    }
}

bool myStackEmpty(MyStack* obj)
{
    return QueueEmpty(&(obj->p1)) && QueueEmpty(&(obj->p2));
}

void myStackFree(MyStack* obj)
{
    //将队列销毁
    QueueDestroy(&(obj->p1));
    QueueDestroy(&(obj->p2));
    //将模拟栈销毁
    free(obj);
}

二、用栈实现队列

原题链接:用栈实现队列
在这里插入图片描述

解题思路:

此题可以用两个栈实现,一个栈进行入队操作,另一个栈进行出队操作

出队操作: 当出队的栈不为空时,直接进行出栈操作,如果为空,需要把入队的栈元素全部导入到出队的栈,然后再进行出栈操作

在这里插入图片描述

//实现动态增长的栈
typedef int STDateType;
typedef struct Stack
{
	STDateType* a;
	int top;//栈顶
	int capacity;//容量
}ST;

//初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);

//入栈和出栈
void STPush(ST* pst, STDateType x);
void STPop(ST* pst);

//取栈顶数据
STDateType STTop(ST* pst);

//判空
bool STEmpty(ST* pst);

//获得数据的个数
int STSize(ST* pst);

//初始化和销毁
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	//top指向栈顶数据的下一个元素
	pst->top = 0;

	//top指向栈顶元素
	//pst->top = -1;

	pst->capacity = 0;
}

void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top = 0;
	pst->capacity = 0;
}

//入栈和出栈
void STPush(ST* pst, STDateType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		//扩容
		int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		STDateType* tmp = realloc(pst->a, newcapacity * sizeof(ST));
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top++] = x;
}

void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

//取栈顶数据
STDateType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}

//判空
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}

//获得数据的个数
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}



typedef struct
{
    ST pushst;//专门插入数据的
    ST popst;//专门出数据的
} MyQueue;


MyQueue* myQueueCreate()
{
    //开辟一个模拟队列
    MyQueue* pst = (MyQueue*)malloc(sizeof(MyQueue));
    //初始化栈
    STInit(&(pst->pushst));
    STInit(&(pst->popst));
    return pst;
}

void myQueuePush(MyQueue* obj, int x)
{
    //默认插入第一个栈
    STPush(&(obj->pushst),x);
}
int myQueuePeek(MyQueue* obj);
int myQueuePop(MyQueue* obj)
{
    //调用peek函数
    int top = myQueuePeek(obj);
    //删除栈顶元素
    STPop(&(obj->popst));
    return top;
}

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));
}

bool myQueueEmpty(MyQueue* obj)
{
    return STEmpty(&(obj->pushst)) && STEmpty(&(obj->popst));
}

void myQueueFree(MyQueue* obj)
{
    //将栈销毁
    STDestroy(&(obj->pushst));
    STDestroy(&(obj->popst));
    //将模拟队列销毁
    free(obj);
}

三、设计循环队列

原题链接:设计循环队列

在这里插入图片描述

解题思路:

通过一个定长数组实现循环队列

入队:首先要判断队列是否已满,再进行入队的操作,入队操作需要考虑索引循环的问题,当索引越界,需要让它变成最小值
出队:首先要判断队列是否为空,再进行出队操作,出队也需要考虑索引循环的问题

判空: 队头 = 队尾
判满: 队尾 + 1 = 队头


在这里插入图片描述

typedef struct
{
    int* a;//动态数组储存数据
    int head;//指向队头
    int tail;//指向队列尾数据的下一个位置
    int k;//队列长度    
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k)
{
    //开辟一个循环队列
    MyCircularQueue* pst = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    //创建一个可以存放k个元素的循环队列,实际申请的空间为k + 1
    pst->a = (int*)malloc((k + 1) * sizeof(int));
    pst->head = 0;
    pst->tail = 0;
    pst->k = k;
    return pst;
}

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

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

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
{
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    //注意回绕问题
    obj->a[obj->tail++] = value;
    obj->tail %= obj->k + 1;
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj)
{
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    //注意回绕问题
    obj->head++;
    obj->head %= obj->k + 1;
    return true;
}

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

int myCircularQueueRear(MyCircularQueue* obj)
{
     if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[(obj->tail - 1 + obj->k + 1 )% (obj->k + 1)];
}

void myCircularQueueFree(MyCircularQueue* obj)
{
    free(obj->a);
    free(obj);    
}

  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值