栈和队列(栈的详解)

栈也是线性表(在逻辑上是顺序存储)的一种,栈只允许其在固定的一端进行插入和删除,栈中的元素遵循后进先出(先进后出)
出入数据可以比喻成羽毛球和电梯
栈顶:可以进行插入和删除数据,入数据和出数据都在栈顶
栈底:不允许插入和删除数据

栈的实现

栈可以用数组或单链表,双向链表实现
双向链表要多维护一个指针
单链表和数组实现栈都比较好,但是数组的缓存利用率比较高

缓存利用率(命中率)是什么?
如果第一个数据在缓存中,缓存命中,直接访问
不在缓存中,叫不命中,要把数据从内存中加载到缓存中,再访问
简单来说是缓存命中的多少

数组在内存中是连续存储的,所以提取数组的缓存利用率比较高
1.命中第一个数据,后面的数据就会连续命中
2.第一个数据不命中,把它加载到缓存中,后面的一系列数据就都会加载到缓存中
比如你们去春游,老师叫来一个大巴车把A同学带来了,司机不只把A同学带来,还把后面的所有同学都带来了

链表
链表是由一个一个指针链接在一起的,拉到缓存中一次也只能来一个数据,效率比较低,缓存命中率不高
还有一个原因就是拉一个数据,可能把后面无关的数据也拉进来,会造成缓存污染,把之前有用的数据给挤出去,链表物理上不是连续存储的

所以我们使用数组来实现栈
在这里插入图片描述

栈的结构

//静态栈不太实现,都是实现的动态栈
//用数组为基础实现动态栈
typedef int STDataType;
typedef struct Stack
{
    STDataType* a;//指向数组的指针
	int top;//标识栈顶
	int capacity;//栈的大小
}Stack;

栈的初始化

//栈的初始化
void StackInit(Stack* pst)
{
	assert(pst);

	pst->a = NULL;
	pst->top = 0;
	//指向栈顶的下一个数据,可以表示元素的多少(size)

	//s->top = -1;
	//指向栈顶数据,可以表示元素的下标
	pst->capacity = 0;
}

top有两种情况

  • top可以指向栈顶,可以这样理解,top是指向数组下标
  • 先top++,再插入数据
    在这里插入图片描述
  • top还可以指向栈顶的下一个数据,top可以理解为size,数据的多少
  • 先插入数据,再top++
    在这里插入图片描述
    易错点:top指向栈顶元素,又top初始化为0,为0是指向栈顶的下一个元素
    应该top初始化为-1

栈的销毁

//栈的销毁
void StackDestroy(Stack* pst)
{
	assert(pst);
    //pst指向栈的结构体为空就找不到数组了
	free(pst->a);
	pst->a = NULL;
	pst->top = pst->capacity = 0;
}

入栈

//入栈
void StackPush(Stack* pst, STDataType x)
{
	assert(pst);

	//扩容
	if (pst->capacity == pst->top)
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		//->的优先级大于*
		STDataType* tmp = (STDataType*)realloc(pst->a,(sizeof(STDataType))
		* newcapacity);
		//为数组realloc一个连续的空间
		if (tmp == NULL)
		{
			perror("StackPush:realloc");
			return;
		}
		//开辟成功一个空间
		pst->capacity = newcapacity;
		//把新空间给它
		pst->a = tmp;
		//新数组给它
	}
	pst->a[pst->top] = x;
	//插入x
	pst->top++;
	//top增加1
}

判满(判断空间是否满了):
1.top指向栈顶的下一个数据(size),top == capacity
2.top指向栈顶(下标),top+1 == capacity

出栈

//出栈
void StackPop(Stack* pst)
{
	assert(pst);
	assert(pst->top > 0);
	//top指向0的时候是没有元素的

	pst->top--;
	//元素少一个
}

在这里插入图片描述

获取栈顶元素

//获取栈顶元素
STDataType STTop(Stack* pst)
{
	assert(pst);
	assert(pst->top > 0);
	//取的最后一个元素是0下标的元素,取0时下标为-1是越界访问的

	return pst->a[pst->top - 1];
	//返回最后一个数据
}

栈的判空

//栈的判空
bool STEmpty(Stack* pst)
{
	assert(pst);

	return pst->top == 0;
	//为真时top为0,返回true,数组中没有元素
	//为假时top为非0,返回false,数组中还有元素
}

获取栈的数据个数

//获取栈的数据个数
int STSize(Stack* pst)
{
	assert(pst);

	return pst->top;
	//top(代表数据个数)
}

test.c(测试)

 #define _CRT_SECURE_NO_WARNINGS

#include"Stack.h"

//int main()
//{
//	Stack sl;
//	StackInit(&sl);
//	StackPush(&sl,1);
//	StackPush(&sl, 2);
//	StackPush(&sl, 3);
//	StackPush(&sl, 4);
//	StackPop(&sl);
//	StackPop(&sl);
//	StackPop(&sl);
//	StackPop(&sl);
//	printf("%d", STTop(&sl));
//	StackDestroy(&sl);
//
//	return 0;
//}

int main()
{
	Stack sl;
	//入栈: 1,2,3,4 出栈: 4,3,2,1 / 1,2,3,4 有多种可能
	// 队列的入栈是1,2,3,4 出栈一定是:4,3,2,1
	//创建一个栈的变量
	StackInit(&sl);

	StackPush(&sl,1);
	//printf("%d", STTop(&sl));

	StackPush(&sl, 2);
	printf("%d ", STTop(&sl));
	StackPop(&sl);

	StackPush(&sl, 3);
	//printf("%d", STTop(&sl));

	StackPush(&sl, 4);
	//printf("%d", STTop(&sl));


	while (!STEmpty(&sl))//判断栈是不是空
	{
	   //有数据返回false,!(取反)真,获取栈顶数据,弹出栈顶数据
	   //没数据返回true,!(取反)假,top == 0,退出循环,空栈
		printf("%d ", STTop(&sl));
		StackPop(&sl);
	}
	//2 4 3 1

	StackDestroy(&sl);

	return 0;
}

总结

栈做题关键是找最近的匹配(东西),比如leetcode中括号的匹配,
{ [ ( ) ] }
最后祝大家题题AC

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值