吃饭的时候也能学的栈和队列

嗨害,搞完了比较难的链表,今天来搞一下轻松一点的栈和队列

目录

栈的概念和结构

栈的实现

队列的概念和结构

队列的实现

栈和队列练习题


栈的概念和结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。


栈的实现

初始化:

typedef int STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;		// 栈顶的位置
	int capacity;	// 容量
}ST;

void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

入栈:

void StackPush(ST* ps, STDataType x)//入栈
{
	assert(ps);
	// 
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

出栈:

void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}

判断栈是否为空:

bool StackEmpty(ST* ps)//判断栈是否为空
{
	assert(ps);
	return ps->top == 0;
}

返回栈顶元素:

STDataType StackTop(ST* ps)//返回栈顶元素
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

返回栈的大小:

int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}

销毁栈:

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

只能说很简单


队列的概念和结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头


队列的实现

初始化:

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QueueNode;

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


void QueueInit(Queue* p)
{
	assert(p);
	p->head = p->tail = NULL;
}

这里注意实现队列的时候要创建两个结构体,一个用来实现队列,存储队列的数据。一个用来存放队列的头和尾

销毁:

void QueueDestroy(Queue* p)
{
	assert(p);
	QueueNode* cur = p->head;
	while (cur)
	{
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}
	p->head = p->tail = NULL;
}

入队:

void QueuePush(Queue* p, QDataType x)
{
	assert(p);
	QueueNode* newcode = (QueueNode*)malloc(sizeof(QueueNode));
	assert(newcode);
	newcode->data = x;
	newcode->next = NULL;
	if (p->head == NULL)
	{
		p->head = p->tail = newcode;
	}
	else
	{
		p->tail->next= newcode;
		p->tail = newcode;
	}
}

分入队的是队列第一个数据还是第二个数据起两种情况

出队:

void QueuePop(Queue* p)
{
	assert(p);
	assert(p->head && p->tail);
	if (p->head->next == NULL)
	{
		free(p->head);
		p->head = p->tail = NULL;
	}
	else
	{
		QueueNode* newhead = p->head->next;
		free(p->head);
		p->head = newhead;
	}
}

同样,分出队的是唯一一个元素还是队列中还是其它元素两种情况。

判断是否为空队列:

bool QueueEmpty(Queue* p)
{
	assert(p);
	return p->head == NULL;
}

这里如果头为空,说明没有元素,返回1

输出队首:

QDataType QueueFront(Queue* p)
{
	assert(p);
	assert(p->tail);
	return p->head->data;
}

输出队尾:

QDataType QueueBack(Queue* p)
{
	assert(p);
	assert(p->tail);
	return p->tail->data;
}

输出队列长度:

