【数据结构】栈(Stack)的实现 -- 详解

news2024/9/25 11:21:54

一、栈的概念及结构

1、概念

:一种特殊的线性表,其只允许在表尾进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出 LIFO(Last In First Out)的原则。


压栈:栈的插入操作叫做进栈 / 压栈 / 入栈,入数据在栈顶

出栈:栈的删除操作叫做出栈。出数据也在栈顶


2、结构 

 


二、栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为栈只会在一端进行插入和删除操作,用数组效率还是比较高,数组在尾上插入数据的代价比较小。当然,也会存在一些问题,就是每次空间不够,要重新开辟空间,可能会造成一些内存浪费。

1、栈的顺序存储结构

数组的首元素作为栈底,另外一端作为栈顶,同时定义一个变量 top 来记录栈顶元素在数组中的位置。 


2、栈的链表存储结构 

单链表的尾部作为栈底,头部作为栈顶,方便插入和删除(进栈头插,出栈头删),头指针和栈顶指针 top 合二为一。


三、栈的实现

1、创建文件

  • test.c(主函数、测试栈各个接口功能)
  • Stack.c(栈接口函数的实现)
  • Stack.h(栈的类型定义、接口函数声明、引用的头文件)

2、Stack.h 头文件代码

下面是动态增长的栈的结构,在实际中更常用。

#pragma once
#include <stdio.h>
#include <assert.h> // assert
#include <stdlib.h> // realloc
#include <stdbool.h> // bool

// 支持动态增长的栈
typedef int STDataType; // 类型重命名 栈中元素类型先假设为int

typedef struct Stack
{
    STDataType* a; // 指向动态开辟的数组
    int top; // 记录栈顶位置
    int capacity; // 栈的容量大小
}Stack;

// 初始化栈
void StackInit(Stack* ps); 
// 销毁栈
void StackDestroy(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); 

四、Stack.c 中各个接口函数的实现

1、栈的初始化

// 初始化栈
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
    
    //思路一:
	ps->top = 0; // 意味着top指向栈顶数据的下一个

    //思路二:
    //ps->top = -1; // 意味着top指向栈顶数据
    
	ps->capacity = 0;
}

思路二: 

 


2、栈的销毁

// 销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->a)
	{
		free(ps->a);
	}
    ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

3、入栈

// 入栈
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);

	if (ps->top == ps->capacity) // 检查栈空间是否满了
	{
		int newCapacity = (ps->capacity == 0) ? 4 : (ps->capacity) * 2;
		STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity); // 扩容至新容量
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity; // 更新容量
	}
	ps->a[ps->top] = x; // 将新增元素放入栈顶空间
	ps->top++; // 栈顶指针加一
}

4、出栈

// 出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps)); // 栈不能为空
	ps->top--; // 栈顶指针减一
}

5、获取栈顶元素

// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
    assert(!StackEmpty(ps)); // 栈不能为空
	return ps->a[ps->top-1];
}

6、获取栈中有效元素个数

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

7、检测栈是否为空

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{
	assert(ps);

    //写法一:
    /*if (ps->top == 0)
	{
		return true;
	}
	else
	{
		return false;
	}*/

    //写法二:
	return ps->top == 0;
}

五、代码整合

// Stack.c
#include "Stack.h"

// 初始化栈
void StackInit(Stack* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->top = 0; // 意味着top指向栈顶数据的下一个
	ps->capacity = 0;
}

// 销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);
	if (ps->a)
	{
		free(ps->a);
	}
    ps->a = NULL;
	ps->top = 0;
	ps->capacity = 0;
}

// 入栈
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);

	if (ps->top == ps->capacity) // 检查栈空间是否满了
	{
		int newCapacity = (ps->capacity == 0) ? 4 : (ps->capacity) * 2;
		STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity); //扩容至新容量
		if (tmp == NULL)
		{
			printf("realloc fail\n");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity; // 更新容量
	}
	ps->a[ps->top] = x; // 将新增元素放入栈顶空间
	ps->top++; // 栈顶指针加一
}

