数据结构:线性表(栈的实现)

2023-10-30

1. 栈(Stack)

1.1 栈的概念

  • 栈(Stack)是只允许在一端进行插入或删除操作的线性表.首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作.
  • 进行数据插入和删除操作的一端栈顶,另一端称为栈底.
  • 栈中的元素遵循后进先出LIFO(Last In First Out)的原则

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶.
出栈:栈的删除操作叫做出栈,出数据也在栈顶.

1.2 栈的结构

在这里插入图片描述

栈的元素,遵循后进先出原则


栈采用什么逻辑结构呢?
通常栈可以用链表和数组实现.
当然我们调用的时候不需要知道,使用的是什么方法实现.

链表栈

通过在表前端插入来实现 push ,通过删除表前端元素实现 pop.
可以使用之前实现的单链表来实现栈,单链表实现详见.只用注意,栈只有插入删除操作,需要头删和头插.

虽然操作都是花费常数时间,但是对mallocfree的调用是十分昂贵的.

数组栈

更为流行的是使用数组来实现栈.虽然数组的大小需要提前说明或者临时开辟,但是,在典型的应用程序中,栈元素的实际个数一般不会太大,使用数组是更加高效的方法.


总结:

  • 推荐使用数组结构实现栈
  • 若使用链表实现栈
    用尾做栈顶,尾删尾插,要设计成双向链表
    用头做栈顶,头删头插,要设计成单链表

2. 栈的定义

一般来说,使用动态栈而非静态栈,需要扩容的时候,可以进行适当扩容,增加了程序的适用性.

动态栈实现的数据结构

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);

3. 栈的实现

3.1 初始化栈 (StackInit)

// 初始化栈
void StackInit(Stack* ps)
{
  assert(ps);
  
  ps->a = NULL;     
  ps->top = 0;
  ps->capacity = 0;
}
  1. 和创建顺序表的操作是一致的,通过结构体指针ps修改主函数中的结构体的成员变量,将ps指向的数组指针指向NULL,同时将容量capacity和栈顶元素top置为 0.
  1. 注意,在这里,top所指向的是栈顶元素后一个空间,此时没有元素,自然就指向了数组的第一个空间

3.2 入栈 (StackPush)

//入栈
void StackPush(Stack* ps, STDataType data)
{
  assert(ps);       //确保ps合法

  //如果容量不够则扩容
  if (ps->capacity == ps->top)
  {
    int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;   //定义新的容量
    STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);  //开辟新的空间
    if (tmp == NULL)
    {
      perror("malloc error");
      exit(-1);
    }
    else 
    {
      ps->a = tmp;
      ps->capacity = newCapacity;
    }
  }

  //将数据入栈
  ps->a[ps->top] = data;
  ps->top++;
}
  1. 首先确保ps合法
  2. 因为是动态栈,会出现空间不够的情况,在入栈之前首先确保容量充足,如果容量不够,则进行扩容
  3. 将数据入栈,同时top++

3.3 出栈 (StackPop)

// 出栈
void StackPop(Stack* ps)
{
  assert(ps); //确保ps合法

  assert(!StackEmpty(ps));  //确保栈不为空
  ps->top--;

}
  1. 确保 ps 合法
  2. 确保栈不为空
  3. 直接将top--即可

3.4 检测栈是否为空 (StackEmpty)

// 检测栈是否为空, 如果为空返回非零结果, 如果不为空返回 0
int StackEmpty(Stack* ps)
{
  assert(ps); //确保ps合法
  
  if (ps->top > 0)
  {
    return 0;
  }
  else 
  {
    return 1;
  }
}
  1. 确保ps合法
  2. 如果top大于0,说明栈不为空,反之则为空

3.5 获取栈顶元素 (StackTop)

// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
  assert(ps);   //确保ps合法
  assert(!StackEmpty(ps));  //确保栈不为空

  return ps->a[ps->top - 1];
}
  1. 确保ps合法
  2. 确保栈不为空
  3. 直接返回top - 1位置的元素

3.6 获取栈中有效元素个数 (StackSize)

// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
  assert(ps);   //确保ps合法
  return ps->top;
}
  1. 确保ps合法
  2. 直接将top返回即可

3.7 销毁栈 (StackDestroy)

// 销毁栈
void StackDestroy(Stack* ps)
{
  assert(ps); //确保ps合法

  free(ps->a);
  ps->a = NULL;
  ps->capacity = 0;
  ps->top = 0;
}
  1. 确保ps合法
  2. 将ps指向的数组空间归还给操作系统,同时将topcapacity置为0

