栈&队列(c语言版)

目录

1.栈

1.1概念和结构

1.2栈的实现

1.2.1栈的头文件

1.2.2栈的具体实现

1.2.2.1初始化

1.2.2.2栈的销毁

1.2.2.3入栈

1.2.2.4出栈

1.2.2.5获取栈顶元素

1.2.2.6判断空

1.2.2.7获取栈数据数量

 2.队列

2.1概念和结构

2.2队列实现

2.2.1队列头文件

2.2.2队列具体实现

2.2.2.1尾插

2.2.2.2出队

2.2.2.3队列初始化

2.2.2.4队列销毁

2.2.2.5获取队头数据

2.2.2.6获取队尾数据

2.2.2.7判断空

2.2.2.8队列数据数量

3.例题

3.1有效的括号

3.2用队列实现栈

3.3用栈实现队列

3.4设计循坏队列


1.栈

1.1概念和结构

一种比较特殊的线性表,在固定一段进行插入和删除操作,这一端称为栈顶,另一端称为栈顶。比较形象的形容的话,就是一个杯子,我们平整的放入一些饼干,放进去和拿出来都是通过杯口,而栈遵循的是后进先出原则,在这个例子中,就是我们后放进去的饼干,如果要拿饼干,也是最先拿出来的饼干。

我们将杯口称为栈顶,杯底称为栈底。

压栈就是将数据放入栈中,在上面例子中,就是把饼干平整放进去。

出栈就是把数据从栈中拿出来(对于栈来说就是删除数据),也就是把饼干从杯子中拿出来。(注意,是从杯口拿出来)

在实现上,我们更推荐数组,因为数组在尾插尾删上效率很高,栈顶就可以是数组的末尾。

链表也可以,但是效率比不过数组

1.2栈的实现

1.2.1栈的头文件

#pragma once


#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int STDataType;
//方便改数据类型
typedef struct Stack
{
	int* a;
	int top;
	int capacity;
}ST;
//栈的结构,采用数组实现,top是栈顶元素的下一个坐标
//capacity是扩容,本质跟顺序表的扩容一样

void STInit(ST* pst);
void STDestory(ST* pst);
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);

1.2.2栈的具体实现

1.2.2.1初始化
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->capacity = 0;
	pst->top = 0;

}
断言空指针

数组指针置空

数组大小和top都置为0
1.2.2.2栈的销毁
void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top=pst->capacity = 0;
}

断言空指针

free掉数组空间

数组指针置空

top和capacity都设置为0
1.2.2.3入栈
void STPush(ST* pst, STDataType x)
{
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	

	pst->a[pst->top] = x;
	pst->top++;

}
因为栈只有尾插,扩容操作就不额外做个函数了,直接放在这里

如果top==capacity,因为top是栈顶元素的下一个下标,capacity是数组大小,
那这样就说明此时数组数据已经满了,需要扩容

接下来就是常规的扩容操作,定一个newcapacity,如果数组大小为空就赋值4,非0就是扩容2倍

动态申请数组空间,判断开辟是否失败

将开辟的空间地址赋给栈的数组,空间大小记得更新

接下来赋值,top是栈顶元素的下一个下标,所以直接top位置赋值即可

top记得++
1.2.2.4出栈
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}
断言空指针

断言此时栈里是否还有数据

数组的删除只能通过(不理他)也就是直接top--,让这个位置跟别的存放随机值的空间一个待遇

1.2.2.5获取栈顶元素
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	
	return pst->a[pst->top - 1];
}
因为要返回具体的值,所以返回类型是SLDataType

断言空指针

断言空栈

top是栈顶元素的下一个下标,所以直接返回top-1位置元素即可
1.2.2.6判断空
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
断言空指针

判断是否为空,只要确认top是不是0就行,
因为是返回bool类型,返回一个关系表达式就可以
1.2.2.7获取栈数据数量
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}
top是栈顶元素的下一个下标,而数组是从0开始的
那么top正好就是数组有效数据的个数

返回top即可

 2.队列

2.1概念和结构

队列可以想象成一个中空的管子,我们从一段(队尾)放东西进去,再从另一端(队头)出来

队列遵循先进先出,放数据(放入队尾)称为入队,拿数据(把队头数据删除)称为出队

结构上我们采用链表,因为需要头删和尾插,链表适合频繁删改的,综合上考虑还是选择链表。

2.2队列实现

