专栏目录(数据结构与算法解析):https://blog.csdn.net/qq_40344524/article/details/107785323
栈
栈是限定只能在表尾进行插入或删除操作的线性表,在学习栈的相关操作之前我们要先记住几个概念:栈底,栈顶,空栈,入栈(压栈),出栈
概念解释
栈底
线性表的尾部位置叫做栈底,它有一些特殊含义,后续在讲解代码时会讲到
栈顶
线性表的表头位置叫做栈顶,它是出栈和入栈的操作位置
空栈
当定义为栈的线性表中不存在任何元素的时候该线性表叫做空栈
入栈
也叫压栈,是指将元素放到栈中,此时栈顶指针要上移一位
出栈
将栈顶元素从栈中取出,栈顶指针下移一位
栈的具体操作过程见如下演示图
从以上图例演示中我们可以看出栈的几个重要特性,首先栈最主要的特点是后进先出(越晚入栈的元素反而是越早出栈),其次栈也是具有线性特点的。
栈的实现
栈的实现有数组和链表两种方式,接下来我会通过代码对栈的实现和操作进行详细讲解:
数组栈
栈的创建
//数组栈是具有特定约束的数组,因此C语言中通常使用结构体进行表示
typedef int DataType;
typedef struct SeqStack
{
DataType* data;
int top; //栈顶
int capacity; //容量
int bottom; //栈底
}SeqStack;
//初始化栈
void SeqStackInit(SeqStack* stack)
{
//1、申请可存放5个元素的内存空间
stack->data = (DataType*)malloc(sizeof(DataType) * 5);
//2、申请成功则进行初始化
stack->top = 0;
stack->bottom = 0;
stack->capacity = 5;
}
入栈
void SeqStackPush(SeqStack* stack, DataType d)
{
//1、判断栈是否满了,如果满了,就扩容,不满则直接压栈
if(stack->top == stack->capacity)
{
//使用realloc调整容量大小为原先的二倍
stack->data = (DataType*)realloc(stack->data, sizeof(DataType)*(stack->capacity)*2);
//扩容成功之后再调整容量大小
stack->capacity *= 2;
}
//2、入栈
stack->data[stack->top] = d;
//3、移动栈顶位置
stack->top++;
}
出栈
void SeqStackPop(SeqStack* stack)
{
//1、判断是否为空栈,空栈不可进行出栈操作,判断栈空的方法有两种,1、判断栈顶是否在0位置,2、判断栈顶和栈底是否重合
if(stack->top > 0)
//2、出栈,只需将栈顶向下移一位
pss->top--;
}
判断栈是否为空
bool SepStackIsNull(SeqStack* stack)
{
//判断栈空的方法有两种,1、判断栈顶和栈底是否重合,2、判断栈顶是否在0位置
if(stack->top == stack->bottom)
return ture;
return false;
}
取栈顶元素
//取栈顶元素
DataType SeqStackTop(SeqStack* stack)
{
return stack->data[stack->top - 1];
}
销毁栈
//销毁栈
void SeqStackDestory(SeqStack* stack)
{
if (stack->data)
{
free(stack->data);
stack->data = NULL;
stack->top = 0;
stack->capacity = 0;
}
}
链表栈
栈的创建
//链表栈是具有特定约束的链表,通常使用结构体表示结点和栈
typedef int DataType;
//结点定义
typedef struct Node
{
DataType data;
struct Node* next;
}Node;
//链式栈定义
typedef struct LinkStack
{
Node* top; //栈顶指针
int nCount; //个数
}LinkStack;
//初始化链式栈
void LinkStackInit(LinkStack* lstack)
{
lstack->nCount = 0;
lstack->top = NULL;
}
入栈
void LinkStackPush(LinkStack* lstack, DataType d)
{
Node* node = (Node*)malloc(sizeof(Node));
node->data = d;
node->next = NULL;
//入栈
node->next = lstack->top;
lstack->top = node;
lstack->nCount++;
}
出栈
void LinkStackPop(LinkStack* lstack)
{
Node* del = NULL;
//判断栈是否为空
if (lstack->nCount == 0)
{
return;
}
//2、出栈
del = lstack->top;
lstack->top = del->next;
lstack->nCount--;
free(del);
del = NULL;
}
判断栈是否为空
//判断栈是否为空
bool LinkStackEmpty(LinkStack* lstack)
{
if(lstack->nCount == 0)
return true;
return false;
}
取栈顶元素
//获取栈顶元素
DataType LinkStackTop(LinkStack* lstack)
{
return lstack->top->data;
}
销毁栈
//销毁栈
void LinkStackDestory(LinkStack* lstack)
{
Node* del = NULL;
while (lstack->top)
{
del = lstack->_top;
lstack->top = del->next;
free(del);
del = NULL;
pls->nCount--;
}
}
总结
通过上述讲解想来大家也看出来了栈的核心特点就是后进先出,基于这个特点我们会在很多场景用到栈的结构,包括内容的逆序输出,检查成对符号使用规范,数制转换等,通常我们在开发中无需自己定义栈,学习上述代码主要是为了更好了解栈的特点和使用方式。
栈的讨论就到这里,下一节详解另一个特殊的线性数据结构——队列,它与栈有区别也有一定的联系。
以上是我的一些粗浅的见解,有表述不当的地方欢迎指正,谢谢!
表述能力有限,部分内容讲解的不到位,有需要可评论或私信,看到必回…