C语言数据结构之栈

news2025/1/15 12:26:09

目录

    • 1.栈的概念及结构
    • 2.栈的实现
    • 3.栈的代码实现
    • 4.相关例题

在这里插入图片描述
•͈ᴗ•͈ 个人主页:御翮
•͈ᴗ•͈ 个人专栏:C语言数据结构
•͈ᴗ•͈ 欢迎大家关注和订阅!!!
在这里插入图片描述

1.栈的概念及结构

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

讲到栈,就要提到它的两种基本操作:压栈(入栈)和出栈。

入栈和出栈遵循一种规则:后进先出(进入栈晚的数据先出栈,就像是先进去的数据被后进入的数据压住了,只能先把上面的数据拿走才能拿到下面的数据)

在这里插入图片描述

连续入栈和连续出栈:

在这里插入图片描述

2.栈的实现

对于栈的实现,我们有两种结构可以选择:顺序表和链表。考虑到先进后出的规则,链表尾插和尾删的成本比顺序表高,不太适合,顺序表尾插和尾删只需要改变加减的size的大小就可以做到,所以我们采用顺序表来实现栈。

关于栈,我们要实现以下几个接口:

在这里插入图片描述

3.栈的代码实现

test.c

#include "Stack.h"

void menu()
{
	printf("********************************************\n");
	printf("***************    请选择    ***************\n");
	printf("******   1.PushStack    2.PopStack   *******\n");
	printf("******   3.PrintStack   4.TopStack   *******\n");
	printf("******   5.SizeStack    6.CheckEmpty *******\n"); 
	printf("******             0.Exit            *******\n");
	printf("********************************************\n");
}

int main()
{
	int input = 0;
	int value = 0;
	Stack s;

	Init_Stack(&s);
	do
	{
		menu();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入你要入栈的值>:");
			scanf("%d", &value);
			Push_Stack(&s, value);
			break;
		case 2:
			Pop_Stack(&s);
			break;
		case 3:
			Print_Stack(&s);
			break;
		case 4:
			if (Empty_Stack(&s) != 0)
			{
				printf("栈为空\n");
				break;
			}
			printf("栈顶的元素是%d\n", Top_Stack(&s));
			break;
		case 5:
			printf("栈中有效元素的个数为%d\n", Size_Stack(&s));
			break;
		case 6:
			if (Empty_Stack(&s) != 0)
				printf("栈为空\n");
			else
				printf("栈不为空\n");
			break;
		case 0:
			Destroy_Stack(&s);
			printf("销毁栈成功\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

Stack.c

#include "Stack.h"


//初始化栈
void Init_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	STDataType* tmp = (STDataType*)malloc(3*sizeof(STDataType));

	if (tmp == NULL) //申请空间可能失败,失败会返回NULL,不能解引用,要终止程序
	{
		perror("Init_Stack\n");
		exit(1);
	}

	ptr->stack = tmp; //申请空间成功就正常初始化
	ptr->capacity = 3;
	ptr->size = 0;
}



//销毁栈
void Destroy_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	free(ptr->stack);
	ptr->stack = NULL;
}



//检查栈是否满了,满了则扩容
void Check_Capacity(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	if (ptr->size == ptr->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ptr->stack, 2 * ptr->capacity * sizeof(STDataType));

		if (tmp == NULL) //申请空间可能失败,失败会返回NULL,不能解引用,要终止程序
		{
			perror("Check_Capacity\n");
			exit(1);
		}

		ptr->stack = tmp;   //要把申请到的空间赋给原指针,不然后面操作不了这块空间
		ptr->capacity *= 2; //扩容之后要修改capacity的值,不然检查栈的大小时会一直扩容
	}
}



//打印栈里面的数据
void Print_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	for (int i = 0; i < ptr->size; i++)
	{
		printf("%d ", ptr->stack[i]);
	}

	printf("\n");
}



