面试题笔记-表达式求值类

题目:用Java或C/C++写一段8位十六进制整数表达式求值程序,具体要求为:以一个字符串作为参数,字符串是由十六进制整数构成的表达式,可支持±*/%&|^~运算(运算符意义和优先级按C和Java语法理解,除法的商只保留整数部分),允许括号,计算出这个表达式的值返回,如果表达式有错误需要提示出来。特别地,代码要注意效率,尽量少扫描源串(包括隐性扫描)。

先写个大概放这里

笔记

总结下学到的内容,为了在遇到同类问题都能解决。毕竟很多出题人都是经典问题改一改
表达式求值,重点在于对字符串的解析、根据操作顺序选择数据结构。
经典的题目为三种表达式的运算(前缀,中缀,后缀)
后缀表达式求值我们曾在栈的学习中学到过,又称逆波兰表达式

数据结构:

用栈,根据后进先出的特性,将 字符串位置靠后确需要先计算的子表达式 先出栈计算

有趣的是,cpu也是操作栈内存来计算表达式,这也是学习组成原理相关知识时的经典案例

算法

一个长长的表达式(虽然肯定不如我长)可以被拆成很多子表达式,而子表达式的运算优先级不同
相同优先级的子表达式可以直接运算,这就是最基础的表达式求值问题的思路

数字
操作符
逐个字符读入
数字?
连续读入并转换为数字
入栈
从栈中取出两个数字运算
操作数栈

我们以此为基础,针对不同题目进行变种,同时也为上图逻辑进行扩张

  • 语法检查?
  • 单目运算符?
  • 不同优先级?

优先级问题

!在这里插入图片描述

左括号
右括号
低于栈顶
高于栈顶
等于栈顶
读取到操作符
入运算符栈
运算栈中内容直至遇到左括号-
与栈顶元素比较优先级
计算
入栈
先计算

此处是严蔚敏老师《数据结构》书上的方法(“算符优先法”),用二维数组表示两个运算符间优先级的高低,就像图论里的邻接矩阵一样。
同时也是一种空间换时间的思想,提前写好优先级关系随取随用。

空间换时间:把后面要用的数据存在提前内存里,随取随用(立即推,用能随机访问的数组保存)
时间换空间:不存储结果,每次需要的时候现场靠计算临时算出来

不过我们也可以用一维数组直接用数字来表示各个运算符的优先级。

#include<iostream>
#include<stack>
using namespace std;

long long calculateHex(string s);
int priority[] = {4, 4, 5, 5, 5, 3, 1, 2, 6, -1, 10};
stack <long long> numbers;  // Operand stack
stack <char> opers;  // Operator stack
/*
* 根据操作符返回其优先级 
*/
int getPriority(char a){  
    int i;  
    switch(a){  
        case'+':i=0;break;  
        case'-':i=1;break;  
        case'*':i=2;break;  
        case'/':i=3;break;  
		case'%':i=4;break;
		case'&':i=5;break;
		case'|':i=6;break;
		case'^':i=7;break;
		case'~':i=8;break;
		case'(':i=9;break;  
        case')':i=10;break;
		default: return 0;  
    }  
    return priority[i];  
}

int main(){
	string s;
	while(cin>>s){
		cout<<calculateHex(s) << endl;	
		numbers.pop();
	}	
} 

/*
* 弹出运算符栈栈顶的运算符,并对操作数栈进行对应运算
* 输入:操作符x
* return 运算结果
*/
long long operate(char x){  
	long long i, m, n;  
        switch(x){  
        case'+':
        	m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = m + n;break;  
        case '-':
			m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = n - m;break;  
        case'*':
        	m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = m * n;break;  
        case'/':
        	m = numbers.top();
			numbers.pop();
			if(m == 0){
				cout<<"不可除以0"<<endl;
				return 0; 
			} 
			n = numbers.top();
			numbers.pop();
			i = n / m;break;  
		case'%':
			m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = n % m;break;
		case'&':
			m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = n & m;break;
		case'|':
			m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = n | m;break;
		case'^':
			m = numbers.top();
			numbers.pop();
			n = numbers.top();
			numbers.pop();
			i = n ^ m;break;
    }  
    return i;
}  

/*
* 计算16进制表达式
* 输入:表达式字符串s
* return 整个表达式计算后的结果,即numbers最后的栈顶元素
*/
long long calculateHex(string s){
	//numbers.push(0);
	string tempNum = "";
	for(int i = 0; i < s.length(); i++){
		//如果是16进制数字 
		if(isxdigit(s[i])){
			long temp = 0;
			while(isxdigit(s[i])){
				temp*=16;
				if(s[i]>='0' && s[i] <= '9'){
					temp += (s[i] - '0');
				}else if(s[i] >= 'a' && s[i] <= 'f'){
					temp += (s[i] - 'a' + 10);
				}else if(s[i] >= 'A' && s[i] <= 'F'){
					temp += (s[i] - 'A' + 10);
				}
				i++;
			}
			i--;
			numbers.push(temp); 
		}
		//如果是字符 
		else{
			int pro = getPriority(s[i]);
			switch(pro){
				case 0:
					cout<<"\'" << s[i] << "\' is invalid" ;
					break;
				case -1:
					opers.push('(');
					break;
				case 10:
					while(true){
						char oper = opers.top();
						opers.pop();
						if(oper == '(') break;
						numbers.push(operate(oper));
					}
					break;	
				case 6:		
					++i;
					if(s[i] >= 'a' && s[i] <= 'f')	
						numbers.push(~(s[i] - 'a' + 10));
					else if(s[i] >= 'A' && s[i] <= 'F')	
						numbers.push(~(s[i] - 'A' + 10));
					else 
						numbers.push(~(s[i] - '0'));
					break;
				default:
					if(opers.empty()){
						opers.push(s[i]);
						continue;
					} 
					if(pro >= getPriority(opers.top())){
						opers.push(s[i]);
					}else{
						numbers.push(operate(opers.top()));
						opers.pop();
						opers.push(s[i]);
					}
			}
		}
	}
	while(!opers.empty()){
		numbers.push(operate(opers.top()));
		opers.pop();
	}
	return numbers.top();
}
  • 这if-else也太多了,,能不能用设计模式之类的优化下,,
  • 负数处理、最后一次弹出处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值