DS 0907(第三章 栈与队列的应用等)

栈的应用—括号匹配问题

eg:(((())))
在程序中:最后出现的左括号最先被匹配

----->栈的特性:先进后出,后进先出

每出现一个右括号,就消耗(出栈)一个左括号
在这里插入图片描述
遇到左括号就入栈,遇到右括号就消耗一个左括号出栈

程序流程图:
在这里插入图片描述

#include<stdio.h> 
#include<stdlib.h>
#define MaxSize 10		//定义栈中元素的最大个数 
typedef struct {
	char data[MaxSize];	//静态数组存放栈中元素 
	int top;			//栈顶指针 
}SqStack;

//初始化栈
void InitStack(SqStack &S){
	S.top=-1;  				//初始化栈顶指针 
} 

//判空操作
bool StackEmpty(SqStack S){
	if(S.top==-1)
	return true;			//栈空 
	else
	return false;			//不空 
}

//入栈操作 
bool Push(SqStack &S,char x){
	if(S.top==MaxSize-1)		//栈满报错 
	return false;
	S.top=S.top+1;				//指针先加1 
	S.data[S.top]=x;				//新元素入栈 
	
	//S.data[++S.top]=x; 
} 

//出栈操作,并用x返回值
bool Pop(SqStack &S,char &x){
	if(S.top==-1)		//栈空,报错 
	return false;
	x=S.data[S.top];	//栈顶元素先出栈 
	S.top=S.top-1;		//指针再减1
	
	// x=S.data[--S.top]; 
	return true; 
}

bool test(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(StackEmpty(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;
	} 
	}
	if( StackEmpty(S)) //检索完所有括号后,栈空则匹配成功 
	return true;
	else
	return false;
}

int main(void){

	char str[4]={'(','(',')',')'};        //建立一个存放括号的数组

	if(test(str,4)){
	printf("true");
	}else{	
	printf("false");
	}
	
	return 0; 
}

栈的应用----表达式求值

  • **三种算术表达式:**前缀,中缀,后缀表达式
    - 中缀表达式转后缀表达式,后缀表达式求值
  • 中缀表达式转前缀表达式,前缀表达式求值

操作表达式由三部分组成:

  1. 操作数
  2. 运算符
  3. 界限符:括号,表明运算的顺序

波兰数学家的灵感:不用界限符也可以无歧义的表达运算顺序

  • 逆波兰表达式:后缀表达式
  • 波兰表达式:前缀表达式

中缀,前缀,后缀表达式

  • 中缀表达式:a+b,a+b-c
  • 后缀表达式:ab+,ab+c-或abc-+
  • 前缀表达式:+ab,-+abc

中缀表达式转后缀表达式的手算

  1. 确定中缀表达式各个运算符的运算顺序
  2. 选择下一个运算符,按【做操作数,右操作数,运算符】的方式组合成一个新的操作数
  3. 如果还有运算符没有被处理,则继续第二步

左优先原则
为了保证手算与机算的结果相同,只要左边的优先符能先算,就先算左边的

后缀表达式的计算
从左往右进行扫描,每遇到一个运算符,就让运算符前面最近的两个操作数执行对应运算,合体为一个操作数,需要注意两个操作数的左右顺序

后缀表达式的机算

注意操作数的特性,后进先出,先出栈的是右操作数

  1. 对一个算式开始从左向右扫描
  2. 扫描到操作数就压入栈中
  3. 扫描的运算符则从栈中取出两个数,注意后进的数先出,且在运算符的右边
  4. 之后扫到操作数再重复第二步

后缀表达式适用于基于栈的编程语言


中缀转前缀表达式

  1. 确定中缀表达式中各个运算符的运算顺序
  2. 选择下一个元运算符,按照【运算符 做操作数 右操作数】的方式组合为一个新的操作数
  3. 如果还有运算符没有处理,就继续第二步

右优先原则:优先让右边的运算符生效

用计算机实现前缀表达式计算时

  1. 从右往左进行扫描元素,直到处理完所有元素
  2. 若扫描到操作数压入栈,并会到第一步,否则执行第三步
  3. 若扫描到运算符,则弹出栈顶两个元素,执行相应运算,运算结果压回栈顶,回到第一步
    与后缀表达式机算不同的是,在前缀表达式中,先出栈的是左操作数

后缀表达式在计算机中使用更多


机器实现

中缀表达式转后缀表达式流程