3.8 完整代码

  • Stack.h
#pragma once 

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.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 = 0;
  ps->capacity = 0;
}

//入栈
void StackPush(Stack* ps, STDataType data)
{
  assert(ps);       //确保ps合法

  //如果容量不够则扩容
  if (ps->capacity == ps->top)
  {
    int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;   //定义新的容量
    STDataType* tmp = (STDataType*)realloc(ps->a, sizeof(STDataType) * newCapacity);  //开辟新的空间
    if (tmp == NULL)
    {
      perror("malloc error");
      exit(-1);
    }
    else 
    {
      ps->a = tmp;
      ps->capacity = newCapacity;
    }
  }

  //将数据入栈
  ps->a[ps->top] = data;
  ps->top++;
}

// 出栈
void StackPop(Stack* ps)
{
  assert(ps); //确保ps合法

  assert(!StackEmpty(ps));  //确保栈不为空
  ps->top--;

}

// 检测栈是否为空, 如果为空返回非零结果, 如果不为空返回 0
int StackEmpty(Stack* ps)
{
  assert(ps); //确保ps合法
  
  if (ps->top > 0)
  {
    return 0;
  }
  else 
  {
    return 1;
  }
}

// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
  assert(ps);   //确保ps合法
  assert(!StackEmpty(ps));  //确保栈不为空

  return ps->a[ps->top - 1];
}

// 获取栈中有效元素个数
int StackSize(Stack* ps)
{
  assert(ps);   //确保ps合法
  return ps->top;
}

// 销毁栈
void StackDestroy(Stack* ps)
{
  assert(ps); //确保ps合法

  free(ps->a);
  ps->a = NULL;
  ps->capacity = 0;
  ps->top = 0;
}

  • Test.c
#include "Stack.h"

void StackTest1()
{
  Stack st;

  StackInit(&st);
  StackPush(&st, 1);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));

  StackPush(&st, 2);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
 
  StackPush(&st, 3);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPush(&st, 4);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPush(&st, 5);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
}

int main(void)
{
  StackTest1();

  return 0;
}

printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
  printf("%d\n", StackSize(&st));
  printf("%d\n", StackTop(&st));
  
  StackPop(&st);
}

