【数据结构与算法】使用数组实现栈:原理、步骤与应用

简介: 【数据结构与算法】使用数组实现栈:原理、步骤与应用

一、引言

🎄栈(Stack)是什么?

  • 栈是一种后进先出(LIFO, Last In First Out)的数据结构。
  • 栈是一种只能在一端进行插入和删除操作的线性表。
  • 允许进行插入和删除操作的一端称为栈顶(top),另一端称为栈底(bottom)。
  • 栈中没有元素时,称为空栈。
  • 栈的基本操作包括:push(入栈)、pop(出栈)、peek(查看栈顶元素)和isEmpty(判断栈是否为空)等。

🎄为什么使用数组实现栈?

  • 数组是一种线性数据结构,能够连续存储数据,且通过索引可以方便地访问任意位置的元素。
  • 因为栈只在栈顶增删,所以基于数组实现,既避免了插入需要移动数据的劣势,又保持了数组访问数据的优势,可以实现高效的栈操作。

二、定义栈结构

🎄栈的结构

  • 指向数组的指针(动态开辟的空间)
  • 标记栈顶位置的变量 top
  • 标记栈的大小的变量 capacity
// 支持动态增长的栈
typedef int STDataType;//对数据类型重命名,方便后期修改类型
typedef struct Stack
{
  STDataType* a;
  int top;    // 栈顶
  int capacity;  // 容量 
}Stack;//定义结构同时重命名

🎄栈顶位置的指向

需要注意的是:top的指向应该始终保持一致性

1.如果top指向栈顶元素,初始不能为0,应该指向-1

2.如果top初始为0,其应该指向栈顶元素的下一个元素

对应的判定栈满和栈空有所不同

三、实现栈的基本操作

🍃初始化

  • 对形参判空
  • 数组指针初始指向空
  • top和capacity初始化为0(这里top指向的是栈顶元素的下一个位置)
// 初始化栈 
void StackInit(Stack* ps)
{
  assert(ps);
  ps->a = NULL;
  ps->top = ps->capacity = 0;
}

🍃销毁

  • 对形参判空
  • 释放数组空间
  • 数组指针指向空
  • top和capacity改为0
// 销毁栈 
void StackDestroy(Stack* ps)
{
  assert(ps);
  free(ps->a);
  ps->a = NULL;
  ps->top = ps->capacity = 0;
}

🍃入栈

判空

判断是否需要扩容(top和capacity相等)

扩容步骤:   空间二倍增长 ,更新数组指针和容量

数据插入到top位置,top位置++

// 入栈 
void StackPush(Stack* ps, STDataType data)
{
  assert(ps);
  //判断是否需要扩容
  if (ps->top == ps->capacity)
  {
    int newcapa = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
    STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapa);
    if (tmp == NULL)
    {
      perror("realloc\n");
      exit(1);
    }
    ps->a = tmp;
    ps->capacity = newcapa;
  }
  //确定空间足够之后再插入数据
  ps->a[ps->top] = data;
  ps->top++;
}

🍃出栈

  • 对形参判空
  • 对栈判空
  • top--

(该方法对于栈只存在一个元素的情况也可以正确处理)

// 出栈 
void StackPop(Stack* ps)
{
  assert(ps);
  assert(ps->top);
 
  ps->top--;
}

注意:

即使函数只有一两条语句也还是建议封装成函数,这样可以提高程序的可维护性和可读性

🍃查看栈顶元素

  • 对形参判空
  • 对栈判空
  • 返回top前一个位置的元素
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
  assert(ps);
  assert(ps->top);
  return ps->a[ps->top-1];
}

🍃对栈判空

  • 对形参判空
  • 返回top==0的结果(因为这里top指向的是栈顶元素的下一个元素,所以栈空时top==0)
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
  assert(ps);
 
  return ps->top == 0;
}

🍃获取有效数据个数

  • 对形参判空
  • 返回top  (top对应的下标是栈顶的下一个元素,top就是元素的个数)
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
  assert(ps);
 
  return ps->top;
}

四、使用数组实现栈的C语言代码

stack.h 栈的头文件

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
 