//数据入栈
void Push_Stack(Stack* ptr,STDataType val)
{
	assert(ptr); // ptr要解引用,不能为空指针

	Check_Capacity(ptr); //入栈时要检查空间是否足够

	ptr->stack[ptr->size] = val;
	ptr->size++;
}



//数据出栈
void Pop_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	if (ptr->size == 0)
	{
		printf("栈为空\n");
		return;
	}

	ptr->size--; //出栈就相当于顺序表可以读取的元素少一个,size - 1 就可以了
}



//获取栈顶数据
STDataType Top_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	return ptr->stack[ptr->size-1];
}



//获取栈储存的元素个数
int Size_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	return ptr->size;
}



//判断栈是否为空
int Empty_Stack(Stack* ptr)
{
	assert(ptr); // ptr要解引用,不能为空指针

	if (ptr->size == 0)
		return 1;
	else
		return 0;
}

Stack.h

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>

typedef int STDataType;  // 栈储存的数据的类型

typedef struct Stack
{
	STDataType* stack;  //动态开辟的空间,在堆上
	int size;			//储存元素的个数
	int capacity;		//栈的大小(容量),可变化
}Stack;


//栈的初始化
void Init_Stack(Stack* ptr);


//打印栈里面的数据
void Print_Stack(Stack* ptr);


//数据入栈
void Push_Stack(Stack* ptr, STDataType val);


//数据出栈
void Pop_Stack(Stack* ptr);


//获取栈顶数据
STDataType Top_Stack(Stack* ptr);


//获取栈储存元素的个数
int Size_Stack(Stack* ptr);


//判断栈是否为空
int Empty_Stack(Stack* ptr);


//销毁栈
void Destroy_Stack(Stack* ptr);

4.相关例题

题目描述:

在这里插入图片描述

参考解析:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int STDataType;

typedef struct Stack
{
	STDataType* stack;
	int size;
	int capacity;
}Stack;

void Init_Stack(Stack* ptr)
{
	assert(ptr);

	STDataType* tmp = (STDataType*)malloc(3 * sizeof(STDataType));
	if (tmp == NULL)
	{
		perror("Init_Stack\n");
		exit(1);
	}

	ptr->stack = tmp;
	ptr->capacity = 3;
	ptr->size = 0;
}

void Destroy_Stack(Stack* ptr)
{
	assert(ptr);

	free(ptr->stack);
	ptr->stack = NULL;
}

void Check_Capacity(Stack* ptr)
{
	assert(ptr);

	if (ptr->size == ptr->capacity)
	{
		STDataType* tmp = (STDataType*)realloc(ptr->stack, 2 * ptr->capacity * sizeof(STDataType));

		if (tmp == NULL)
		{
			perror("Check_Capacity\n");
			exit(1);
		}

		ptr->stack = tmp;
		ptr->capacity *= 2;
	}
}

void Print_Stack(Stack* ptr)
{
	assert(ptr);

	for (int i = 0; i < ptr->size; i++)
	{
		printf("%d ", ptr->stack[i]);
	}

	printf("\n");
}

void Push_Stack(Stack* ptr, STDataType val)
{
	assert(ptr);

	Check_Capacity(ptr);

	ptr->stack[ptr->size] = val;
	ptr->size++;
}

void Pop_Stack(Stack* ptr)
{
	assert(ptr);

	if (ptr->size == 0)
	{
		return;
	}

	ptr->size--;
}

STDataType Top_Stack(Stack* ptr)
{
	assert(ptr);

	return ptr->stack[ptr->size - 1];
}

int Size_Stack(Stack* ptr)
{
	assert(ptr);

	return ptr->size;
}

int Empty_Stack(Stack* ptr)
{
	assert(ptr);

	if (ptr->size == 0)
		return 1;
	else
		return 0;
}


//解题思路:
//该题比较简单,是对栈特性很好的应用,具体操作如下:
//循环遍历String中的字符,逐个取到每个括号,如果该括号是:
//1. 左括号,直接入栈
//2. 右括号,与栈顶的左括号进行匹配,如果不匹配直接返回false
//否则继续循环
//循环结束后,如果栈空则匹配,否则左括号比右括号多肯定不匹配

