本篇博客主要介绍栈的概念与结构等内容。

1.栈
1.1 概念与结构
栈:一种特殊的线性表 ,其只允许在固定的一端进行插入和删除元素 操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中的数据元素遵守后进先出LIFO(Last in First out)的原则。
**压栈:**栈的插入操作叫作进栈/压栈/入栈,入数据在栈顶。
出栈: 栈的删除操作叫作出栈。出数据也在栈顶。

栈底层结构选型
栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较少。
1.2 栈的实现
Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top; //指向栈顶的位置
int capacity; //容量
}ST;
void STInit(ST* ps);
void STDestroy(ST* ps);
//入栈---栈顶
void StackPush(ST* ps, STDataType x);
//出栈---栈顶
void StackPop(ST* ps);
//取栈顶数据
STDataType StackTop(ST* ps);
bool StackEmpty(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
1.3 定义栈的结构
//定义栈的结构
typedef int STDataType;
typedef struct Stack
{
STDataType* arr;
int top; //指向栈顶的位置
int capacity; //容量
}ST;
代码逻辑:
首先,通过
typedef
定义了一个整数类型STDataType
,这里实际上是将int
类型取了一个别名STDataType
。然后,定义了一个结构体
Stack
,这个结构体包含了三个成员:
STDataType* arr
:一个指向整数类型的指针,用于存储栈的元素。int top
:用于指向栈顶的位置。int capacity
:表示栈的容量。
1.4 栈的初始化
void STInit(ST* ps)
{
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
代码逻辑:
函数的参数是一个指向
ST
结构体的指针ps
。在函数内部,将结构体中的成员进行了如下初始化:
ps->arr
被初始化为NULL
,表示栈的元素数组为空。ps->top
和ps->capacity
都被初始化为0
,分别表示栈顶位置和栈的容量为0
。这样,通过调用这个函数,可以将一个栈结构体初始化为一个空栈的状态。
1.5 栈的销毁
void STDestroy(ST* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
代码逻辑:
函数的参数是一个指向
ST
结构体的指针ps
。在函数内部,进行了以下操作:首先,检查
ps->arr
是否不为NULL
。如果不为NULL
,则使用free
函数释放ps->arr
所指向的内存空间,以避免内存泄漏。然后,将
ps->arr
再次设置为NULL
,并将ps->top
和ps->capacity
都设置为0
,将栈结构体的相关成员恢复到初始状态。这样,通过调用这个函数,可以释放栈所占用的内存资源,并将栈结构体的成员进行清理和重置。
1.6 入栈
//入栈----栈顶
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
//空间不够---增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
//空间足够
ps->arr[ps->top++] = x;
}
代码逻辑:
用于实现向栈中入栈的操作。
函数的参数包括一个指向栈结构体的指针
ps
和一个要入栈的元素x
。在函数内部,首先使用
assert
函数检查ps
是否有效 。然后,通过判断栈顶位置ps->top
是否等于栈的容量ps->capacity
来确定是否需要进行扩容操作。如果需要扩容,会根据当前容量的情况计算新的容量(如果当前容量为
0
,则新容量为4
;否则新容量为当前容量的2
倍),然后使用realloc
函数 对栈的元素数组进行扩容。如果扩容失败(tmp
为NULL
),则会输出错误信息并退出程序。如果空间足够,直接将元素
x
放入栈顶位置(ps->arr[ps->top] = x
),然后将栈顶位置加1
(ps->top++
)。总的来说,这个函数实现了在栈空间不足时进行自动扩容,并将元素成功入栈的功能。
1.7 判空和出栈
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈----栈顶
void StackPop(ST* ps)
{
assert(!StackEmpty(ps));
--ps->top;
}
代码逻辑:
bool StackEmpty(ST* ps)
: 这个函数用于判断栈是否为空。它首先使用assert
函数检查ps
是否有效,然后通过判断栈顶位置ps->top
是否为0
来确定栈是否为空。如果ps->top
为0
,则表示栈为空,函数返回true
;否则,函数返回false
。void StackPop(ST* ps)
: 这个函数用于实现出栈操作。它首先使用assert
函数和StackEmpty
函数来检查栈是否为空,如果栈为空则会产生断言错误。如果栈不为空,那么将栈顶位置ps->top
减1
,实现出栈操作。
1.8 取栈顶数据
//取栈顶数据
STDataType StackTop(ST* ps)
{
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
1.9 获取栈中数据有效个数
//获取栈中有效元素个数
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
代码逻辑:
函数的参数是一个指向栈结构体的指针
ps
。在函数内部,首先使用assert
函数检查ps
是否有效。然后,直接返回栈顶位置ps->top
的值,将其作为栈中有效元素的个数。因为栈顶位置的值就表示了栈中当前元素的数量。
2.源代码(Stack.c)
#define _CRT_SECURE_NO_WARNINGS
#include"Stack.h"
void STInit(ST* ps)
{
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
void STDestroy(ST* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->top = ps->capacity = 0;
}
//入栈----栈顶
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
//空间不够---增容
int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail!");
exit(1);
}
ps->arr = tmp;
ps->capacity = newCapacity;
}
//空间足够
ps->arr[ps->top++] = x;
}
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
//出栈----栈顶
void StackPop(ST* ps)
{
assert(!StackEmpty(ps));
--ps->top;
}
//取栈顶数据
STDataType StackTop(ST* ps)
{
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
//获取栈中有效元素个数
int STSize(ST* ps)
{
assert(ps);
return ps->top;
}
3.小结
以上便是本篇博客的所有内容,如果诸君学到知识的话,还请诸君点点赞,支持博主。
