【数据结构和算法分析】栈与逆波兰式

栈ADT

:是限制插入和删除只能在一个位置上进行的表,该位置就是表的末端,或者栈的顶端(top)。对栈的基本操作有 push(进栈) 和 pop(出栈)。注意,对空栈进行的 pop 或 top 操作一般被认为是栈ADT的一个错误。另一方面,当运行 push 时空间用尽是一个实现限制,但不是ADT错误


栈的实现:由于栈是一个表,因此任何实现表的方法都能实现栈,所以在 java 中 ArrayList 和 LinkedList 都支持栈操作,99%的时间它们都是最合理的选择。栈的优势在于它的操作不仅是以常数时间运行,而且是以非常快的常数时间运行。即使是这样简单的结构与功能,却有着非常强大的作用

为了简单,用 ArrayList 实现一个具有基本功能的 MyStack

import java.util.ArrayList;


public class MyStack {

	private ArrayList stack = new ArrayList<>();
	
	public MyStack(){	
	}
	
	/*
	 * 进栈
	 */
	public void push(Object object){
	    stack.add(object);
	}
	/*
	 * 出栈
	 */
	public void pop(){
		if (stack.size() == 0) {
			throw new IndexOutOfBoundsException();
		}else{
		stack.remove(stack.size()-1);
		}
	}
	/*
	 * 返回栈顶元素
	 */
	public Object top(){
		if (stack.size() == 0) {
			throw new IndexOutOfBoundsException();
		}else{
		return stack.get(stack.size()-1);
		}
	}
	
    
	public int length(){
		return stack.size();
	}
	

} 
  

栈的应用

中缀表达式转后缀表达式

中缀表达式即标准形式的表达式。我们通过只允许操作+,-,*,/,(,),并坚持普通的优先级法则而将一般的问题浓缩成小规模的问题,比如,将中缀表达式 a + b * c + ( d * e + f ) * g  转换为后缀表达式就是 a b c * + d e * f + g * +

解法

1.遇到操作数,直接输出(添加到后缀表达式中)

2.栈为空时,遇到运算符,直接入栈

3.遇到左括号,将其入栈

4.遇到右括号,执行出栈操作,并将出栈的元素依次输出,直至弹出的是左括号,左括号不输出

5.遇到其他的运算符,加减乘除,依次弹出运算符优先级大于或者等于该运算符的的栈顶元素,然后将该运算符入栈

6.最终将栈内的元素依次出栈,输出


逆波兰式求值

逆波兰式,即后缀表达式

解法

从左往右扫描表达式,遇到数字时,将数字压入栈中,遇到运算符时,将栈顶的两个元素弹出,用运算符对它们进行相应的操作,并将结果入栈;重复上述步骤,到表达式扫描结束,最后运算得到的结果就是原表达式的结果。


附上代码:

import java.util.Stack;
import java.util.regex.Pattern;

public class PostFix {
	private static String infixToSuffix(String infix) {
		Stack stack = new Stack();
		String suffix = "";
		int length = infix.length();
		for (int i = 0; i < length; i++) {
			Character temp;
			char c = infix.charAt(i);
			switch (c) {
			// 忽略空格
			case ' ':
				break;

			// 碰到'(',push到栈
			case '(':
				stack.push(c);
				break;

			// 碰到'+''-',将栈中所有运算符弹出,送到输出队列中
			case '+':
			case '-':
				while (stack.size() != 0) {
					temp = stack.pop();
					if (temp == '(') {
						stack.push('(');
						break;
					}
					suffix += " " + temp;
				}
				stack.push(c);
				suffix += " ";
				break;

			// 碰到'*''/',将栈中所有乘除运算符弹出,送到输出队列中
			case '*':
			case '/':
				while (stack.size() != 0) {
					temp = stack.pop();
					if (temp == '(' || temp == '+' || temp == '-') {
						stack.push(temp);
						break;
					} else {
						suffix += " " + temp;
					}
				}
				stack.push(c);
				suffix += " ";
				break;

			// 碰到右括号,将靠近栈顶的第一个左括号上面的运算符全部依次弹出,送至输出队列后,再丢弃左括号
			case ')':
				while (stack.size() != 0) {
					temp = stack.pop();
					if (temp == '(')
						break;
					else
						suffix += " " + temp;
				}
				break;

			default:
				suffix += c;
			}
		}

		while (stack.size() != 0)
			suffix += " " + stack.pop();
		return suffix;
	}
	
	// 通过后缀表达式求出算术结果
	private static double suffixToArithmetic(String postfix) {
		Pattern pattern = Pattern.compile("\\d+||(\\d+\\.\\d+)"); // 匹配数字
		String strings[] = postfix.split(" ");
		for (int i = 0; i < strings.length; i++)
			strings[i].trim();
		Stack stack = new Stack();
		for (int i = 0; i < strings.length; i++) {
			if (strings[i].equals(""))
				continue;
			if ((pattern.matcher(strings[i])).matches()) {
				stack.push(Double.parseDouble(strings[i]));
			} else {
				double y = stack.pop();
				double x = stack.pop();
				stack.push(caculate(x, y, strings[i]));
			}
		}
		return stack.pop();
	}

	private static double caculate(double x, double y, String simble) {
		if (simble.trim().equals("+"))
			return x + y;
		if (simble.trim().equals("-"))
			return x - y;
		if (simble.trim().equals("*"))
			return x * y;
		if (simble.trim().equals("/"))
			return x / y;
		return 0;
	}

	public static void main(String args[]) {
		System.out.println(infixToSuffix("2+(5-2*2)*3"));
		System.out.println(suffixToArithmetic(infixToSuffix("2+(5-2*2)*3")));
	}
	
}


你可能感兴趣的:(数据结构和算法分析)