int main(void)
{
  StackTest1();

  return 0;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构:线性表(栈的实现) 的相关文章

  • Shell脚本编写教程【五】——Shell 基本运算符

    Shell脚本编写教程 五 Shell 基本运算符 目录 https blog csdn net shn111 article details 131590488 参考教程 https www runoob com linux linux
  • 练习-Java类和对象之包的定义

    第1关 练习 Java类和对象之包的定义 任务描述 编程要求 测试说明 任务描述 本关任务 定义一个电影类和一个电影测试类 在电影测试类中通过对象完成成员变量和成员方法的使用 编程要求 仔细阅读右侧编辑区内给出的代码框架及注释 在 Begi
  • 【Docker】Docker网络

    1 配置容器网络 1 通过实训平台进入到操作系统界面 在 后输入docker run i t d net none ubuntu bin bash命令 启动一个 bin bash容器 示例代码如图1所示 2 在 后输入docker ps a

随机推荐

  • ant-vue中的a-icon使用方法

    Ant Design 图标库 直接引入的使用方式 你直接点击相应的图标会自动将图标名称复制到你的剪切板上
  • Unity3D游戏开发介绍

    Unity3D游戏开发介绍 Unity3D Unity是实时3D互动内容创作和运营平台 包括游戏开发 美术 建筑 汽车设计 影视在内的所有创作者 借助Unity将创意变成现实 Unity平台提供一整套完善的软件解决方案 可用于创作 运营和变
  • CenOS7 下安装wget命令

    1 安装vsfdp yum y install vsftpd 2 关闭防火墙 systemctl stop firewalld service 3 将本机目录下的wget安装文件上传至虚拟机 scp wget 1 14 18 el7 6 1
  • 案例:用户信息列表展示

    1 需求 用户信息的增删改查操作 2 设计 1 技术选型 Servlet JSP MySQL JDBCTempleat Duird BeanUtilS tomcat 2 数据库设计 create database day17 创建数据库 u
  • uva11292 Dragon of Loowater (水题)

    include
  • 电脑怎么在Bios中开启虚拟化

    1 开机按F1进入BIOS Configuration Secuity 2 然后选择Virtulize 或者Intel Virtual Technolody 设置成Enable 3 F10保存 重启
  • String类为什么是不可变的

    String StringBuilder StringBuffer是经常考的东西 其中 String是不可变的 为什么呢 简单解释如下 String类new了一个对象后 我们看到的该对象只是引用 存放了真正内存的地址 并不是真的内存值 如果
  • 500.键盘行 693.交替位二进制数(java实现)

    键盘行 题目 给定一个单词列表 只返回可以使用在键盘同一行的字母打印出来的单词 键盘如下图所示 示例1 输入 Hello Alaska Dad Peace 输出 Alaska Dad 注意 你可以重复使用键盘上同一字符 你可以假设输入的字符
  • 瑞芯微RK3399交叉编译MPP

    上一篇介绍了如何在ubuntu下搭建瑞芯微RK3399的检查编译环境 现在就要开始交叉编译MPP来进行对视频的硬编硬解 这里RK3399用的aarch64架构芯片 上面跑的linux 如果编译android版 需要其他配置 这里主要对lin
  • 101. Symmetric Tree\104. Maximum Depth of Binary Tree\111. Minimum Depth of Binary Tree

    Symmetric Tree 题目描述 代码实现 Maximum Depth of Binary Tree 题目描述 代码实现 Minimum Depth of Binary Tree 题目描述 代码实现 101 Symmetric Tre
  • hcie数通认证考试科目有哪些

    HCIE数通认证考试科目包括网络架构设计和规划 华为路由交换设备的技术和应用 安全和防护 数据中心技术和架构设计以及其他技术和应用1 网络架构设计和规划是HCIE认证考试的重点内容之一 包括网络结构的分层设计 网络拓扑规划 网络性能和可靠性
  • 器件基础知识——电阻

    一 认识电阻 1 耗能能力 电阻对电流有阻碍作用 它自身消耗能量 会将电能转换为热能 为热功率 为流过电阻的电流 为电阻阻值 2 欧姆定律 为加在电阻两端的电压 在电路中为固定值 3 等效模型 理想的电阻器 由纯电阻组成 不受工作频率影响
  • Docker安装教程

    0 安装Docker Docker 分为 CE 和 EE 两大版本 CE 即社区版 免费 支持周期 7 个月 EE 即企业版 强调安全 付费使用 支持周期 24 个月 Docker CE 分为 stable test 和 nightly 三
  • 一个简单的神经网络实例——两层神经网络手写数字识别

    简介 这里是使用神经网络识别手写数字的教程 这个简单的神经网络教程演示了使用numpy从零开始实现一个简单的神经网络 我们定义前向传播和反向传播函数 在训练过程中不断更新权重 完成对数据的拟合 1 准备数据 我们准备了一个简单的训练集 包含
  • 三种素数筛总结——(朴素筛,埃氏筛,线性筛)

    但行好事 莫问前程 题目背景 题目 leetcode 204 计数质数 给定整数 n 返回 所有小于非负整数 n 的质数的数量 对于这类求解素数个数有关的题目 通常采用质数筛算法 本文不计算时间复杂度 只介绍自己对于思路的理解 质数筛 1
  • Backbone 模型 - Backbone.Model

    BackBone模型源码 2013 06 06 11 12 01 分类 JavaScript Backbone 模型 Backbone Model 首先是 Model 模块的源码 Backbone Models 在框架中是基础的数据对象 常
  • 你是否真的适合搞NDK开发?

    最近很多人说 Android越来越不好找工作了 学习NDK开发会不会好点 今天就聊聊这个问题 是否应该选择学NDK 一 哪些场景下要用到NDK开发 跨平台的库 如FFmpeg skip weex 加固 防逆向 签名校验 图片压缩 音视频解码
  • 【以太坊傻瓜教程】在私链上发布第一个合约

    以太坊傻瓜教程 在私链上发布第一个合约 教程简介 本教程将介绍如何编写合约 编译合约以及如何将合约发布到自己的私链上并调用 开发环境 本教程开发环境 操作系统 Windows10 Ethereum客户端 Windows版Geth 可以从这里
  • M1 Mac创建虚拟环境遇到的问题

    报错信息 PackagesNotFoundError The following packages are not available from current channels python 3 7 Current channels ht
  • 数据结构:线性表(栈的实现)

    文章目录 1 栈 Stack 1 1 栈的概念 1 2 栈的结构 链表栈 数组栈 2 栈的定义 3 栈的实现 3 1 初始化栈 StackInit 3 2 入栈 StackPush 3 3 出栈 StackPop 3 4 检测栈是否为空 S