题目:用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也太多了,,能不能用设计模式之类的优化下,,
- 负数处理、最后一次弹出处理