栈(从数据结构的三要素出发)

文章目录

  • 逻辑结构
  • 物理结构
    • 顺序栈
    • 链栈
    • 共享栈
  • 数据的操作
    • 顺序栈的基本操作
    • 链栈的基本操作
    • 共享栈的基本操作
  • 数据结构的应用
    • 栈在括号匹配中的应用
    • 栈在表达式求值中的应用
    • 栈在递归调用中的应用

逻辑结构

栈是只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作。

栈的数学性质:当 n n n个不同的元素进栈时,出栈元素的不同排列个数为 1 n + 1 C 2 n n \frac{1}{n+1}C_{2n}^n n+11C2nn(卡特兰公式)。

在这里插入图片描述


物理结构

顺序栈

利用静态数组实现,并需要记录栈顶指针,且栈的大小不可改变

typedef struct {
	int data[Maxsize];		// 定义栈中元素
	int top;				// 定义栈顶指针
} SqStack;

链栈

利用链表实现,只需要记录头指针,利用链表的头插法实现进栈操作,解决了顺序栈中栈的大小不可扩充的问题。
在这里插入图片描述

typedef struct LinkNode {
	int data;					// 数据域
	struct LinkNode *next;		// 指针域
} LinkNode, *LiStack;

共享栈

利用栈底位置的相对不变性,可以让两个顺序栈共享一个一维数组,将两个栈的栈底分别设置在共享空间的两端,两个栈的栈顶向共享空间的中间延伸,解决了顺序栈空间利用率不足的问题。

在这里插入图片描述

typedef struct {
	int data[Maxsize];		// 数据域
	int top1, top 2;		// 两个栈顶指针
} SStack;

数据的操作

顺序栈的基本操作

初始化

void InitStack(SqStack &s)
{	
	s.top = -1;
}

判断栈为空

bool isEmpty(SqStack s)
{	
	if (s.top == -1) return true;
	return false;
} 

判断栈为满

bool isFull(SqStack s)
{	
	if (s.top == Maxsize - 1) return true;
	return false;
}

入栈

bool Push(SqStack &s, int x)
{	
	if (!isFull(s)) 
	{
		s.data[ ++ s.top] = x;
		return true;
	}
	return false;
}

出栈

bool Pop(SqStack &s, int &x)
{	
	if (!isEmpty(s)) 
	{
		x = s.data[s.top -- ];
		return true;
	}
	return false;
}

读取栈顶元素

bool GetTop(SqStack s, int &x)
{	
	if (!isEmpty(s)) 
	{
		x = s.data[s.top];
		return true;
	}
	return false;
}

链栈的基本操作

初始化

void InitStack(LiStack s)
{	
	s->next = NULL;
}

判断栈为空

bool isEmpty(LiStack s)
{	
	if (s->next == NULL) return true;
	return false;
} 

入栈

bool Push(LiStack s, int x)
{	
	LinkNode *t = (LinkNode *) malloc (sizeof(LinkNode));
	t->next = s->next;
	s->next = t;
	return true;
}

出栈

bool Pop(SqStack s, int &x)
{	
	if (!isEmpty(s)) 
	{
		LinkNode *t = s->next;
		x = t->data;
		s->next = t->next;
		free(t);
		return true;
	}
	return false;
}

读取栈顶元素

bool GetTop(SqStack s, int &x)
{	
	if (!isEmpty(s)) 
	{
		x = s->next->data;
		return true;
	}
	return false;
}

共享栈的基本操作

初始化

void InitStack(SStack &s)
{	
	s.top1 = -1;
	s.top2 = Maxsize;
}

判断栈为空(true:判断栈1,false:判断栈2)

bool isEmpty(SStack s, bool flag)
{	
	if (s.top1 == -1 && flag) return true;
	else if (s.top2 == Maxsize && !flag) return true;
	return false;
} 

判断栈为满

bool isFull(SStack s)
{	
	if (s.top1 + 1 == s.top2) return true;
	return false;
}

入栈(true:入栈1,false:入栈2)