bool isValid(char* s) 
{
	Stack st;

	Init_Stack(&st);

	while (*s)
	{
		switch (*s)
		{
			//是左括号则入栈
		case '(':
		case '{':
		case '[':
			Push_Stack(&st, *s);
			break;
			//是右括号则从栈顶获取左括号来检测是否匹配
		case ')':
		case '}':
		case ']':
			if (Size_Stack(&st) == 0) // 如果有右括号,而栈里面左括号已经没有了,就不是匹配的
				return false;

			char tmp = Top_Stack(&st);
			Pop_Stack(&st);			 // 取完元素要从栈中删除

			if (tmp == '(' && *s != ')')
				return false;

			else if (tmp == '{' && *s != '}')
				return false;

			else if (tmp == '[' && *s != ']')
				return false;
		}
		s++;
	}

	if (Empty_Stack(&st)) // 如果完全匹配,循环结束时栈内一定是空的
		return true;
	else
		return false;
}

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

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

相关文章

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-5

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

【iOS开发】(五)react Native路由和导航20240421-22

【iOS开发】(五)react Native 路由和导航Navigation 20240421 在&#xff08;一&#xff09;&#xff08;二&#xff09;中我们 Reactnative搭建了开发环境、学习了 基础语法、状态管理&#xff0c;JSX、组件、状态和生命周期以及样式布局等。 在&#xff08;三&#xff09;&a…

JavaScript-事件监听、事件对象与事件流

事件监听 什么是事件什么是事件监听&#xff1f;事件监听三要素 事件监听L0 绑定L2 绑定区别vue绑定 事件类型鼠标事件键盘事件焦点事件文本事件 什么是事件对象获取事件对象环境对象回调函数事件流事件捕获事件冒泡阻止冒泡 解绑事件两种注册事件的区别事件委托 什么是事件 事…

「笔试刷题」:孩子们的游戏(圆圈中最后剩下的数)

一、题目 描述 每年六一儿童节&#xff0c;牛客都会准备一些小礼物和小游戏去看望孤儿院的孩子们。其中&#xff0c;有个游戏是这样的&#xff1a;首先&#xff0c;让 n 个小朋友们围成一个大圈&#xff0c;小朋友们的编号是0~n-1。然后&#xff0c;随机指定一个数 m &#xf…

敏捷之Scrum开发

目录 一、什么是 Scrum 1.1 Scrum 的定义 二、Scrum 迭代开发过程 2.1 迭代开发过程说明 2.1.1 开发方法 2.1.1.1 增量模型 2.1.1.1.1 定义 2.1.1.1.2 模型方法说明 2.1.1.2 迭代模型 2.1.1.2.1 定义 2.1.1.2.2 模型方法说明 2.1.2 迭代过程 2.1.2.1 产品需求Produ…

GPU服务器和普通服务器有何区别?

众所周知&#xff0c;服务器是网络中的重要设备&#xff0c;要接受少至几十人、多至成千上万人的访问&#xff0c;因此对服务器具有大数据量的快速吞吐、超强的稳定性、长时间运行等严格要求。 GPU服务器和普通服务器的主要区别在于硬件配置和适用场景&#xff0c;特别是处理器…

STM32修改主频的方法

大家都知道STM32F103C8T6的主频是72M&#xff0c;那怎么样才能在程序中获得这个主频的值呢&#xff1f;怎么样才能更改主频的值呢&#xff1f; 如图找到主频的变量&#xff0c;然后显示这个变量就是显示主频了。 #include "stm32f10x.h" // Device…

【图解计算机网络】简单易懂的https原理解析

简单易懂的https原理解析 https与http的区别混合加密对称加密非对称加密混合加密解析混合加密问题 摘要算法数字证书数字证书原理为什么通过CA证书可以解决中间人攻击的问题呢&#xff1f; https握手流程 https与http的区别 http是明文传输的&#xff0c;非常不安全&#xff0…

