数据结构栈的理解

一、简单了解栈即栈的概念及结构

:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端
称为栈顶,另一端称为栈底

在这里插入图片描述
在C语言中,栈(Stack)是一种重要的数据结构,它具有后进先出(LIFO,Last In First Out)的特性。栈的基本操作包括压栈(Push,将元素添加到栈顶)、弹栈(Pop,从栈顶移除元素)以及查看栈顶元素(Peek)。

栈的理解
后进先出(LIFO):这是栈最重要的特性。例如,你可以想象一个盘子堆栈,当你添加一个盘子时,它会被放在顶部;当你取出一个盘子时,你也只能从顶部取。
栈顶(Top):栈顶是栈中进行元素插入和删除操作的一端。
栈底(Bottom):栈的起始端,通常栈底是固定的,不允许进行插入和删除操作。
空栈(Empty Stack):没有任何元素的栈。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。

二、栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小。
在这里插入图片描述

1、下面是定长的静态栈的结构,

 
typedef int STDataType;
#define N 10
typedef struct Stack
{
 STDataType _a[N];
 int _top; // 栈顶
}Stack;

实际中一般不实用,所以我们主要实现下面的支持动态增长的栈

2、头文件的包含

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

3、动态增长栈和函数的定义

// 支持动态增长的栈
typedef int STDataType;
typedef struct Stack
{
 STDataType* _a;
 int _top; // 栈顶
 int _capacity; // 容量
}Stack;
// 初始化栈
void StackInit(Stack* ps); 
// 入栈
void StackPush(Stack* ps, STDataType data); 
// 出栈
void StackPop(Stack* ps); 
// 获取栈顶元素
STDataType StackTop(Stack* ps); 
// 获取栈中有效元素个数
int StackSize(Stack* ps); 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps); 
// 销毁栈
void StackDestroy(Stack* ps);

栈的结构体中包含一个指针域,两个数据域,
typedef: typedef是C语言中的一个关键字,用于为已存在的类型定义一个新的名称。在这个例子中,它用于为struct Stack这个结构体类型定义了一个别名Stack。这样,在后续的代码中,你可以直接使用Stack这个名称来声明这个类型的变量,而不需要每次都写struct Stack。
struct Stack: 这是定义的结构体的名称。结构体是一种复合数据类型,它允许你将多个不同类型的变量组合成一个单一的类型。
*STDataType _a;**:
STDataType:是用于表示栈中元素的数据类型。它可能是一个整数(int)、浮点数(float)、字符(char)或其他自定义类型。为了完整性和可读性,通常会在代码的其他部分或头文件中定义这个类型。在此代码中表示的是int整形。
_a:这是一个指向STDataType类型的指针,用于存储栈中的元素。在C语言中,栈通常使用数组或动态分配的数组(通过指针)来实现。在这里,_a就是这个动态分配的数组。
int _top; // 栈顶:
_top是一个整数变量,用于存储栈顶的位置。在栈中,通常将数组的最后一个元素作为栈底(下标为0或-1,取决于实现),而栈顶的位置则是动态变化的。_top的值通常表示下一个要入栈的元素应该放在哪个位置,或者表示当前栈顶元素的位置(取决于你的具体实现)。
int _capacity; // 容量:
_capacity是一个整数变量,用于存储栈的最大容量,即_a指向的数组的大小。这个值在栈的初始化时确定,并在栈的整个生命周期中保持不变(除非重新分配内存)。
综上所述,这个Stack结构体定义了一个简单的栈数据结构,它使用指针_a来动态存储栈中的元素,并使用_top和_capacity来管理栈的状态。然而,这个结构体本身并不包含栈的操作(如push、pop等),这些操作通常会在结构体的外部以函数的形式实现。

4、栈的函数实现即"Stack.c"的主要内容

