zoukankan      html  css  js  c++  java
  • 线性结构02栈

    一)栈的定义

    堆栈(Stack)可以看成是一种“特殊的”线性表,这种线性表上的插入和删除运算限定在表的某一端进行的。

    • 通常称插入、删除的这一端为栈顶(Top),另一端称为栈底(Bottom)。
    • 当表中没有元素时称为空栈。
    • 栈为后进先出(Last In First Out)的线性表,简称为LIFO表。
    • 栈的修改是按后进先出的原则进行。每次删除(退栈)的总是当前栈中"最新"的元素,即最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能删除。

    二)栈的基本操作

    获取栈的状态(栈空、栈满、栈大小)、出栈、入栈。

    三)java中的栈

    java中的类Stack实现了栈的功能。其类结构如下图

    Vector是ArrayList的Synchronized实现,Stack继承了Vector,因此Stack是基于数组的顺序栈。

    主要方法:push、pop、peek

    public E push(E item) {
    	addElement(item);
    	return item;
    }
    
    public synchronized E pop() {
    	E obj;
    	int len = size();
    	obj = peek();
    	removeElementAt(len - 1);
    	return obj;
    }
    
    public synchronized E peek() {
    	int len = size();
    	if (len == 0)
    	    throw new EmptyStackException();
    	return elementAt(len - 1);
    }
    

    四)栈的应用

    4.1)表达式的计算

    这里有两个知识点:后缀表达式(逆波兰算法)、后缀表达式求值

    后缀表达式:不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则,如:(2 + 1) * 3 , 即2 1 + 3 *)。其优点就是不用再考虑运算符的优先级。

    将中缀表达式转换成后缀表达式,其算法如下:(算法为转载,出处不明!)

    private List<String> infixExpToPostExp(String exp) {// 将中缀表达式转化成为后缀表达式
    		List<String> postExp = new ArrayList<String>();// 存放转化的后缀表达式的链表
    		StringBuffer numBuffer = new StringBuffer();// 用来保存一个数的
    		Stack<Character> opStack = new Stack<Character>();// 操作符栈
    		char ch, preChar;
    		opStack.push('#');
    		try {
    			for (int i = 0; i < exp.length();) {
    				ch = exp.charAt(i);
    				switch (ch) {
    				case '+':
    				case '-':
    				case '*':
    				case '/':
    					preChar = opStack.peek();
    					// 如果栈里面的操作符优先级比当前的大,则把栈中优先级大的都添加到后缀表达式列表中
    					while (priority(preChar) >= priority(ch)) {
    						postExp.add("" + preChar);
    						opStack.pop();
    						preChar = opStack.peek();
    					}
    					opStack.push(ch);
    					i++;
    					break;
    				case '(':
    					// 左括号直接压栈
    					opStack.push(ch);
    					i++;
    					break;
    				case ')':
    					// 右括号则直接把栈中左括号前面的弹出,并加入后缀表达式链表中
    					char c = opStack.pop();
    					while (c != '(') {
    						postExp.add("" + c);
    						c = opStack.pop(); // 'c'是'('时不要添加到输出字符串中了
    					}
    					i++;
    					break;
    				// #号,代表表达式结束,可以直接把操作符栈中剩余的操作符全部弹出,并加入后缀表达式链表中
    				case '#':
    					char c1;
    					while (!opStack.isEmpty()) {
    						c1 = opStack.pop();
    						if (c1 != '#')
    							postExp.add("" + c1);
    					}
    					i++;
    					break;
    				// 过滤空白符
    				case ' ':
    				case '\t':
    					i++;
    					break;
    				// 数字则凑成一个整数,加入后缀表达式链表中
    				default:
    					if (Character.isDigit(ch)) {
    						while (Character.isDigit(ch)) {
    							numBuffer.append(ch);
    							ch = exp.charAt(++i);
    						}
    						postExp.add(numBuffer.toString());
    						numBuffer = new StringBuffer();
    					} else {
    						throw new IllegalExpressionException("illegal operator");
    					}
    				}
    			}
    		} catch (RuntimeException e) {
    			throw new IllegalExpressionException(e.getMessage());
    		}
    		return postExp;
    	}
    

      

    转成后缀表达式,求值就很容易了,遇到运算符就“弹出”两个运算量进行计算,将计算结果“压入”运算量栈,以此类推直至运算符栈为空。

    其算法如下:

    private double doEval(List<String> list) {
    		Stack<String> stack = new Stack<String>();
    		String element;
    		double n1, n2, result;
    		try {
    			for (int i = 0; i < list.size(); i++) {
    				element = list.get(i);
    				if (isOperator(element)) {
    					n1 = Double.parseDouble(stack.pop());
    					n2 = Double.parseDouble(stack.pop());
    					result = doOperate(n2, n1, element);
    					stack.push(new Double(result).toString());
    				} else {
    					stack.push(element);
    				}
    			}
    			return Double.parseDouble(stack.pop());
    		} catch (RuntimeException e) {
    			throw new IllegalExpressionException(e.getMessage());
    		}
    	}
    

      

    4.2)子程序嵌套调用

    4.3)递归

    递归方法思路:
    第一步骤(递归步骤):将规模较大的原问题分解为一个或多个规模更小、但具有类似于原问题特性的子问题。即较大的问题递归地用较小的子问题来描述,解原问题的方法同样可用来解这些子问题。
    第二步骤:确定一个或多个无须分解、可直接求解的最小子问题(称为递归的终止条件)。

    简单理解就是大问题化小问题,小问题有解。

  • 相关阅读:
    IDA 动态调试 ELF 文件
    双机调试环境搭建[win7+Windbg+VirtualKD]
    从域环境搭建到 MS14-068 提升为域权限
    栈溢出原理与 shellcode 开发
    Flask_0x05 大型程序结构
    rest-framework 框架的基本组件
    restful 规范
    Django的CBV和FBV
    Django-model 进阶
    Django-Ajax
  • 原文地址:https://www.cnblogs.com/huangfox/p/2565508.html
Copyright © 2011-2022 走看看