惟大英雄能本色,是真名士自风流
——易中天(百家讲坛)
1.表达式的转换
1.1 中缀表达式转前缀表达式
中缀表达式转前缀表达式有许多的方式,有加括号去除法、语法树遍历法、堆栈处理法1.
测试程序的实现:
package ds.java.ch03;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
/**
* @author LbZhang
* @version 创建时间:2015年11月15日 下午7:14:35
* @description 中缀转化为前缀
*
* 运算符的优先级越低位置越靠前
*/
public class NifixToPrefix {
public static void main(String[] args) {
String expression = "4*3/(5-2)+6";
System.out.println(expression);
char[] temp = expression.toCharArray();
// for (int i = temp.length - 1; i >= 0; i--) {
// System.out.print(temp[i] + " ");
// }
// System.out.println("算法的实现:");
//System.out.println();
// /算法实现
// 1.初始化两个栈
// ///寄存算符
Stack<Character> OPTR = new Stack<Character>();
// ///中缀转化为前缀
Stack<Character> Result = new Stack<Character>();
// 2.对存储在字符串Inf ix 中的算术表达式从右左检查每个字符;
for (int i = temp.length - 1; i >= 0; i--) {
// 3.如果是空格直接略过
Character ct = temp[i];
if (ct == ' ') {
continue;
}
// 4.若是操作数, 直接存入RESULT 栈;
if (isOperatorNum(ct)) {
Result.push(ct);
continue;
}
// 5.若为") ", 直接存入OPTR 栈;
if (ct == ')') {
OPTR.push(ct);
continue;
}
/*
* 6.若为'(',循环弹出OPTR栈中算符, 并存入RESULT栈,直到取出')'为止,')'出栈并丢弃;
*/
if (ct == '(') {
Character cc;
while ((cc = OPTR.pop()) != ')') {
Result.push(cc);
}
continue;
}
/*
* 7.若为算符, 则: ①若OPTR 栈为空, 则存入OPTR 栈; ②若该算符较栈顶算符优先级高或相等, 存入OPTR 栈;
* ③否则循环弹出OPTR栈中算符,并存入RESULT 栈, 直至遇到优先级相等的算符
*/
if (isOperator(ct)) {
if (OPTR.isEmpty()) {
OPTR.push(ct);
continue;
} else if (levelOf(ct) >= levelOf(OPTR.peek())) {
OPTR.push(ct);
continue;
} else {
Character cc = OPTR.peek();
while (levelOf(cc) > levelOf(ct) && (!OPTR.isEmpty())) {
Result.push(OPTR.pop());
}
OPTR.push(ct);
}
continue;
}
/*
* 8. 若当前表达式尚未扫描完毕, 跳转到步骤(3) 继续执行;
*/
}
//System.out.println("??"+Result);
/*
* 9.若表达式读取完成后OPTR栈中尚有运算符, 则依次弹出, 并存入RESU LT 栈, 直至栈空;
*/
while (!OPTR.isEmpty()) {
Result.push(OPTR.pop());
}
/*
* 10.RESULT 栈中元素依次出栈并存入字符串prefix 中, 并向prefix 中写入字符串结束符 ,
* 这样字符串prefix中存放的就是转换得到的前缀表达式。
*/
StringBuilder sb = new StringBuilder();
while(!Result.isEmpty()){
sb.append(Result.pop());
}
String re = sb.toString();
System.out.println("前缀表达式:"+re);
}
/**
* 计算当前的算符的优先级
* @param op
* @return
*/
private static int levelOf(Character op) {
int level;
switch (op) {
case '+':
case '-':
level = 1;
break;
case '*':
case '/':
level = 2;
break;
default:
level = 0;
break;
}
return level;
}
/**
* 判断输入串中的字符是不是数字,如果是返回true
*
* @param op
* @return
*/
private static boolean isOperator(Character op) {
if (op == '+' || op == '-' || op == '*' || op == '/')
return true;
else
return false;
}
/**
* 判断输入串中的字符是不是操作符,如果是返回true
*
* @param op
* @return
*/
private static boolean isOperatorNum(Character op) {
if (op >= 48 && op <= 57) {// /0-9的ascii码
return true;
} else {
return false;
}
}
}
1.2 中缀表达式转后缀表达式
中缀表达式转后缀表达式有许多的方式,有加括号去除法、语法树遍历法、堆栈处理法2.
package ds.java.ch03;
import java.util.Stack;
/**
* @author LbZhang
* @version 创建时间:2015年11月15日 下午7:14:53
* @description 类说明
*/
public class NifixToPostfix {
public static void main(String[] args) {
String expression = "4*3/(5-2)+6";
System.out.println(expression);
char[] temp = expression.toCharArray();
// /算法实现
// 1.初始化两个栈
// ///寄存算符
Stack<Character> OPTR = new Stack<Character>();
// ///中缀转化为后缀
Stack<Character> Result = new Stack<Character>();
// 2.对存储在字符串Inf ix 中的算术表达式从左向右检查每个字符;
for (int i = 0; i <temp.length; i++) {
// 3.如果是空格直接略过
Character ct = temp[i];
//System.out.println(ct);
if (ct == ' ') {
continue;
}
// 4.若是操作数, 直接存入RESULT 栈;
if (isOperatorNum(ct)) {
Result.push(ct);
continue;
}
// 5.若为"( ", 直接存入OPTR 栈;
if (ct == '(') {
OPTR.push(ct);
continue;
}
/*
* 6.若为')',循环弹出OPTR栈中算符, 并存入RESULT栈,直到取出')'为止,')'出栈并丢弃;
*/
if (ct == ')') {
Character cc;
while ((cc = OPTR.pop()) != '(') {
Result.push(cc);
}
continue;
}
/*
* 7.若为算符, 则: ①若OPTR 栈为空, 则存入OPTR 栈; ②若该算符较栈顶算符优先级高或相等, 存入OPTR 栈;
* ③否则循环弹出OPTR栈中算符,并存入RESULT 栈, 直至遇到优先级相等的算符
*/
if (isOperator(ct)) {
if (OPTR.isEmpty()) {
OPTR.push(ct);
continue;
} else if (levelOf(ct) >= levelOf(OPTR.peek())) {
OPTR.push(ct);
continue;
} else {
Character cc = OPTR.peek();
while (levelOf(cc) > levelOf(ct) && (!OPTR.isEmpty())) {
Result.push(OPTR.pop());
}
OPTR.push(ct);
}
continue;
}
/*
* 8. 若当前表达式尚未扫描完毕, 跳转到步骤(3) 继续执行;
*/
}
//System.out.println("??"+Result);
/*
* 9.若表达式读取完成后OPTR栈中尚有运算符, 则依次弹出, 并存入RESULT 栈, 直至栈空;
*/
while (!OPTR.isEmpty()) {
Result.push(OPTR.pop());
}
/*
* 10.RESULT 栈中元素依次出栈并存入字符串
* 然后将字符串逆置
*/
System.out.println(Result);
StringBuilder sb = new StringBuilder();
while(!Result.isEmpty()){
sb.append(Result.pop());
}
String re = sb.toString();
char[] array = re.toCharArray();
String reResult="";
for(int i=array.length-1;i>=0;i--){
reResult+=array[i];
}
System.out.println("后缀表达式:"+reResult);
/*
* 另外一种翻转字符串的方法
*/
// StringBuffer bu = new StringBuffer(re).reverse();
// System.out.println(bu);
}
/**
* 计算当前的算符的优先级
*
* @param op
* @return
*/
private static int levelOf(Character op) {
int level;
switch (op) {
case '+':
case '-':
level = 1;
break;
case '*':
case '/':
level = 2;
break;
default:
level = 0;
break;
}
return level;
}
/**
* 判断输入串中的字符是不是数字,如果是返回true
*
* @param op
* @return
*/
private static boolean isOperator(Character op) {
if (op == '+' || op == '-' || op == '*' || op == '/')
return true;
else
return false;
}
/**
* 判断输入串中的字符是不是操作符,如果是返回true
*
* @param op
* @return
*/
private static boolean isOperatorNum(Character op) {
if (op >= 48 && op <= 57) {// /0-9的ascii码
return true;
} else {
return false;
}
}
}
1.3 前缀表达式与后缀表达式比较
- 前缀表达式:也被称为波兰表示法,其特点是将操作符置于操作数之前。后缀表达式:又被称为逆波兰法,其特点是将操作符置于操作数之后。
- 二者都需要两个栈作为辅助空间。
- 前缀表达式在求解的时候和后缀表达式不一样,前缀表达式在求解扫描的时候是从右侧开始的和后缀表达式截然相反。
2.表达式的求解
2.1 前缀表达式求解
package ds.java.ch03;
import java.util.Scanner;
import java.util.Stack;
/**
* @author LbZhang
* @version 创建时间:2015年11月14日 下午10:17:07
* @description 类说明
*/
public class PrefixEvaluator {
private final static char ADD = '+';
private final static char SUB = '-';
private final static char MUL = '*';
private final static char DIV = '/';
private Stack<Integer> stack;
public PrefixEvaluator() {
stack = new Stack<Integer>();
}
/**
* 找到最右边的操作符(即最先运算的操作符),取其右边的两个操作数进行运算,然后找右边第二个操作符,以此类推。
* @param expression
* @return
*/
public int evaluate(String expression) {
int op1, op2, result = 0;
String token;
Scanner parser = new Scanner(expression);
while (parser.hasNext()) {
token = parser.next();
if (isOperator(token)) {
op1 = (stack.pop()).intValue();
op2 = (stack.pop()).intValue();
result = evaluateSingleOperator(token.charAt(0), op1, op2);
stack.push(result);
} else {
stack.push(new Integer(Integer.parseInt(token)));
}
}
return result;
}
private int evaluateSingleOperator(char operation, int op1, int op2) {
int result = 0;
switch (operation) {
case ADD:
result = op1 + op2;
break;
case SUB:
result = op1 - op2;
break;
case MUL:
result = op1 * op2;
break;
case DIV:
result = op1 / op2;
break;
}
return result;
}
/**
* 判断是否是运算符
*
* @param token
* @return
*/
private boolean isOperator(String token) {
return (token.equals("+") || token.equals("-") || token.equals("*") || token
.equals("/"));
}
}
2.2 后缀表达式求解
package ds.java.ch03;
import java.util.Scanner;
import java.util.Stack;
/**
* @author LbZhang
* @version 创建时间:2015年11月14日 下午10:17:07
* @description 类说明
*/
public class PostfixEvaluator {
private final static char ADD = '+';
private final static char SUB = '-';
private final static char MUL = '*';
private final static char DIV = '/';
private Stack<Integer> stack;
public PostfixEvaluator() {
stack = new Stack<Integer>();
}
public int evaluate(String expression) {
int op1, op2, result = 0;
String token;
Scanner parser = new Scanner(expression);
while (parser.hasNext()) {
token = parser.next();
//System.out.println(token);
if (isOperator(token)) {
op2 = (stack.pop()).intValue();
op1 = (stack.pop()).intValue();
result = evaluateSingleOperator(token.charAt(0), op1, op2);
stack.push(result);
} else {
stack.push(new Integer(Integer.parseInt(token)));
}
}
return result;
}
private int evaluateSingleOperator(char operation, int op1, int op2) {
int result = 0;
switch (operation) {
case ADD:
result = op1 + op2;
break;
case SUB:
result = op1 - op2;
break;
case MUL:
result = op1 * op2;
break;
case DIV:
result = op1 / op2;
break;
}
return result;
}
/**
* 判断是否是运算符
*
* @param token
* @return
*/
private boolean isOperator(String token) {
return (token.equals("+") || token.equals("-") || token.equals("*") || token
.equals("/"));
}
}
2.3 main测试类
package ds.java.ch03;
import java.util.Scanner;
/**
* @author LbZhang
* @version 创建时间:2015年11月14日 下午4:09:56
* @description 后缀实现
*/
public class Tester {
public static void main(String[] args) {
//前缀测试
int preresult;
// /获取输入流中的内容
/**
* 后缀表达式计算
*/
//中缀表达式 4*3/(5-2)+6
PrefixEvaluator preEvaluator = new PrefixEvaluator();
String prefix = " + / * 4 3 - 5 2 6";
System.out.println("前缀表达式:"+prefix);
// 计算结果存储
StringBuffer prefixb = new StringBuffer(prefix).reverse();
preresult = preEvaluator.evaluate(prefixb.toString());
System.out.println();
System.out.println("前缀表达式的结果: " + preresult);
System.out.println("----------------------分隔线------------------------");
////后缀测试
int postresult;
// /获取输入流中的内容
/**
* 后缀表达式计算
*/
//中缀表达式 4*3/(5-2)+6
PostfixEvaluator postEvaluator = new PostfixEvaluator();
String postfix = "4 3 5 2 - / * 6 +";
System.out.println("后缀表达式:"+postfix);
// 计算结果存储
postresult = postEvaluator.evaluate(postfix);
System.out.println();
System.out.println("后缀表达式的结果: " + postresult);
}
}
3.链式结构
链式结构是一种数据结构,它使用对象引用变量来创建对象之间的链接。链式结构是基于数据集合实现的主要替代方案。链表由一些对象构成,其中每个对象指向链表中的下一个对象3。
- 访问链表:从头结点开始不断的遍历
- 插入结点:处理第一个结点需要注意,引入哨兵结点
- 删除结点:处理第一个结点需要注意,引入哨兵结点
4.迷宫求解
栈的另一种经典的使用是,跟踪穿越迷宫的各种可能性,或者其他包含尝试和错误的类似算法。
下面实现一个穿越迷宫基于栈的解决方案:
4.1 Maze.java初始化迷宫矩阵
package ds.java.ch03;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
/**
* @author LbZhang
* @version 创建时间:2015年11月16日 上午11:31:34
* @description 迷宫类
*/
public class Maze {
private static final int TRIED = 2;
private static final int PATH = 3;
private int numRows, numColumns;// 矩阵的行 、 列
private int[][] grid = {
{ 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0 },
{ 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1 },
{ 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1 },
{ 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1 },
{ 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
public Maze() {
this.numRows = grid.length;
this.numColumns = grid[0].length;
}
public Maze(String filename) throws FileNotFoundException{
Scanner scan = new Scanner(new File(filename));
this.numRows = scan.nextInt();
this.numColumns = scan.nextInt();
grid = new int[numRows][numColumns];
for(int i=0;i<this.numRows;i++){
for(int j = 0;j<this.numColumns;j++){
grid[i][j] = scan.nextInt();
}
}
}
public void tryPosition(int row,int col){
grid[row][col] = TRIED;
}
public void makePath(int row,int col){
grid[row][col] = PATH;
}
public boolean valid (int row, int column)
{
boolean result = false;
if (row >= 0 && row < grid.length &&
column >= 0 && column < grid[row].length)
if (grid[row][column] == 1)
result = true;
return result;
}
public String toString ()
{
String result = "
";
for (int row=0; row < grid.length; row++)
{
for (int column=0; column < grid[row].length; column++)
result += grid[row][column] + "";
result += "
";
}
return result;
}
public int getRows(){
return this.numRows;
}
public int getColumns(){
return this.numColumns;
}
}
4.2 MazeSlover.java回溯法
package ds.java.ch03;
import ds.java.ch03.stackImpl.LinkedStack;
import ds.java.ch03.stackImpl.StackADT;
/**
* @author LbZhang
* @version 创建时间:2015年11月16日 下午2:49:38
* @description 迷宫路径发现的封装类
*/
public class MazeSlover {
private Maze maze;
/*
* 构造方法
*/
public MazeSlover(Maze maze) {
this.maze = maze;
}
/**
* 遍历方法
*
* @return
*/
public boolean traverse() {
boolean done = false;
Position pos = new Position();
Object dispose;
StackADT<Position> stack = new LinkedStack<Position>();
// /路径栈 想办法实现
stack.push(pos);
while (!(done) && (!stack.isEmpty())) {
pos = stack.pop();
maze.tryPosition(pos.getX(), pos.getY());
if (pos.getX() == maze.getRows() - 1
&& pos.getY() == maze.getColumns() - 1)
done = true; // the maze is solved
else {
stack = push_new_pos(pos.getX() - 1, pos.getY(), stack);
stack = push_new_pos(pos.getX() + 1, pos.getY(), stack);
stack = push_new_pos(pos.getX(), pos.getY() - 1, stack);
stack = push_new_pos(pos.getX(), pos.getY() + 1, stack);
}
}
return done;
}
/**
* 使用回溯法 来对迷宫进行求解
* 可使用回溯方法,即从入口出发,顺着某一个方向进行探索,若能走通,则继续往前进;
* 否则沿着原路退回,换一个方向继续探索,直至出口位置,求得一条通路。假如所有可能
* 的通路都探索到而未能到达出口,则所设定的迷宫没有通路。
*
* @return
*/
public String findPathForMaze() {
boolean done = false;
//路径栈的声明
StackADT<Position> stack = new LinkedStack<Position>();
Position pos = new Position();
stack.push(pos);
maze.makePath(pos.getX(), pos.getY());
while(!(done) && (!stack.isEmpty())){
pos = stack.peek();///获取当前的坐标
maze.tryPosition(pos.getX(), pos.getY());///已经走过
if (pos.getX() == maze.getRows() - 1
&& pos.getY() == maze.getColumns() - 1){
done = true; // the maze is solved
break;
}
if(maze.valid(pos.getX(), pos.getY()+1)){
Position p = new Position(pos.getX(), pos.getY()+1);
stack.push(p);
}else if(maze.valid(pos.getX(), pos.getY()-1)){
Position p = new Position(pos.getX(), pos.getY()-1);
stack.push(p);
}else if(maze.valid(pos.getX()+1, pos.getY())){
Position p = new Position(pos.getX()+1, pos.getY());
stack.push(p);
}else if(maze.valid(pos.getX()-1, pos.getY())){
Position p = new Position(pos.getX()-1, pos.getY());
stack.push(p);
}else{
//四个方向都不存在有效的下一坐标那么就出栈
stack.pop();
if(stack.isEmpty()){
break;
}
}
}
if(stack.isEmpty()){
System.out.println("No way");
}else{
System.out.println("OK ");
}
Position pp;
StringBuffer sb = new StringBuffer();
while(!stack.isEmpty()){
pp=stack.pop();
sb.append(pp+"<-");
//路径栈的相关的坐标修改标志数据
maze.makePath(pp.getX(), pp.getY());
}
System.out.println(maze);
return sb.toString().substring(0, sb.toString().length()-2);
}
private StackADT<Position> push_new_pos(int x, int y,
StackADT<Position> stack) {
Position npos = new Position();
npos.setX(x);
npos.setY(y);
if (maze.valid(x, y)) {
stack.push(npos);
}
return stack;
}
}
4.3 MazeTester.java测试主类
package ds.java.ch03;
/**
* @author LbZhang
* @version 创建时间:2015年11月16日 下午2:49:53
* @description 类说明
*/
public class MazeTester {
public static void main(String[] args) {
System.out.println("-----------**---迷宫遍历的路径---**-------------");
Maze maze = new Maze();
System.out.println(maze);
System.out.println("-------------------原始的迷宫矩阵----------------");
MazeSlover ms = new MazeSlover(maze);
System.out.println(ms.findPathForMaze());
}
}
4.4 Position.java辅助类
package ds.java.ch03;
/**
* @author LbZhang
* @version 创建时间:2015年11月16日 下午2:45:16
* @description
* 坐标类的设计
*/
public class Position {
private int x;
private int y;
public Position(){
x=0;
y=0;
}
public Position(int x, int y){
this.x=x;
this.y=y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "["+this.x+","+this.y+"]";
}
}
- 曹晓丽. 将中缀表达式转换为前缀表达式的三种方法。 ↩
- 严晶晶张涛. 将中缀表达式转换成后缀表达式的三种
方法. 计算机与网络. ↩ - 参照单链表的Java实现。 ↩