数据结构(Java)学习笔记——知识点:前缀、中缀、后缀表达式
八、前、中、后缀表达式
1、前缀表达式(波兰表达式)
a、前缀表达式的运算符位于操作数之前
b、举例:( 3 + 4 ) * 5 - 6 对应的前缀表达式应为为 - * + 3 4 5 6
在前缀表达式中,是从右至左扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们进行相应的计算(栈顶元素和次栈顶元素),并将结果入栈;重复上述过程,直到表达式的最左端,最后算出的值就是表达式的结果。
上述表达式求值的步骤如下:
Ⅰ、从右至左扫描,将 6、5、4、3 压入堆栈。
Ⅱ、遇到 + 运算符,弹出 3 和 4 (3 为栈顶元素,4为次栈顶元素),计算出 3 + 4 的结果, 为 7,再将 7 入栈。
Ⅲ、接下来进行 * 的运算,弹出 7 和 5 ,计算出得 7 * 5 = 35,再将 35 入栈。
Ⅳ、最后时 - 运算符,计算出 35 - 6 的值,得到29,输出最终结果。
2、中缀表达式
中缀表达式,就是我们人看到的数学表达式,例:( 3 + 4 ) * 5 - 6 。对人类友好,对二进制的硅基大脑是不友好的。在计算时,一般都转换成后缀表达式进行操作。
3、后缀表达式(逆波兰表达式)
a、后缀表达式与前缀表达式相似,就是运算符位于操作数之后。
b、举例:( 3 + 4 ) * 5 - 6 对应的后缀表达式是 3 4 + 5 * 6 -
c、再比如
在后缀表达式中,是从左至右扫描表达式,遇到数字时,将数字压入堆栈,遇到运算符时,弹出栈顶的两个数,用运算符对他们进行相应的计算(栈顶元素和次栈顶元素),并将结果入栈;重复上述过程,直到表达式的最右端,最后算出的值就是表达式的结果。
上述表达式求值的步骤如下:
Ⅰ、从左至右扫描,将 3 和 4 压入堆栈。
Ⅱ、遇到 + 运算符,弹出 4 和 3 (4 为栈顶元素,3为次栈顶元素),计算出 3 + 4 的结果, 为 7,再将 7 入栈。
Ⅲ、将 5 压入栈
Ⅳ、接下来进行 * 的运算,弹出 5 和 7 ,计算出得 7 * 5 = 35,再将 35 入栈。
Ⅴ、将6入栈
Ⅵ、最后时 - 运算符,计算出 35 - 6 的值,得到29,输出最终结果。(注意!此时注意减数和被减数的顺序!!!)
4、逆波兰计算器
需求如下
1)输入一个逆波兰表达式(后缀表达式),使用栈(Stack),计算结果
2)支持小括号和多位数整数,这里只做简易的计算,所以只用整数就行。
3)思路分析
4)完成代码
一个小时的头脑风暴下,我终于写了出来。方法不唯一,这里我只写了和上一段代码相似的代码。其实也可以用内置剪切方法 split()来写。
代码实现
package com.database.stack;
public class PolandNotation {
public static void main(String[] args) {
//首先定义一个计算公式
//(3+4)*5-6 => 3 4 + 5 * 6 -
String suffixExpression = "3 4 + 5 * 6 - ";
Poland poland = new Poland(suffixExpression.length());
int index = 0;//定义一个下标
int num1 = 0;
int num2 = 0;
int res = 0;
char ch = ' ';
String str = "";
while(true){
//跳出循环
if(index >= suffixExpression.length()){
break;
}
ch = suffixExpression.substring(index,index + 1).charAt(0);
if(ch == ' '){
index++;
continue;
}else{
if(!poland.isOper(ch)){
//插入数字
if(str == ""){
str += ch;
}
//判断如果下一个元素不是符号
if(!poland.isOper(suffixExpression.substring(index + 1,index + 2).charAt(0))){
//如果不是符号,就拼接到str后面
str += ch;
index++;
continue;
}
poland.push(Integer.parseInt(str));
str = "";
}else{
//如果是符号,就取出刚刚入栈的数,进行计算,然后再放入栈中
num1 = poland.pop();
num2 = poland.pop();
res = poland.cal(num1,num2,ch);
poland.push(res);
}
}
index++;
}
System.out.println("最终计算结果为:" + res);
}
}
class Poland{
private int maxSize;//栈的大小
private int[] stack;//定义一个数组,因为我们要用数组来实现栈。
private int top = -1;//初始状态下,将top置于数组的顶端。
//编写一个构造器,声明变量的同时就生成长度
public Poland(int maxSize){
this.maxSize = maxSize;
stack = new int[maxSize];//根据maxSize来生成一个数组
}
//判断栈满,因为栈是从下标为0开始的,所以栈满时正好是 maxSize - 1
public boolean isFull(){
return top == maxSize - 1;
}
//判栈空
public boolean isEmpty(){
return top == -1;
}
//入栈 push
public void push(int value){
if (isFull()){
System.out.println("栈是满的!");
return ;
}
top++;//top先自加再往里面填值
stack[top] = value;
}
//出站 pop
public int pop(){
int value = 0;
if(isEmpty()){
System.out.println("栈是空的!");
return 0;
}
value = stack[top];
top--;
return value;
}
//遍历栈的时候是从栈顶开始遍历的 栈顶的下标值为top
public void list(){
if(isEmpty()){
System.out.println("栈是空的!");
return ;
}
for(int i = top; i >= 0; i--){
System.out.println( stack[i] + " ");
}
}
//判断是个运算符还是一个数字
public boolean isOper(char val){
return val == '+' || val == '-' || val == '*' || val == '/' || val == ' ';
}
//一个计算方法
public int cal(int num1, int num2, char oper){
int res = 0;//定义一个res接收结果
switch (oper){
case '+':
res = num1 + num2;
break;
case '-'://注意顺序
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num1 / num2;
break;
default:
break;
}
return res;
}
}
运行结果
5、中缀表达式转后缀表达式
具体步骤
1)初始化两个栈:运算符栈s1和存储中间结果的栈s2
2)从左到右扫描中缀表达式
3)遇到数字,压入s2
4)遇到运算符,比较其与s1栈顶运算符的优先级
a、如果s1为空,或栈顶运算符为"(",则直接把此运算符入栈。
b、否则,如果优先级比栈顶运算符的高,也将此运算符入栈。
c、否则,将s1栈顶的运算符弹出,并压入到s2中,再次转到4-a中与s1中的栈顶运算符相比较。
5)如果遇到括号时:
a、如果时左括号“(”,则直接压入s1
b、如果时右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号丢弃
6)重复步骤2到5,直到表达式的最右边
7)将s1中剩余的运算符依次弹出并压入s2
8)依次弹出s2中的元素并输出,结果的逆序就是中缀表达式对应的后缀表达式
举例:1 + ( ( 2 + 3 ) * 4 ) - 5 ==> 1 2 3 + 4 * + 5 -
代码实现
package com.database.stack;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PolandNotation {
public static void main(String[] args) {
//完成将一个中缀表达式转后缀表达式的功能
//说明
//1、1+((2+3)*4)-5 => 1 2 3 + 4 * + 5 -
String infixExpriession = "1+((2+3)*4)-5";
InfixExpression s1 = new InfixExpression(infixExpriession.length());
InfixExpression s2 = new InfixExpression(infixExpriession.length());
int subscript = 0;
char element = ' ';
String number = "";
while(true){
//如果遍历到最后一个数,直接跳出循环
if(subscript >= infixExpriession.length()){
break;
}
element = infixExpriession.substring(subscript,subscript + 1).charAt(0);
if(s1.isOper(element)){
//如果element的值是一个运算符
//如果s1为空,或栈顶运算符为"(",则直接把此运算符入栈。
if(s1.isEmpty() || s1.peek() == '('){
s1.push(element);
}else if(s1.priority(element) > s1.priority(s1.peek())){
//否则,如果优先级比栈顶运算符的高,也将此运算符入栈。
s1.push(element);
}else{
//否则,将s1栈顶的运算符弹出,并压入到s2中,再次转到4-a中与s1中的栈顶运算符相比较。
s2.push(s1.pop());
continue;
}
}else if(s1.isBracket(element)){
//如果输入的是括号
if(element == '('){
//如果是左括号
s1.push(element);
}else{
//如果时右括号“)”,则依次弹出s1栈顶的运算符,并压入s2,直到遇到左括号为止,此时将这一对括号**丢弃**
while (true){
if(s1.peek() == '('){
s1.pop();
break;
}
s2.push(s1.pop());
}
}
}else{
//如果element的值是一个数字,压入s2
if(number == ""){
number += element;
}
//如果运行到最后一个字符的时候
if(subscript == infixExpriession.length() - 1){
s2.push(Integer.parseInt(number));
break;
}
if(!s2.isOper(infixExpriession.substring(subscript + 1,subscript +2).charAt(0))){
if(!s2.isBracket(infixExpriession.substring(subscript + 1,subscript +2).charAt(0))){
number += element;
subscript++;
continue;
}
}
s2.push(Integer.parseInt(number));
number = "";
//s2.push(element);
}
subscript++;
}
//将s1中剩余的运算符依次弹出并压入s2
while(true){
if(s1.isEmpty()){
break;
}
s2.push(s1.pop());
}
//定义一个数组
List<Object> list = new ArrayList<Object>();
while(true) {
if (s2.isEmpty()) {
break;
}
int temp = s2.pop();
if((temp + '0') < 60){
list.add((char)(temp + '0'));
continue;
}
list.add((char)temp);
}
//倒序输出
Collections.reverse(list);
System.out.println("生成的后缀表达式为:" + list);
//首先定义一个计算公式
//(3+4)*5-6 => 3 4 + 5 * 6 -
String suffixExpression = "3 4 + 5 * 6 - ";
Poland poland = new Poland(suffixExpression.length());
int index = 0;//定义一个下标
int num1 = 0;
int num2 = 0;
int res = 0;
char ch = ' ';
String str = "";
while(true){
//跳出循环
if(index >= suffixExpression.length()){
break;
}
ch = suffixExpression.substring(index,index + 1).charAt(0);
if(ch == ' '){
index++;
continue;
}else{
if(!poland.isOper(ch)){
//插入数字
if(str == ""){
str += ch;
}
//判断如果下一个元素不是符号
if(!poland.isOper(suffixExpression.substring(index + 1,index + 2).charAt(0))){
//如果不是符号,就拼接到str后面
str += ch;
index++;
continue;
}
poland.push(Integer.parseInt(str));
str = "";
}else{
//如果是符号,就取出刚刚入栈的数,进行计算,然后再放入栈中
num1 = poland.pop();
num2 = poland.pop();
res = poland.cal(num1,num2,ch);
poland.push(res);
}
}
index++;
}
System.out.println("最终计算结果为:" + res);
}
}
class InfixExpression{
private int maxSize;//栈的大小
private int[] stack;//定义一个数组,因为我们要用数组来实现栈。
private int top = -1;//初始状态下,将top置于数组的顶端。
//构造器
public InfixExpression(int maxSize){
this.maxSize= maxSize;
stack = new int[maxSize];
}
//判断栈满,因为栈是从下标为0开始的,所以栈满时正好是 maxSize - 1
public boolean isFull(){
return top == maxSize - 1;
}
//判栈空
public boolean isEmpty(){
return top == -1;
}
//怎加一个,返回栈顶的值的方法
public int peek(){
return stack[top];
}
//入栈 push
public void push(int value){
if (isFull()){
System.out.println("栈是满的!");
return ;
}
top++;//top先自加再往里面填值
stack[top] = value;
}
//出站 pop
public int pop(){
int value = 0;
if(isEmpty()){
System.out.println("栈是空的!");
return 0;
}
value = stack[top];
top--;
return value;
}
//判断运算符优先级的方法,数字越大,优先级越高
public int priority(int oper){
if(oper == '*' || oper == '/'){
return 1;
}else if(oper == '+' || oper == '-'){
return 0;
}else{
return -1;
}//目前假定只有 + - * / 这四个运算
}
//遍历栈的时候是从栈顶开始遍历的 栈顶的下标值为top
public void list(){
if(isEmpty()){
System.out.println("栈是空的!");
return ;
}
for(int i = top; i >= 0; i--){
System.out.println( stack[i] + " ");
}
}
//判断是个运算符还是一个数字
public boolean isOper(char val){
return val == '+' || val == '-' || val == '*' || val == '/';
}
//判断是否为括号
public boolean isBracket(char val){
return val == ')' || val == '(';
}
}
class Poland{
private int maxSize;//栈的大小
private int[] stack;//定义一个数组,因为我们要用数组来实现栈。
private int top = -1;//初始状态下,将top置于数组的顶端。
//编写一个构造器,声明变量的同时就生成长度
public Poland(int maxSize){
this.maxSize = maxSize;
stack = new int[maxSize];//根据maxSize来生成一个数组
}
//判断栈满,因为栈是从下标为0开始的,所以栈满时正好是 maxSize - 1
public boolean isFull(){
return top == maxSize - 1;
}
//判栈空
public boolean isEmpty(){
return top == -1;
}
//入栈 push
public void push(int value){
if (isFull()){
System.out.println("栈是满的!");
return ;
}
top++;//top先自加再往里面填值
stack[top] = value;
}
//出站 pop
public int pop(){
int value = 0;
if(isEmpty()){
System.out.println("栈是空的!");
return 0;
}
value = stack[top];
top--;
return value;
}
//遍历栈的时候是从栈顶开始遍历的 栈顶的下标值为top
public void list(){
if(isEmpty()){
System.out.println("栈是空的!");
return ;
}
for(int i = top; i >= 0; i--){
System.out.println( stack[i] + " ");
}
}
//判断是个运算符还是一个数字
public boolean isOper(char val){
return val == '+' || val == '-' || val == '*' || val == '/' || val == ' ';
}
//一个计算方法
public int cal(int num1, int num2, char oper){
int res = 0;//定义一个res接收结果
switch (oper){
case '+':
res = num1 + num2;
break;
case '-'://注意顺序
res = num2 - num1;
break;
case '*':
res = num1 * num2;
break;
case '/':
res = num1 / num2;
break;
default:
break;
}
return res;
}
}
这段代码是有毛病的,只能运算解析9以内的表达式。可以用转换数组的方式进行完善,这里主要是描述思路,请有空闲时间自行完成。