bool Push(SStack &s, int x, bool flag)
{	
	if (!isFull(s)) 
	{
		if (flag) s.data[ ++ top1] = x;
		else s.data[ -- top2] = x;
		return true;
	}
	return false;
}

出栈(true:出栈1,false:出栈2)

bool Pop(SStack &s, int &x, bool flag)
{	
	if (!isEmpty(s, flag)) 
	{
		if (flag) x = s.data[top -- ];
		else x = s.data[top ++ ];
		return true;
	}
	return false;
}

读取栈顶元素(true:读栈1,false:读栈2)

bool GetTop(SStack s, int &x)
{	
	if (!isEmpty(s, flag)) 
	{
		if (flag) x = s.data[top];
		else x = s.data[top];
		return true;
	}
	return false;
}

数据结构的应用

栈在括号匹配中的应用

在这里插入图片描述

bool bracketCheck(char str[], int length)
{
	SqStack s, InitStack(s);
	
	for (int i = 0; i < length; i ++ )
	{
		if (str[i] == '(' || str[i] == '[' || str[i] == '{')		// 遇到左括号进栈
			Push(s, str[i]);
		else 
		{
			if (isEmpty(s)) return false;		// 若遇到右括号,但栈是空的,证明左括号多了一个,则匹配不成功
			
			char topElem;
			Pop(s, topElem);

			/*左右括号不匹配*/
			if (str[i] == ')' && topElem != '(') return false;
			if (str[i] == ']' && topElem != '[') return false;
			if (str[i] == '}' && topElem != '{') return false;
		}
	}
	return isEmpty(s);		// 左右括号全部匹配
}

栈在表达式求值中的应用

在这里插入图片描述
在这里插入图片描述

  • 栈中运算符的优先级高于当前运算符的优先级时:可以得出基于栈顶元素的运算就可以被确定了。
  • 栈中运算符的优先级等于当前运算符的优先级时:基于左优先原则(左侧先做运算)。

其实就是中缀表达式转后缀表达式的过程中,边转换边计算(一旦确定运算就直接计算,再计算完的操作数压回栈中)就可以实现表达式的求值。

计算单元

void eval(Stack<int> &num, Stack<char> &op)
{
	int x = num.top();
	num.pop();
	int y = num.top();
	num.pop();
	char c = op.top();
	op.pop();

	if (c == '+')
        num.push(x + y);
    else if (c == '-')
        num.push(y - x);
    else if (c == '*')
        num.push(x * y);
    else 
        num.push(y / x);
}

计算表达式单元