// 出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(!StackEmpty(ps)); // 栈不能为空
	ps->top--; // 栈顶指针减一
}

// 获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
    assert(!StackEmpty(ps)); // 栈不能为空
	return ps->a[ps->top-1];
}

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

// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{
	assert(ps);
	return ps->top == 0;
}

六、测试栈的功能

栈的实现不能像顺序表一样,去实现一个打印函数来遍历栈并输出,这样做就不符合栈的特点了(只能在栈顶插入删除,后进先出),所以我们这样来实现出栈:获取并打印栈顶元素,再删除栈顶元素,继续获取新的栈顶元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/810039.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Glide预处理preload原始图片到成品resource 预加载RecyclerViewPreloader,Kotlin

Android Glide预处理preload原始图片到成品resource & 预加载RecyclerViewPreloader&#xff0c;Kotlin <uses-permission android:name"android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name"android.permission.READ_MED…

集合的概述

基本的集合有六种&#xff0c;分别是Vector、ArrayList、LinkedList、TreeSet、HashSet、LinkedHashSet 其中Vector、ArrayList、LinkedList实现了List接口&#xff0c;LinkedHashSet实现了HashSet接口&#xff0c;TreeSet、HashSet实现了Set接口 List和Set又实现了Collectio…

Anaconda原理解析及使用

anaconda想必大家都不陌生&#xff0c;属于使用python的重要工具&#xff0c;更是学习机器学习、深度学习的必备工具。在搭建环境过程中&#xff0c;感觉出现的许多问题根源在于对于anaconda的基本原理理解不到位&#xff0c;导致许多无效操作。为此&#xff0c;我重温了一遍an…

TypeScript实战篇 - TS实战:花田APP的架构

目录 TS实现花田APP的聊天Node端 整体架构 项目拆分 项目的特点 模型层 所有系统都是模型的外设 模型层的优势 TS实现花田APP的聊天Node端 整体架构 项目拆分 代号&#xff1a;huatian 5个独立的npm包 huatian/ui 花田的主项目huatian/component 花田组件库huatian/…

❤️创意网页:高考加油倒计时网页文字加多版 - 增加祝福语句和下雪背景效果

✨博主&#xff1a;命运之光 &#x1f338;专栏&#xff1a;Python星辰秘典 &#x1f433;专栏&#xff1a;web开发&#xff08;简单好用又好看&#xff09; ❤️专栏&#xff1a;Java经典程序设计 ☀️博主的其他文章&#xff1a;点击进入博主的主页 前言&#xff1a;欢迎踏入…

