文章目录
- 前言
- 一、栈的概念及其结构
- 二、栈的实现
- 1.头文件Stack.h(栈的结构及其声明方法)
- 2.源文件Stack.c (实现栈的方法:增删查改)
- 总结
前言
栈(Stack)是一种线性数据结构,具有特殊的操作规则。栈中的数据项按照后进先出(Last In First Out, LIFO)的原则进行插入和删除操作。
栈具有两个主要的操作:
- 入栈(Push):在栈顶插入一个元素。如果栈已满,则无法插入新元素。
- 出栈(Pop):从栈顶删除一个元素,并返回被删除的元素。如果栈为空,则无法进行删除操作。
栈还具有以下几个概念和特性:
- 栈顶(Top):栈中最后插入的元素。
- 栈底(Bottom):栈中第一个插入的元素。
- 栈的大小(Size):栈中元素的数量。
- 栈的空间限制:栈的大小有限,当栈已满时,无法再进行入栈操作。
- 栈的空间利用:由于栈的后进先出规则,栈中的元素可以快速插入和删除,因此栈常常用于临时存储、函数调用等场景。
栈可以通过数组或链表来实现。使用数组实现的栈称为顺序栈(Sequential Stack),使用链表实现的栈称为链式栈(Linked Stack)。
一、栈的概念及其结构
栈: 一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。 进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。 栈中的数据元素遵守后进先出 LIFO ( Last In First Out )的原则。压栈 :栈的插入操作叫做进栈 / 压栈 / 入栈, 入数据在栈顶 。出栈 :栈的删除操作叫做出栈。 出数据也在栈顶 。
二、栈的实现
1.头文件Stack.h(栈的结构及其声明方法)
利用CPU高速缓存,实现栈我们底层使用数组
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
// 初始化和销毁
void STInit(ST* pst);
void STDestroy(ST* pst);
// 入栈 出栈
void STPush(ST* pst, STDataType x);
void STPop(ST* pst);
// 取栈顶数据
STDataType STTop(ST* pst);
// 判空
bool STEmpty(ST* pst);
// 获取数据个数
int STSize(ST* pst);
2.源文件Stack.c (实现栈的方法:增删查改)
2.1 初始化与销毁
#include"Stack.h"
// 初始化和销毁
void STInit(ST* pst)
{
assert(pst);
pst->a = NULL;
// top指向栈顶数据的下一个位置
pst->top = 0;
// top指向栈顶数据
//pst->top = -1;
pst->capacity = 0;
}
void STDestroy(ST* pst)
{
assert(pst);
free(pst->a);
pst->a = NULL;
pst->top = pst->capacity = 0;
}
注意:此时top代表的含义,当top为0时是有数据还是没数据,习惯性把top初始化为0,指向栈顶数据下一个位置
2.2 入栈与出栈
// 入栈 出栈
void STPush(ST* pst, STDataType x)
{
assert(pst);
// 扩容
if (pst->top == pst->capacity)
{
int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
STDataType* tmp = (STDataType*)realloc(pst->a, newcapacity * sizeof(STDataType));
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--;
}
a.此时的扩容与之前的顺序表如出一辙,注意三目操作符的使用
b.因为top指向下一个位置数据,所以先插入,后++,与数组下标类似
c.为什么pop代码只有一行也需要创建一个函数?----- 保持接口一致性,避免为空的一些bug
2.3 取栈顶数据
// 取栈顶数据
STDataType STTop(ST* pst)
{
assert(pst);
assert(pst->top > 0);
return pst->a[pst->top - 1];
}
一定不能为空,返回时注意top指向
2.4 判空
// 判空
bool STEmpty(ST* pst)
{
assert(pst);
return pst->top == 0;
}
当top为0时则为空
2.5 获取数据个数
// 获取数据个数
int STSize(ST* pst)
{
assert(pst);
return pst->top;
}
类比顺序表
总结
栈是一种常见的数据结构,具有先进后出(LIFO)的特点。它的作用主要包括以下几个方面:
1. 内存管理:栈用于管理计算机内存的分配和释放。在程序运行过程中,局部变量、函数调用和返回等操作都会通过栈来管理内存空间。
2. 函数调用:栈用于存储函数调用的相关信息,如函数参数、局部变量和返回地址等。每当一个函数被调用时,相关信息会被压入栈中,函数执行完毕后再从栈中弹出。
3. 表达式计算:栈在表达式计算中起着重要作用。例如,中缀表达式可以通过将其转换为后缀表达式,然后使用栈来计算。栈可以保存操作符和操作数的顺序,便于计算和更新。
4. 后退操作:栈可以用于实现后退操作,例如浏览器的返回功能。当用户点击返回按钮时,浏览器会从栈中弹出最近访问的页面,以实现后退到上一个页面的功能。
5. 括号匹配:栈可以用于检查括号的匹配情况。当遇到左括号时,将其推入栈中;当遇到右括号时,将栈顶的左括号弹出,检查是否匹配。如果最终栈为空,说明括号匹配成功。
总的来说,栈在计算机科学和软件开发中有着广泛的应用,是一种非常重要的数据结构。