Java混淆的重要性

在软件开发领域&#xff0c;安全性与代码保护一直是备受关注的问题。特别是在Java这样的跨平台语言中&#xff0c;保护源代码的机密性和完整性显得尤为重要。Java混淆作为一种代码保护技术&#xff0c;其在现代软件开发中的地位日益凸显。本文将详细探讨Java混淆的重要性&#…

Java上传文件并存储到MySQL数据库

Java上传文件并存储到MySQL数据库实现过程&#xff1a; 第一步创建接口层 /** *文件接口层 */RestControllerRequestMapping("/file")public class FileController { //引用文件业务层 Resource private FileService fileService; /** *上传文件接…

基于51单片机的智能红外遥控电源电压调节系统设计

基于51单片机的智能红外遥控电源电压调节系统设计 摘要&#xff1a;随着科技的发展&#xff0c;电源电压调节系统在各种电子设备中发挥着越来越重要的作用。本文设计了一种基于51单片机的智能红外遥控电源电压调节系统&#xff0c;该系统能够通过红外遥控器实现对电源电压的快…

网络安全实训Day23

网络空间安全实训-渗透测试 文件上传攻击 定义 将Webshell文件上传到网站服务器上&#xff0c;从而获得网站整台服务器控制权限的攻击方式 Webshell 一种以网页形式存在的命令行执行环境&#xff0c;又称网页木马 分类 一句话木马 只有一行代码&#xff0c;功能强大&#xff…

ssm智能停车场管理系统

视频演示效果: SSMvue智能停车场 摘 要 本论文主要论述了如何使用JAVA语言开发一个智能停车场管理系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述智能停车…

【匹配】匈牙利匹配算法

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 匈牙利匹配算法 1. 正文 1.1 基础概念 二分图 顶点分为两个集合&#xff0c;集合间顶点相连&#xff0c;集合内点不相连 匹配 一个匹配就是一个边的…

ZYNQ之嵌入式开发04——自定义IP核实现呼吸灯、固化程序

文章目录 自定义IP核——呼吸灯实验固化程序 自定义IP核——呼吸灯实验 Xilinx官方提供了很多IP核&#xff0c;在Vivado的IP Catalog中可以查看这些IP核&#xff0c;在构建自己复杂的系统时&#xff0c;只使用Xilinx官方的免费IP核一般满足不了设计的要求&#xff0c;因此很多…

【Linux进程】守护进程

【Linux进程】守护进程 目录 【Linux进程】守护进程守护进程守护进程概念进程组和会话的概念 系统的守护进程函数 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.4.27 前言&#xff1a;本篇博客将会介绍守护进程&#xff0c;以及进程组和会话的概念&#xff0c;如何变成…

我教你如何可翻页电子画册

​电子画册是一种创新的方式&#xff0c;可以将传统的纸质画册转化为数字化的形式&#xff0c;并且具备翻页的功能。它不仅可以提供更好的阅读体验&#xff0c;还可以方便地分享给他人。 1.选择制作工具&#xff1a; 有许多在线平台和软件可以帮助你制作电子画册&#xff0c;比…

Qt6找不到Bluetooth蓝牙组件

图文解释来了&#xff01;调试了一下午。 错误如图&#xff1a; Failed to find required Qt component "Bluetooth" 解决方法&#xff1a; 找到安装QT安装包下的MaintenanceTool.exe&#xff0c;双击打开 打开后&#xff0c;找到这个Qt Connectivity&#xff0c;…

ChatGPT有记忆了?!持久记忆(Memory)功能详细解读和教程!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

【个人博客搭建】(11)swagger添加jwt信息

这个主要是为了方便使用swagger时&#xff0c;能更好的带入我们的token。 ps&#xff1a;如果使用其他第三方api工具&#xff08;apipost、postman等&#xff09;则不需要。 &#xff08;当然&#xff0c;不用不能没有&#xff0c;是吧&#xff09; 1、在AddSwaggerGen内添加…