Emacs之改造最快的文件搜索工具fd-dired(基于fd命令)(一百二十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

RAID相关知识

简介 RAID &#xff08; Redundant Array of Independent Disks &#xff09;即独立磁盘冗余阵列&#xff0c;通常简称为磁盘阵列。RAID技术将多个单独的物理硬盘以不同的方式组合成一个逻辑磁盘&#xff0c;从而提高硬盘的读写性能和数据安全性。 数据组织形式 分块&#x…

Flowable-任务-手动任务

定义 手动任务是预期在没有任何业务流程执行引擎或任何应用程序的帮助下执行的任务&#xff0c;它用于建 模那些引擎不需要知道的人所做的工作&#xff0c;以及那些不存在已知系统或 UI 界面的人所做的工作&#xff0c;一 般完善流程结构描述&#xff0c;不被引擎执行。例如&a…

cookie登录b站获取cookie登录billbill教程

利用cookie免账号密码登录b站cookie登录哔哩哔哩cookie登录billbill教程 1.获取cookie 以Edge浏览器为例&#xff0c;随便找一个人私聊&#xff0c;按下F12&#xff0c;选到网络(network)&#xff0c;在筛选器里填send_msg&#xff0c;如下图所示。如果没有网络(network)&…

@TableId(type = IdType.ASSIGN_ID)

最近一直在使用mybatis plus ,上篇说没有添加ID 那不得学习一把 本来想不去添加主键&#xff0c;但是暂时还没发现mybatis plus增么 可以不设置主键的情况下修改&#xff0c;想想还是不行&#xff0c;主要我不想去多写代码&#xff08;肯定不是因为懒&#xff09;&#xff0c;…

巨人互动|Google海外户Google SEO常见术语

随着越来越多的人开始建立网站和在线业务&#xff0c;谷歌搜索引擎优化&#xff08;SEO&#xff09;变得越来越重要。要在谷歌上获得更高的排名&#xff0c;您需要掌握许多不同的术语和技术。在本篇文章中&#xff0c;我们将介绍一些常见的谷歌SEO术语&#xff0c;以帮助您了解…

perf 分析MySQL底层函数调用

文章目录 一、安装软件包二、数据采集2.1 perf top2.2 perf record 三、数据加工和解读 一、安装软件包 sudo yum install -y perf git clone https://github.com/brendangregg/FlameGraph二、数据采集 2.1 perf top perf top -g -p pidof mysqld 第一列&#xff1a;符号引…

指针的应用练习(数组与指针的关系)

如果对指针不是那么熟悉&#xff0c;我这里有几篇指针相关入门&#xff0c;不知道能不能帮助到你 http://t.csdn.cn/BbVwT http://t.csdn.cn/eqBng http://t.csdn.cn/hwNXp 看完后&#xff0c;检测一下这两段代码是否能透彻理解 &#xff08;1&#xff09; #include<s…

<MyBatis>前台同一个参数传多个条件查询方式(传数组或者拼接字符串)

方式一&#xff1a;前台传参为数组&#xff0c;后台SQ查询案例&#xff1a; 一般为多选场景&#xff1a;查询&#xff1b; 举例如下&#xff1a; 传值&#xff1a;“status” : [“保存”,“关闭”], 不传值&#xff1a;“status”: [], 传给后台&#xff1a; 控制层&#xff1…

虚拟机中Linux的IP地址配置详解

目录 第一章、虚拟机中Linux的IP地址配置详解1.1&#xff09;什么是IP地址1.2&#xff09;如何查看自己电脑ip地址1.3&#xff09;虚拟机NAT模式中Linux的IP地址设置有什么要求 第二章、使用Linux中的编辑命令进行网卡信息文件的配置 友情提醒 先看文章目录&#xff0c;大致了…

c++静态代码扫描工具clang-tidy详细介绍

clang-tidy 文章目录 clang-tidy1. 什么是clang-tidy2. clang-tidy可以解决什么问题3. 工作原理4. 如何使用clang-tidy4. 总结5. 举例说明&#xff1a; 1. 什么是clang-tidy Clang-Tidy是一个由LLVM项目提供的开源工具&#xff0c;是一个静态分析工具&#xff0c;用于进行静态…

Emacs之point-undo代码步骤记忆前进/回退(一百二十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Android 面试题 虚拟机、进程、线程 七

&#x1f525; 安卓虚拟机 &#x1f525; 虽然Android程序是使用Java语言开发的&#xff0c;当然&#xff0c;现在也可以使用kotlin语言。但是实际上我们开发出来的Android程序并不能运行在JVM上&#xff0c;而是只能运行在一个类似JVM的Android虚拟机上。Android虚拟机有两种&…

【数据结构】队列(Queue)的实现 -- 详解

一、队列的概念及结构 1、概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out)。 入队列&#xff1a;进行插入操作的一端称为队尾。 出队列&#xff1a;进行删除操作的…

作为程序员,你很有必要了解一下IVX

一、IVX是什么 iVX 是一个“零代码”的可视化编程平台&#xff0c;拥有方便的在线集成开发环境&#xff0c;不需要下载开发环境&#xff0c;打开浏览器即可随时随地进行项目编辑。iVX 还拥有“一站式”的云资源&#xff0c;通过这一套一站式服务&#xff0c;iVX 可以实现一站式…