2.2.1队列头文件

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDataType;
//方便改数据类型
typedef struct QueueNode
{
	QDataType val;//数据
	struct QueueNode* next;//链表下一个地址
}QNode;
//队列的一个节点
typedef struct Queue
{
	QNode* phead;//指向队列链表结构的头结点,也就是队头
	QNode* ptail;//指向队列链表结构的尾节点,也就是队尾
	int size;//队列大小
}Queue;
//队列
//方便直接引用头结点和尾节点

//入队
void QueuePush(Queue* pq, QDataType x);
//出队
void QueuePop(Queue* pq);
//初始化
void QueueInit(Queue* pq);
//队列销毁
void QueueDestrory(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//确认队列是否为空
bool QueueEmpty(Queue* pq);
//获取队列数据数量
int QueueSize(Queue* pq);

2.2.2队列具体实现

2.2.2.1尾插
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
断言空指针

开辟一个队列节点,判断开辟是否失败

给节点赋值,指针置空

判断当前队列是否是空队列,是的话,两个指针都先指向这个新节点

否则的话,让ptail的next先指向新节点(尾插),再让ptail指向新的尾节点
size记得++
2.2.2.2出队
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;
	if (pq->phead == NULL)
	{
		pq->ptail = NULL;
	}
	pq->size--;
}
断言空指针和空队列

把队头也就是phead指向的节点赋给del指针

让phead指向第二个节点,让第二个节点成为新的队头

free掉del指向的空间,del指针记得置空

如果此时只有一个节点,删了之后就是空队列,那么ptail也置空,以免引用野指针

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

断言空指针

两个指针都置空

size也置0
2.2.2.4队列销毁
void QueueDestrory(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;
}
队列销毁

断言空指针

参考链表的销毁,逐级销毁

记得把phead和ptail也都置空,size变0
2.2.2.5获取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

断言空指针和空队列

返回phead指向的队头节点的val即可
2.2.2.6获取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}
断言空指针和空队列

返回ptail指向的队列尾节点的val即可
2.2.2.7判断空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL;
}
断言空指针


因为bool类型,返回一个关系表达式即可
2.2.2.8队列数据数量
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}
断言空指针


返回size即可

 

3.例题

3.1有效的括号

20. 有效的括号 - 力扣(LeetCode)

#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef char STDataType;

typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;

void STInit(ST* pst);
void STDestory(ST* pst);
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);

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

}
void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top=pst->capacity = 0;
}
void STPush(ST* pst, STDataType x)
{
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	

	pst->a[pst->top] = x;
	pst->top++;

}
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}
STDataType 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;
}



//这里上面都是手搓的栈,我直接cv了


bool isValid(char* s) {
    ST S;
    STInit(&S);
    //创建栈,初始化
    while(*s)
    {
        if(*s=='('||*s=='{'||*s=='[')
        {
            STPush(&S,*s);
        }
        //这题我们可以把左括号都先入栈
        else
        {
						if(STEmpty(&S))
						{
							return false;
						}
            //考虑左括号少于右括号的情况
            char top=STTop(&S);
            STPop(&S);
            if(top!='{' && *s=='}' ||
            top!='[' && *s==']' ||
			top!='(' && *s==')')
			{
			    return false;
			}
        }
        //遇到非左括号,直接出栈,左括号和非左括号对比,如果
        不是直接false
        s++;
        //s记得++
    }
			bool ret=STEmpty(&S);
    //考虑右括号少于左括号的情况
    STDestory(&S);
    
    return ret;
}

3.2用队列实现栈

225. 用队列实现栈 - 力扣(LeetCode)


#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* next;
}QNode;
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
void QueueInit(Queue* pq);
void QueueDestrory(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);

#define _CRT_SECURE_NO_WARNINGS 1



void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pq->ptail == NULL)
	{
		pq->ptail = pq->phead = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;
	if (pq->phead == NULL)
	{
		pq->ptail = NULL;
	}
	pq->size--;
}
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
void QueueDestrory(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;
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->phead == NULL;
}
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}
//上面都是队列的接口和结构,cv即可
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;
//用两个队列实现栈

//创建一个栈
//申请栈空间,初始化两个队列.
MyStack* myStackCreate() {
    MyStack*pst=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&pst->q1);
    QueueInit(&pst->q2);
    return pst;
}

//入栈
//我们把数据全部压到一个非空队列上,如果都是空队列,
//选一个队列压进去即可
void myStackPush(MyStack* obj, int x) {
    if(!QueueEmpty(&obj->q1))
    {
        QueuePush(&obj->q1,x);
    }
    else
    {
        QueuePush(&obj->q2,x);
    }
}