//初始化
void STInit(ST* ps)
{
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

//销毁
void StackDestroy(ST* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->top = 0;
}

//插入
void StackPush(ST* ps, STDataType data)
{
	assert(ps);
	/*assert(ps->a > 0);*/
	if (ps->capacity == ps->top)
	{
		
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
	    STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity* sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc  fail ");
			return -1;
		}
		ps->a = tmp;
		ps->capacity = newcapacity;
	}
	ps->a[ps->top] = data;
	ps->top++;
}
//删除
void StackPop(ST* ps)
{
	assert(ps);
	assert(ps->top > 0);
	ps->top--;
}
//取出
STDataType StackTop(ST* ps)
{
	  assert(ps);
      assert(ps->a > 0);
	   return ps->a[ps->top-1];
}
//记录
int StackSize(ST* ps)
{
	assert(ps);
	return ps->top;
}
//判断空
bool StackEmpty(ST* ps)
{
	assert(ps);
	return ps->top == 0;
 }

代码的解释

1、初始化和销毁
二者比较简单,只需简单了解前面所学习的内容便可理解
销毁的时候先free即释放掉ps->a,再让其置为空;
2 、栈的插入
首先暴力判断,断言一下传进来的ps不能为空,如果为空程序直接停止报错。然后栈不为空,进入下面,
先判断栈的大小和栈顶的元素下标是否相等,要是相等,就说明栈已经不能再继续插入元素了,需要动态开辟空间,
即realloc申请空间,在这里我运用了一个三目操作符,在C语言中,这行代码表示了一个三元条件运算符(也称为条件运算符)的使用,用于根据一个条件来计算两个表达式中的一个,并将结果存储在变量中。
可以分解为以下逻辑:

1.检查ps->capacity的值是否等于0。
2.如果ps->capacity等于0,则newcapacity被赋值为4。
3.如果ps->capacity不等于0,则newcapacity被赋值为ps->capacity的两倍(即ps->capacity * 2)。
这里,ps很可能是一个指向某个结构体(比如前面定义的Stack结构体)的指针,而该结构体中有一个名为capacity的成员变量。

这个三元条件运算符常用于实现栈的动态扩容功能。当栈满(即ps->capacity等于栈中当前元素的数量)并需要添加新元素时,你可能想要增加栈的容量。这行代码提供了一种策略:如果栈的当前容量是0(可能意味着栈是刚刚创建的),则将新容量设置为4;否则,将新容量设置为当前容量的两倍。这样做可以平衡内存使用和扩容成本。
接着判断开辟的空间是否为空,进行检查,若为空则直接退出程序。
然后进行数据的插入
3、栈的删除
直接–top删除栈顶的数据
4、栈的取出(重点理解ps->top-1)
在大多数栈的实现中,栈顶索引top通常指向栈中最后一个有效元素的位置。这意味着,如果栈中有元素,top的值是大于0的,且top等于栈中元素的数量。因此,要访问栈顶元素,我们需要使用ps->top - 1作为索引,因为数组索引是从0开始的。

例如,如果栈中有3个元素,那么ps->top的值将是3,但栈顶元素的实际索引是2(因为索引是从0开始的)。所以,要获取栈顶元素,我们需要使用ps->a[ps->top - 1]。

总结一下,ps->top-1是因为栈顶索引top指向栈中最后一个有效元素的下一个位置,而数组索引是从0开始的,所以要访问栈顶元素需要使用top-1作为索引。
5、栈的记录
记录栈里面的数据个数。
6、判断栈的状态(空)
判断top是否为0,当top==0是栈就是为空。

5、主函数实现即"Test.c"的主要内容

#include "Stack.h"
int main()
{
	ST* s;
	STInit(&s);

	STpush(&s, 1);
	STpush(&s, 2);
	printf("%d", STTop(&s));
	STPop(&s);
	STpush(&s, 3);
	STpush(&s, 4);
	while (!STEmpty(&s))
	{
		printf("%d",STTop(&s));
		STPop(&s);
	}
	printf("\n");
	  STDestory(&s);

}

对栈进行一个检测,检查栈的增加、删除、取出等是否有误,对此栈就已经完成了!

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值