size_t QueueSize(Queue* p)
{
	assert(p);
	QueueNode* cur = p->head;
	size_t count = 0;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

遍历计数器累加算出长度,也可以在队列的结构体中多定义一个变量用于存储长度

 难度依旧不高


栈和队列练习题

1. 括号匹配问题

#define _CRT_SECURE_NO_WARNINGS 1
typedef int STDataType;
typedef struct StackEmpty{
    int capacity;
    int top;
    STDataType *a;
}ST;
void StackInit(ST* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

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

void StackPush(ST* ps, STDataType x)//入栈
{
	assert(ps);
	// 
	if (ps->top == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		ps->a = (STDataType*)realloc(ps->a, newCapacity * sizeof(STDataType));
		if (ps->a == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->capacity = newCapacity;
	}
	ps->a[ps->top] = x;
	ps->top++;
}

void StackPop(ST* ps)//出栈
{
	assert(ps);
	assert(ps->top > 0);
	--ps->top;
}

bool StackEmpty(ST* ps)//判断栈是否为空
{
	assert(ps);
	return ps->top == 0;
}

STDataType StackTop(ST* ps)//返回栈顶元素
{
	assert(ps);
	assert(ps->top > 0);

	return ps->a[ps->top - 1];
}

int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
bool isValid(char * s)
{
    ST st;
    StackInit(&st);
    while(*s)
    {
        if(*s=='('||*s=='['||*s=='{')
        {
            StackPush(&st,*s);
        }
        else
        {
            if(StackEmpty(&st))//如果进来的时候栈为空说明第一个符号为右边的括号,直接返回
            return false;
            char c=StackTop(&st);
            StackPop(&st);
            if((*s==')'&&c!='(')
            ||(*s=='}'&&c!='{')
            ||(*s==']'&&c!='['))//如果和栈顶的括号不匹配返回false
            {
                StackDestory(&st);
                return false;
            }
        }
        s++;
    }
    bool ret=StackEmpty(&st);//如果只有左括号没有右括号说明栈不为空,返回false
    StackDestory(&st);
    return ret;
}

这个题要先实现出一个栈,所以比较麻烦,这里直接复制上面的代码,然后开始遍历字符串,当遇到左边的括号的时候把括号压入栈,当遇到右边的括号的时候进行出栈,和出栈的数据进行比较,如果一样则继续遍历,如果不一样的话直接返回false
还有几个需要注意的点:
1.如果字符串只有左边的括号,那最后判断栈是否为空的时候,栈不为空,返回false,如果是成对的括号那栈一定是空的
2.如果字符串第一个就是右边的括号,或者后面的也都是右边的括号,那直接返回空,在第一层else里开头进行判断栈是否为空(因为第一个不是左括号因此栈为空)

2. 用队列实现栈

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) {
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(QueueEmpty(empty))
    {
        QueuePush(nonempty,x);
    }
    else{
        QueuePush(empty,x);
    }
}

int myStackPop(MyStack* obj) {
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(!QueueEmpty(empty))
    {
        empty=&obj->q2;
        nonempty=&obj->q1;
    }
    while(QueueSize(nonempty)>1)
        {
            QueuePush(empty,QueueFront(nonempty));
            QueuePop(nonempty);
        }
        int a=QueueFront(nonempty);
        QueuePop(nonempty);
        return a;
}

int myStackTop(MyStack* obj) 
{
    Queue* empty=&obj->q1;
    Queue* nonempty=&obj->q2;
    if(!QueueEmpty(empty))
    {
        empty=&obj->q2;
        nonempty=&obj->q1;
    }
    return QueueBack(nonempty);
}

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

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

先把上面所有队列的代码直接复制粘贴到题目解答中(队列的实现),然后再开始写这道题的函数。队列是先进先出,栈是先进后出,因此要用队列实现栈的特性,我们可以用两个队列,

比如新入栈的元素为5,那么如果要出栈的话5应该先出来,那我们可以先把前4个元素复制到另一个队列中(同时弹出这个队列里的前四个元素)

达成这样的效果,这样第一个队列就剩下一个需要返回的栈顶元素,出栈同时返回即可 。

当入栈的时候就比较简单了,找到不为空的队列,把新的元素跟在队列后面即可(栈顶),剩下的函数都比较简单,在最后销毁的时候记得要销毁两层,先销毁掉两个队列,再释放栈的空间

3. 用栈实现队列

typedef struct {
    ST q1;
    ST q2;
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    StackInit(&obj->q1);
    StackInit(&obj->q2);
    return obj;
}

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

int myQueuePop(MyQueue* obj) {
    if(obj->q2.top==0)
    {
        while(StackSize(&obj->q1)>0)
    {
        StackPush(&obj->q2,StackTop(&obj->q1));
        StackPop(&obj->q1);
    }
    int a =StackTop(&obj->q2);
    StackPop(&obj->q2);
    return a;
    }
    else
    {
        int a =StackTop(&obj->q2);
    StackPop(&obj->q2);
    return a;
    }
}

int myQueuePeek(MyQueue* obj) {
   if(obj->q2.top==0)
    {
        while(StackSize(&obj->q1)>0)
    {
        StackPush(&obj->q2,StackTop(&obj->q1));
        StackPop(&obj->q1);
    }
    int a =StackTop(&obj->q2);
    return a;
    }
    else
    {
        int a =StackTop(&obj->q2);
    return a;
    }
}

bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->q1)&&StackEmpty(&obj->q2);
}

void myQueueFree(MyQueue* obj) {
    StackDestory(&obj->q1);
    StackDestory(&obj->q2);
    free(obj);
}

这个题和上面的题很像,是一个反过来的过程,也是要先把我们写的栈的代码先复制,这里就不复制了。整个题和上一个题的思路大致相同,

以删除为例,我们要删除的是队列的第一个元素,也就是1

那么出栈,把栈顶的元素入到另一个栈中,变成这样


此时1变成了另一个栈的栈顶,出栈即可。这里需要注意的是这两个栈分工明确,不再像上一题那样,这里的第一个栈一直用来push插入元素,第二个栈用来pop删除和peek显示队首元素,因此栈不像队列那样,两个队列互相倒顺序还是一样的,而栈互相倒以后顺序就相反了。这里也只需要当第二个栈里的元素全部被pop完以后再将第一个栈的元素压到第二个栈中。

4.设计循环队列

typedef struct {
    int *a;
    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->a=(int*)malloc(sizeof(int)*(k+1));
    obj->head=obj->tail=0;
    obj->k=k;
    return obj;
}

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

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return false;
    if(obj->head==obj->k)
    {
        obj->head=0;
    }
    else{
        obj->head++;
    }
    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;
    if(obj->tail==0)
    return obj->a[obj->k];
    else{
        return obj->a[obj->tail-1];
    }
}

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

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    if(obj->tail==obj->k&&obj->head==0)
    return true;
    else
    return obj->tail+1==obj->head;
}

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

做这个题的时候队列开辟的时候我们多开辟了一个空间,这样便于后面判断队列为空和为满,如果队列满了,则head和tail会相差一个位置(分别在头和尾时不是),为空时,head和tail相等,注意这里tail指向队尾的下一个位置

 注意因为我们多开了一个空间,因此有一个空间是不放数据的(这个空间是随机的),不是固定是最后一个

这个题就有点意思了,需要思考一下


好了,栈和队列的内容就先到此为止了,今天的学习结束!

  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

printf("雷猴");

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值