// 支持动态增长的栈
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 
int StackEmpty(Stack* ps);
// 销毁栈 
void StackDestroy(Stack* ps);

stack.c 栈的实现源文件

#include"stack.h"
 
// 初始化栈 
void StackInit(Stack* ps)
{
  assert(ps);
  ps->a = NULL;
  ps->top = ps->capacity = 0;
}
 
// 入栈 
void StackPush(Stack* ps, STDataType data)
{
  assert(ps);
  //判断是否需要扩容
  if (ps->top == ps->capacity)
  {
    int newcapa = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
    STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newcapa);
    if (tmp == NULL)
    {
      perror("realloc\n");
      exit(1);
    }
    ps->a = tmp;
    ps->capacity = newcapa;
  }
  //确定空间足够之后再插入数据
  ps->a[ps->top] = data;
  ps->top++;
}
 
// 出栈 
void StackPop(Stack* ps)
{
  assert(ps);
  assert(ps->top);
 
  ps->top--;
}
 
// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{
  assert(ps);
  assert(ps->top);
  return ps->a[ps->top-1];
}
 
// 获取栈中有效元素个数 
int StackSize(Stack* ps)
{
  assert(ps);
 
  return ps->top;
}
 
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
int StackEmpty(Stack* ps)
{
  assert(ps);
 
  return ps->top == 0;
}
 
// 销毁栈 
void StackDestroy(Stack* ps)
{
  assert(ps);
  free(ps->a);
  ps->a = NULL;
  ps->top = ps->capacity = 0;
}

test.c  主函数测试文件

#include"stack.h"
 
void test1()
{
  Stack st ;
  StackInit(&st);
  if (StackEmpty(&st))
  {
    printf("栈空\n");
  }
  else
  {
    printf("栈非空\n");
  }
  StackPush(&st, 1);
  StackPush(&st, 2);
  StackPush(&st, 3);
  StackPush(&st, 4);
  if (StackEmpty(&st))
  {
    printf("栈空\n");
  }
  else
  {
    printf("栈非空\n");
  }
  printf("栈中元素个数:%d\n", StackSize(&st));
 
  printf("%d\n", StackTop(&st));
  StackPop(&st);
  printf("%d\n", StackTop(&st));
  StackPop(&st);
  printf("%d\n", StackTop(&st));
  StackPop(&st);
  printf("%d\n", StackTop(&st));
  StackPop(&st);
  if (StackEmpty(&st))
  {
    printf("栈空\n");
  }
  else
  {
    printf("栈非空\n");
  }
 
  StackDestroy(&st);
 
}
 
int main()
{
  test1();
 
  return 0;
}

测试结果

五、栈的应用

  1. 函数调用栈:在程序执行过程中,函数调用是通过栈来实现的。每个函数调用时,其返回地址、局部变量和参数等信息都会被压入栈中,当函数返回时,这些信息会被弹出栈。
  2. 表达式求值:在编译器中,表达式求值通常使用栈来实现。例如,在解析算术表达式时,可以使用两个栈:一个用于存储操作数,另一个用于存储操作符。
  3. 浏览器历史记录:浏览器的“前进”和“后退”功能通常使用栈来实现。用户浏览的网页会被压入栈中,当用户点击“后退”按钮时,会从栈中弹出并显示上一个网页。
  4. 撤销操作:在许多图形编辑器和文本编辑器中,撤销操作通常使用栈来实现。每次编辑操作(如剪切、复制、粘贴等)都会被压入一个撤销栈中,当用户点击“撤销”按钮时,会从栈中弹出并执行相反的操作以撤销上一次编辑。

六、总结

  1. 使用数组实现栈是一种简单且高效的方法,能够充分利用数组的特性来实现栈的基本操作。
  2. 在实际应用中,栈具有广泛的应用场景,如函数调用栈、浏览器的前进后退功能以及表达式求值等。 