int exEval(char str[], int length)
{
	unordered_map<char, int> pr = {{'+', 1}, {'-', 1}, {'*', 2}, {'/', 2}};			// 定义运算符的优先级
	Stack<int> num;
	Stack<char> op;
	
`	for (int i = 0; i < length; i ++ )
	{
		if (isdigit(str[i]))
		{
			int x = 0;
			while (i < length && isdigit(str[i])) x = x * 10 + (str[i] - '0'), i ++ ;		// 将字符类型转换成数字
			num.push(x);
			i -- ;
		}
		else if (str[i] == '(') op.push(str[i]);
		else if (str[i] == ')')
		{
			while (op.top() != '(') eval(num, op);		// 将括号内的所有表达式已确定,直接计算
			op.pop();
		}
		else			// 操作符的情况 
		{
			while (op.size() && op.top() != '(' && pr[op.top()] >= pr[str[i]]) eval(num, op);			// 将已确定的运算式计算
			op.push(str[i]);		// 将待定运算符入栈
		}
	}
	while (op.size()) eval();		// 若栈内还有元素,直接做计算
	return num.top();				// 栈顶元素就是最终计算的结果
}

栈在递归调用中的应用

在这里插入图片描述
在这里插入图片描述
以斐波那契数列为例,其定义为:

F ( n ) = { F ( n − 1 ) + F ( n − 2 ) , n > 1 1 , n = 1 0 , n = 0 F(n)= \begin{cases}F(n-1)+F(n-2), & n>1 \\ 1, & n=1 \\ 0, & n=0\end{cases} F(n)= F(n1)+F(n2),1,0,n>1n=1n=0

int F(int n)
{	
	if (n == 0) return 0;
	if (n == 1) return 1;
	return F(n - 1) + F(n - 2);
}

递归调用需满足以下两个条件:

  • 递归表达式(递归体)
  • 边界问题(递归出口)

精髓:能否将原始问题转换成属性相同但规模较小的子问题。

以F(5)为例,以下是递归调用的执行过程:
在这里插入图片描述

其中F(3)被调用2次,F(2)被调用3次,F(1)被调用5次,F(0)被调用3次,故递归调用效率低下,但是代码实现简单,容易理解。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

保留两位小数不四舍五入,10000.55变成10000.54的坑

正解 function moneyFormat(num){ let money num "";//隐式转换为字符串和toString()效果一样//没有小数补齐这个0if(money.indexOf(".")"-1"){moneymoney".00";}else{//有小数截取前二位小数moneymoney.substring(0,money.inde…

工业AI的崛起,中国自主创新的新机遇

我们都知道&#xff0c;互联网已经深刻地改变了我们的生活方式&#xff0c;催生了无数的新型商业模式和创新产业&#xff0c;推动了社会的经济变革。中国在互联网领域的发展取得了举世瞩目的成就&#xff0c;建成了全球规模最大、技术领先的5G网络&#xff0c;互联网应用的普及…

vue3 vite title 页面标题设置

效果图&#xff1a; 1. 安装 vite-plugin-html 插件 npm install vite-plugin-html -D2. 修改 vite.config.js import {defineConfig, loadEnv} from vite import { createHtmlPlugin } from "vite-plugin-html" import {resolve} from path import vue from vitej…

我说同事咋找工作命中率这么高,原来是学习了这些招式

最近有两个同事离职了&#xff0c;其中一个还是专科&#xff0c;他俩一个是前端开发&#xff0c;一个是python开发&#xff0c;两个人都接近35岁了。我们还劝告他们&#xff0c;不要离职&#xff0c;要骑驴找马。但了解后&#xff0c;他俩非常有信心的说&#xff1a;不怕&#…

振弦采集仪在岩土工程地质灾害监测中的可行性研究

振弦采集仪在岩土工程地质灾害监测中的可行性研究 引言&#xff1a; 岩土工程地质灾害是指在岩土体中由于自然力和人类活动等因素引起的&#xff0c;对人类生活、财产以及环境造成威胁的灾害。为了及时发现并准确监测地质灾害的发生和演化过程&#xff0c;振弦采集仪作为一种新…

web自动化-下拉框操作/键鼠操作/文件上传

在我们做UI自动化测试的时候&#xff0c;会有一些元素需要特殊操作&#xff0c;比如下拉框操作/键鼠操作/文件上传。 下拉框操作 在我们很多页面里有下拉框的选择&#xff0c;这种元素怎么定位呢&#xff1f;下拉框分为两种类型&#xff1a;我们分别针对这两种元素进行定位和…

6-4 先序输出度为2的结点

作者 DS课程组 单位 临沂大学 本题要求实现一个函数&#xff0c;按照先序遍历的顺序输出给定二叉树中度为2的结点。 函数接口定义&#xff1a; void PreorderPrintNodes( BiTree T);T是二叉树树根指针&#xff0c;PreorderPrintNodes按照先序遍历的顺序输出给定二叉树T中度为…

随笔(二)——项目代码优化

文章目录 前言一、传入的props的默认值定义为空数组1.问题&#xff08;提示对象的类型为unknwn&#xff09;2.优化 二、document 上不存在xxx属性1.问题2.做了一个兼容浏览器的关闭全屏方法3. 解决方法 &#xff08;使用declare globa设置全局变量类型&#xff09;&#xff08;…

ssm球场计费管理系统-计算机毕业设计源码77275

摘 要 大数据时代下&#xff0c;数据呈爆炸式地增长。为了迎合信息化时代的潮流和信息化安全的要求&#xff0c;利用互联网服务于其他行业&#xff0c;促进生产&#xff0c;已经是成为一种势不可挡的趋势。在球馆计费管理的要求下&#xff0c;开发一款整体式结构的球场计费管理…

配置阿里yum源

配置阿里yum源&#xff08;这个很重要&#xff09;&#xff1a;https://developer.aliyun.com/article/1480470 1.备份系统自带yum源配置文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup2.下载ailiyun的yum源配置文件 2.1 CentOS7 wge…

专业软件测试机构CMA、CNAS资质与测试报告介绍

软件测试机构 一、专业软件测试机构CMA和CNAS的资质介绍如下&#xff1a; 1.CMA&#xff08;China Inspection Body and Laboratory Mandatory Approval&#xff09;是中国计量认证的缩写&#xff0c;是由中国计量认证机构对软件测试实验室在测试技术能力、测试设备、质量保证…

【Java面试】四、MySQL篇(上)

文章目录 1、定位慢查询2、慢查询的原因分析3、索引3.1 数据结构选用&#xff1a;二叉树 & 红黑树3.2 数据结构选用&#xff1a;B树 4、聚簇索引、非聚簇索引、回表查询4.1 聚簇索引、非聚簇索引4.2 回表查询 5、覆盖索引、超大分页优化5.1 覆盖索引5.2 超大分页处理 6、索…

Stable Diffusion AI绘画:从提示词到模型出图的全景指南

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

Python Anaconda环境复制

虚拟环境复制 conda-pack 第一种方式 conda打包 在打包之前如果没有conda-pack包的话&#xff0c;需要安装pip install conda-pack打包 conda pack -n py36 -o py366.tar.gz -o就是给导出得到的压缩包就在当前目录下 传输到另外一台服务器上 有两台linux服务器&#xff0c…

30V MOS管 60VMOS管 100VMOS管 150VMOS管推荐

MOS管&#xff0c;即金属氧化物半导体场效应管&#xff0c;其工作原理是&#xff1a;在P型半导体与N型半导体之间形成PN结&#xff0c;当加在MOS管栅极上的电压改变时&#xff0c;PN结之间的沟道内载流子的数量会随之改变&#xff0c;沟道电阻也会发生改变&#xff0c;进而改变…

Docker部署后的中文乱码问题

本地和服务器上面生成图片文字多没有乱码&#xff0c;但是服务部署到docker上面就开始出现乱码。排查了一下发现是docker上缺少相应的中文字体&#xff0c;添加字体即可解决。 1.在网站上找到相关资源并下载字体-字体下载-字体下载大全-字体免费下载|字体下载 2.上传到服务器 …

无忧企业文档专为企业打造智能文档管理

在当今这个信息化快速发展的时代&#xff0c;企业运营中产生的文档数量日益增多&#xff0c;如何高效、安全地管理这些文档成为了企业发展过程中不可忽视的问题。那么&#xff0c;企业需要什么样的文档管理系统呢&#xff1f;无忧企业文档&#xff0c;作为一款专为现代企业打造…

使用Prometheus组件node_exporter采集linux系统的指标数据(包括cpu/内存/磁盘/网络)

一、背景 Linux系统的基本指标包括cpu、内存、磁盘、网络等&#xff0c;其中网络可以细分为带宽进出口流量、连接数和tcp监控等。 本文使用Prometheus组件node_exporter采集&#xff0c;存储在promethues&#xff0c;展示在grafana面板。 二、安装node_exporter 1、下载至本…

PID控制中积分项目的理解,消除稳态误差的作用,表示着过去(PID积分控制)

1&#xff0c;消除稳态误差 积分项目是对于历史误差进行的累积&#xff0c;可以理解&#xff0c;系统的误差累积表示不断的在减少误差&#xff0c;最终消除误差&#xff0c;这个过程需要将误差进行累加&#xff0c;才可以真正知道误差的大小是多少&#xff0c;用最终累加的误差…

100个 Unity小游戏系列三 -Unity 抽奖游戏专题三老虎机游戏

一、演示效果 二、知识点讲解 2.1 布局 public void CreateItems(SlotsData[] slotsData){isInited false;slotsPrizeList new List<SlotsData>();for (int i 0; i < slotsData.Length; i){var item slotsData[i];slotsPrizeList.Add(item);}float bottomY -it…