//获取栈顶元素并移除
int myStackPop(MyStack* obj) {
    Queue*emptyq=&obj->q1;
    Queue*nonemptyq=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        emptyq=&obj->q2;
        nonemptyq=&obj->q1;
    }
    while(QueueSize(nonemptyq)>1)
    {
        QueuePush(emptyq,QueueFront(nonemptyq));
        QueuePop(nonemptyq);
    }
    int top=QueueFront(nonemptyq);
    QueuePop(nonemptyq);
    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) {
    QueueDestrory(&obj->q1);
    QueueDestrory(&obj->q2);
    free(obj);
}

3.3用栈实现队列

232. 用栈实现队列 - 力扣(LeetCode)


#include<assert.h>
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int STDataType;

typedef struct Stack
{
	int* a;
	int top;
	int capacity;
}ST;

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

}

void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->top=pst->capacity = 0;
}
void STPush(ST* pst, STDataType x)
{
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = realloc(pst->a, sizeof(STDataType) * newcapacity);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	

	pst->a[pst->top] = x;
	pst->top++;

}


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


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

//上面都是栈的接口,cv即可
typedef struct {
    ST q1;
    ST q2;
} MyQueue;
//队列采用两个栈

//创建队列
//开辟队列空间,初始化两个栈
MyQueue* myQueueCreate() {
    MyQueue*mq=(MyQueue*)malloc(sizeof(MyQueue));
    STInit(&mq->q1);
    STInit(&mq->q2);
    return mq;
}

//队列插入,都入栈到q1栈上
void myQueuePush(MyQueue* obj, int x) {
    assert(obj);
        STPush(&obj->q1,x);
}

//返回队头元素
int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&obj->q2))
    {
        while(!STEmpty(&obj->q1))
        {
            STPush(&obj->q2,STTop(&obj->q1));
            STPop(&obj->q1);
        }
    }
    return STTop(&obj->q2);
}
//在q2栈空的情况下,先把q1的数据出栈,入栈到q2,这样q2栈顶元素就是队头元素

//移除队头元素并返回队头元素
int myQueuePop(MyQueue* obj) {
    assert(obj);
    int ret=myQueuePeek(obj);
    STPop(&obj->q2);
    return ret;

}
//断言空指针
//直接调用上面写的返回队头元素函数,然后把q2栈顶元素删除即可

//判断队列是否为空,引用两个判断栈空接口即可
bool myQueueEmpty(MyQueue* obj) {
    assert(obj);
    return (STEmpty(&obj->q1) &&STEmpty(&obj->q2));
}

//销毁队列,调用两个栈的销毁接口即可。
void myQueueFree(MyQueue* obj) {
    assert(obj);
    STDestory(&obj->q1);
    STDestory(&obj->q2);
    free(obj);
    
}

3.4设计循坏队列

622. 设计循环队列 - 力扣(LeetCode)




typedef struct {
    int *a;
    int front;
    int back;
    int k;
} MyCircularQueue;
//采用数组形式构建循环队列
//front和back是用来定位的,k是用来标记队列长度的

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=0;
    obj->back=0;
    obj->k=k;
    return obj;
}
//构造函数,开辟队列空间,开辟数组空间,front和back都置0,k直接赋值

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->back+1)%(obj->k+1)==obj->front;
}
//怎么判断是否满,back是下一个准备被赋值的位置,
//我们front和back中间是有要有一个空间不存数据,
//做到back+1==front就是满状态,那么back+1%一个k+1,最终的值会是0-k,
//具体可以自己画下图,假如back是7,k是6,说明1-6下标都已被赋值,此时应该是满的
//back+1%7,就等于1,正好是front的值,从而能够判断满

 bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return (obj->front==obj->back);
}
//空,说明此时back和front相等。

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    obj->a[obj->back]=value;
    obj->back++;
    obj->back%=(obj->k+1);
    return true;
}
 //入队,判断是否满,给back位置赋值,记得把back%k+1,保持back不越界

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    ++obj->front;
    obj->front%=(obj->k+1);
    return true;
}
//判断是否空,然后让front++即可,记得%,以免越界。

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    return obj->a[obj->front];
}
//判断队列是否为空,然后把front下标数据返回即可
int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    if(obj->back==0)
    {
        return obj->a[obj->k];
    }
    else
    {
        return obj->a[obj->back-1];
    }
    //return (obj->back-1 +obj->k+1)%(obj->k+1);
}
//先判断是否为空
//如果back=0,说明,此时1-k下标的空间都已经被使用,下标k就是队尾数据
//否则的话,因为back是有效数据的下一个位置,所以back的前一个就是队尾数据



void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}
//释放,先释放数组空间,再释放队列空间

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值