1.初始化一个栈,用于保存暂时还不能确定运算顺序的运算符
2.从左到右处理各个元素,可能遇到三种情况

  • 遇到操作数,直接加入后缀表达式
  • 遇到界限符。遇到(直接入栈,遇到(则依次弹出栈内运算符并加入后缀表达式,直到弹出(为止,注意(不加入后缀表达式
  • 遇到运算符。则依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到(或栈空则停止。之后再把当前运算符入栈
    3.按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式

中缀表达式的计算(用栈实现)

中缀转后缀+后缀表达式的求值----两种算法的结合

用栈实现中缀表达式的计算

  1. 初始化两个栈,操作数栈和运算符栈
  2. 若扫描到操作数压入操作数栈
  3. 若扫描到运算符或界限符,则按照中缀转后缀相同的逻辑压入运算符栈(期间也会弹出运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,运算结果再压回操作数栈)

在这里插入图片描述

栈的应用----递归

void main(){
	int a,b,c;
	//...
	func1(a,b);
	c=a+b;
	//...
} 

void func1(int a,int b){
	int x;
	//...
	func2(x);
	x=x+10086;
	//...
}


void func2(int x){
	int m,n 
	//...
} 

函数调用的特点:==最后调用的函数最先执行结束(后进先出)

  • 函数调用时需要调用一个栈存储
    1.调用返回地址
    2.实参
    3.局部变量

适合用递归算法解决:可以把原始问题转化为属性相同,但规模较小的问题

1.计算正整数的求值n!
factoria(n):n*factorial(n-1)

2.求斐波那契数列:fib(n):fib(n-1)+fib(n-2)

递归调用时,函数调用栈可称为“递归工作栈”

  • 每进入一层递归,就将递归调用所需信息压入栈顶
  • 每退出一层递归,就从栈顶弹出相应信息
  • 缺点:太多层递归可能会导致栈溢出
  • 缺点:可能包含很多重复计算

队列的应用

1.树的层次遍历:一层一层的遍历树里的各个结点
2.图的广度优先遍历:
3.队列再操作系统中的应用:多个进程争抢这使用有限的系统资源时,FCFS(FIrst Come First Service)是一种常用策略,可用队列来实现

特殊矩阵的压缩存储

矩阵可以很容易的使用数组表示

一维数组

ElemType a[10];   //ElemType型一维数组

起始地址:LOC

  1. 各数组元素大小相同,且物理上连续存放
  2. 数组元素a[i]的存放地址=LOC+i*sizeof(ElemType)
  3. 数组下标默认从0开始

二维数组

ElemType b[2][4]		//两行四列的二维数组

逻辑视角:
在这里插入图片描述
内存视角:在内存中有两种模式:行优先,列优先

普通矩阵的存储

  • 可用二维矩阵进行存储注意描述矩阵元素时,行列通常从1开始,然而描述数组时,通常下标从0开始

对于某些特殊的矩阵可以采用特殊的存储方式来节约存储空间

对称矩阵的压缩存储
def:若n阶方针中每一个元素aij都有aij=aji,则该矩阵是对称矩阵

  • 主对角线:i=j
  • 上三角区:i《j
  • 下三角区:i》j
    由于上三角区域与下三角区域相同

压缩存储策略:

  • 只存储主对角线+下三角线
  • 主对角线+上三角区

**实现方法:**按行优先的原则将各元素存入一维数组中
主要问题:
1.数组的大小:
1+2+3+。。。+n

2.对称矩阵压缩存储后怎样才能方便使用
可以实现一个映射函数:矩阵下标——》一维数组下标

eg:aij是第(1+2+3+i-1)+j个元素,所以它对应的下标再-1

三角矩阵的压缩存储

下三角矩阵:除了主对角线和下三角区,其余的元素都相同
上三角矩阵:

**压缩存储策略:**按行优先的原则将不是常量的区域存入一维数组。并在最后一个位置储存常量C。

三对角矩阵的压缩存储:
def:三对角矩阵,又称为带状矩阵,当|i-j|>1时aij=0;

存储策略: 按行优先的原则,只存储带状部分。

第一行和最后一行有两个元素,其他每行都有三个元素

  • 一维数组的长度为3n-2
  • 在这里插入图片描述
  • 数组下标为k的元素在第几行第几列
    在这里插入图片描述
    j=k-2i+3

稀疏矩阵的压缩存储

def:稀疏矩阵的非零元素远远小于矩阵元素的个数

压缩存储策略

  1. 顺序存储:三元组<行,列,值>
    可以定义一个struct,在其中存储i,j,v,再定义一个struct的数组

2.链式存储:十字链表法
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值