运用技术
栈(这里用了顺序栈,就是数组存储的),ASCII码,中缀/后缀表达式
后缀表达式
对于四则运算,括号也是当中的一部分,先乘除后加减问题依然复杂,20实际50年代,波兰逻辑学家Jan Lukasiewicz想到一种不需要括号的后缀表达法*,我们也用逆波兰表示.
举个例子 9+(3-1)*3+10 /2
叫中缀表达式,后缀表达式长这个样子: 9 3 1 - 3 * + 10 2 / +
先看看后缀表达式和栈之间怎么用的把
(书上东西步骤很详细也太多了,有请截图君上场)
后缀表达式确实很神奇就解决掉了括号的问题
如何转换后缀表达式
接下来疑问很简单** 如何把中缀表达式转后缀表达式**,先看规则
从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;
若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减),则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式位置.
9+(3-1)*3+10 /2
===>> 9 3 1 - 3 * + 10 2 / +
嗯…知道说了也看不懂,还是截图君上场
第6步这里,经过实践和一步步敲,得出结论:
第六步第一句话和第二句的写反了
即应该是:紧接着是符号…"*"进栈. 接着是数字3…- 3.
为什么?因为中缀表达式是9+(3-1)*3+10 /2
这个呀,明显*
在前
9+(3-1)*3+10 /2
===>> 9 3 1 - 3 * + 10 2 / +
代码实现
package ***;
/**
* @Author: wsh
*/
public class StackTest {
public static void main(String[] args) {
ArrStackList conver = conver("9+(3-1)*3+6/2");
//目前conver是一个个push进去的
//是倒序,所以再创建一个栈,转换"正过来"
ArrStackList order = new ArrStackList();
int top = conver.arrStack.top;
while (top != -1) {
order.push(conver.pop());
top = conver.arrStack.top;
}
order.out("后缀表达式:");
System.out.println("结果是" + compute(order));
}
public static int compute(ArrStackList order) {
//临时用一个栈中转
ArrStackList arr = new ArrStackList();
arr.init(new String[]{});
do {
int pop = order.pop();
//含运算法则 42 * 43 + 45- 47 /
if (pop < 48) {
int one = (char)(arr.pop());
int two = (char)(arr.pop());
int operator = operator(pop, two, one);
arr.push(operator);
}else{
arr.push( Integer.parseInt(String.valueOf((char)pop)));
}
} while (order.arrStack.top !=-1);
return arr.pop();
}
/**
* 四则运算
*/
public static int operator(int o, int op1, int op2) {
int res=0;
//ASCII码 42 * 43 + 45- 47 /
switch (o) {
case 42: res= op1*op2;break;
case 43: res= op1+op2;break;
case 45: res= op1-op2;break;
case 47: res= op1/op2;break;
}
return res;
}
/**
* 中缀转后缀
* 9+(3-1)*3+6/2 转化为 9 3 1 - 3 * + 6 2 / +
*/
public static ArrStackList conver(String data) {
//用来存储,最后返回
ArrStackList result = new ArrStackList();
result.init(new String[]{});
//中间处理
ArrStackList arrStackList = new ArrStackList();
arrStackList.init(new String[]{});
char[] chars = data.toCharArray();
for (char d : chars) {
//ASCII码 42 * 43 + 45- 47 / 40( 41)
switch (d) {
case 42:
case 47: {
int pop1 = arrStackList.pop();
//若有+ - 优先级低,则* 进栈
if (pop1 != -1 && (pop1 == 43 || pop1 == 45)) {
arrStackList.push(pop1);
arrStackList.push(d);
} else {
result.push(d);
}
break;
}
case 43:
case 45: {
int out = arrStackList.pop();
if (out != -1 && (out == 42 || out == 43)) {
while (out != -1) {
result.push(out);
out = arrStackList.pop();
}
arrStackList.push(d);
} else {
if (out != -1) {
arrStackList.push(out);
}
arrStackList.push(d);
}
break;
}
//左括号直接进栈
case 40:
arrStackList.push(d);
break;
case 41: {
int pop = arrStackList.pop();
while (pop != 40) {
result.push(pop);
pop = arrStackList.pop();
}
break;
}
default:
result.push(d);
}
}
int out = arrStackList.pop();
while (out != -1) {
result.push(out);
out = arrStackList.pop();
}
return result;
}
}
class ArrStackList {
ArrStack arrStack = new ArrStack();
public int pop() {
if (arrStack.top == -1) {
System.out.println("栈已空");
return -1;
}
int datum = arrStack.data[arrStack.top];
arrStack.data[arrStack.top] = 0;
arrStack.top--;
return datum;
}
public void push(int value) {
if (arrStack.top + 1 == arrStack.data.length) {
System.out.println("入栈失败,已满");
return;
}
arrStack.top++;
arrStack.data[arrStack.top] = value;
}
public void init(String[] data) {
for (int i = 0; i < data.length; ) {
int value = Integer.parseInt(data[i]);
arrStack.data[i] = value;
arrStack.top = i++;
}
System.out.println();
}
public void out(String str) {
System.out.println(str);
int[] data = arrStack.data;
int top = arrStack.top;
for (int i = top; i >= 0; i--) {
System.out.print((char) data[i] + " ");
}
System.out.println("\n======");
}
}
class ArrStack {
private int MaxLength = 20;
public int[] data = new int[MaxLength];
public int top = -1;
}
输出
栈已空
栈已空
栈已空
正序内容:
9 3 1 - 3 * + 6 2 / +
======
结果是18
这里的堆栈已空,返回了-1 其实是不同的位置,while在循环中判断-1使用的,所以不受影响
缺点和不足
- 运算长度数少,代码数组初始化20个,注定不能运算太长的四则运算,不然
后缀表达式
周转完以后太长了,就塞不下了 - 运算数值小,在判断运算法则使用
char
类型,String
转char
类型,注定两位数要拆开(10就被算成1和0两个char
),达到效果不一致,所以书中是有个10,我换成了6 😃 - 运算逻辑繁琐,因为默认是int类型,里面存放了
char
的运算符又放了char
的数值,采用ASCII码方式,(42 * 43 + 45- 47 /
),在取出计算时,再根据范围判断运算和数字 - 运算类型转换过多,若是运算符取出两个
char
类型数字转int
加减乘除,若是数值需要Integer.parseInt(String.valueOf((char)pop))
将pop=51
转成int类型数值3,以便后续运算
收获和总结
- 本来以为写的是后缀,发现其实还有运算,相比运算比后缀复杂一些.同时操作两个栈进和出
- 竟然char转int类型还要搜索,真是逊
Integer.parseInt(String.valueOf((char)pop)
- 四则运算那边,自己一开始写的是在
case
里面,差不多最后才拿出来,给自己带来很多无用功.嗯…不会偷懒(抽取)的程序员不是好程序员
拜了个拜,下个知识点搞起.