相关文章
|
10月前
|
存储 监控 JavaScript
基于布隆过滤器的 Node.js 算法在局域网电脑桌面监控设备快速校验中的应用研究
本文探讨了布隆过滤器在局域网电脑桌面监控中的应用,分析其高效空间利用率、快速查询性能及动态扩容优势,并设计了基于MAC地址的校验模型,提供Node.js实现代码,适用于设备准入控制与重复数据过滤场景。
343 0
|
9月前
|
运维 监控 JavaScript
基于 Node.js 图结构的局域网设备拓扑分析算法在局域网内监控软件中的应用研究
本文探讨图结构在局域网监控系统中的应用,通过Node.js实现设备拓扑建模、路径分析与故障定位,提升网络可视化、可追溯性与运维效率,结合模拟实验验证其高效性与准确性。
493 3
|
9月前
|
机器学习/深度学习 资源调度 算法
遗传算法模型深度解析与实战应用
摘要 遗传算法(GA)作为一种受生物进化启发的优化算法,在复杂问题求解中展现出独特优势。本文系统介绍了GA的核心理论、实现细节和应用经验。算法通过模拟自然选择机制,利用选择、交叉、变异三大操作在解空间中进行全局搜索。与梯度下降等传统方法相比,GA不依赖目标函数的连续性或可微性,特别适合处理离散优化、多目标优化等复杂问题。文中详细阐述了染色体编码、适应度函数设计、遗传操作实现等关键技术,并提供了Python代码实现示例。实践表明,GA的成功应用关键在于平衡探索与开发,通过精心调参维持种群多样性同时确保收敛效率
|
9月前
|
机器学习/深度学习 边缘计算 人工智能
粒子群算法模型深度解析与实战应用
蒋星熠Jaxonic是一位深耕智能优化算法领域多年的技术探索者,专注于粒子群优化(PSO)算法的研究与应用。他深入剖析了PSO的数学模型、核心公式及实现方法,并通过大量实践验证了其在神经网络优化、工程设计等复杂问题上的卓越性能。本文全面展示了PSO的理论基础、改进策略与前沿发展方向,为读者提供了一份详尽的技术指南。
粒子群算法模型深度解析与实战应用
|
9月前
|
机器学习/深度学习 算法 安全
小场景大市场:猫狗识别算法在宠物智能设备中的应用
将猫狗识别算法应用于宠物智能设备,是AIoT领域的重要垂直场景。本文从核心技术、应用场景、挑战与趋势四个方面,全面解析这一融合算法、硬件与用户体验的系统工程。
767 0
|
11月前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习模型、算法与应用的全方位解析
深度学习,作为人工智能(AI)的一个重要分支,已经在多个领域产生了革命性的影响。从图像识别到自然语言处理,从语音识别到自动驾驶,深度学习无处不在。本篇博客将深入探讨深度学习的模型、算法及其在各个领域的应用。
2005 3
|
11月前
|
机器学习/深度学习 人工智能 算法
AI-Compass 强化学习模块:理论到实战完整RL技术生态,涵盖10+主流框架、多智能体算法、游戏AI与金融量化应用
AI-Compass 强化学习模块:理论到实战完整RL技术生态,涵盖10+主流框架、多智能体算法、游戏AI与金融量化应用
|
11月前
|
存储 监控 安全
企业上网监控系统中红黑树数据结构的 Python 算法实现与应用研究
企业上网监控系统需高效处理海量数据,传统数据结构存在性能瓶颈。红黑树通过自平衡机制,确保查找、插入、删除操作的时间复杂度稳定在 O(log n),适用于网络记录存储、设备信息维护及安全事件排序等场景。本文分析红黑树的理论基础、应用场景及 Python 实现,并探讨其在企业监控系统中的实践价值,提升系统性能与稳定性。
711 1
|
10月前
|
算法 数据可视化
matlab版本粒子群算法(PSO)在路径规划中的应用
matlab版本粒子群算法(PSO)在路径规划中的应用
|
11月前
|
存储 监控 算法
公司员工泄密防护体系中跳表数据结构及其 Go 语言算法的应用研究
在数字化办公中,企业面临员工泄密风险。本文探讨使用跳表(Skip List)数据结构优化泄密防护系统,提升敏感数据监测效率。跳表以其高效的动态数据处理能力,为企业信息安全管理提供了可靠技术支持。
228 0

热门文章

最新文章