栈与栈的一些典型应用
来源:互联网 发布:淘宝联盟鹊桥手机登录 编辑:程序博客网 时间:2024/06/06 02:03
栈的特点
- 栈属于线性序列结构。可基于链表或者向量结构实现。
- 栈中元素的操作次序始终遵循“后进先出”(last-in-first-out)
/** *基于链表实现栈 */ import java.util.LinkedList;public class StackBaseLinkedList<E> { private LinkedList<E> storage = null; public StackBaseLinkedList() { storage = new LinkedList<E>(); } public int size() { return storage.size(); } public boolean empty() { return storage.isEmpty(); } /** * LinkedList中于栈相关的源码可以看出Java官方的栈底是first,栈顶是last。 * 将对象压入栈 * */ public void push(E e) { storage.addFirst(e); } /** * 返回并删除栈顶对象 * */ public E pop() { return storage.removeFirst(); } /** * 引用栈顶对象但是不删除 * */ public E top() { return storage.getFirst(); } }
函数调用栈
在windos等大部分操作系统中,每个运行中的二进制程序都配有一个调用栈(call stack)和执行栈(execution stack)。借助调用栈可以跟踪属于同一程序的所有函数(书P88图)。
调用栈的基本单位是帧(frame)。每次函数调用时,都会相应地创建一帧,记录该函数实例在二进制程序中的返回地址(return address),以及局部变量、传入参数等,并将该帧压入调用栈。函数一旦运行完毕,对应的帧随即弹出,运行控制权将被交还给函数的上层调用函数,并按照该帧中记录的返回地址确定在二进制程序中继续执行的位置。
位于调用栈栈底的那帧必然对应于入口主函数main(),若它从调用栈中弹出,则意味着整个程序的运行结束,此后控制权将交还给操作系统。
栈的典型应用
- 栈擅长解决的典型问题具有以下共同特征
- 有明确的算法,但其解答却以线性序列的形式给出
- 无论是递归还是迭代实现,该序列都是依逆序计算输出
* 输入和输出规模不确定,难以事先确定盛放输出数据的容器大小
进制转换
/** * 十进制转换为任意进制 * */ public void convert(int e, int base) { final char[] digit = {'0','1','2','3','4','5','6','7','8' ,'9','A','B','C','D','F'}; StackBaseLinkedList<Character> sbll = new StackBaseLinkedList<Character>(); StringBuffer sb = new StringBuffer(); int remainder; while(e > 0) { remainder = e % base; sbll.push(digit[remainder]); e = e / base; } while(!sbll.empty()) { sb.append(sbll.pop()); } System.out.print(sb.toString()); }
括号匹配(栈混洗)
当遇到{ [ ( 时,将其压进栈
当遇见} ] ) 时,或栈顶元素不为null并且是与其匹配的左半部分,则将栈顶元素pop。否则括号不匹配。
/** * 括号匹配,能匹配的括号有()[]{}; * */ public boolean paren(String s) { StackBaseLinkedList<Character> sbll = new StackBaseLinkedList<Character>(); for(int lo = 0 ; lo < s.length() ; lo++) { switch(s.charAt(lo)) { case '(' : case '[' : case '{' : sbll.push(s.charAt(lo)); break; //当存在右括号而栈为空时,直接返回false case ')' : if(sbll.empty() || '(' != sbll.pop()) return false; break; case ']' : if(sbll.empty() || '[' != sbll.pop()) return false; break; case '}' : if(sbll.empty() || '{' != sbll.pop()) return false; break; } } return sbll.empty(); }
中缀表达式求值
将运算表达式的运算符与运算数分别压入两个不同的栈optr与opnd。
根据oprt栈顶元素与当前操作符查表得到优先级次序’<’,’=’,’>’。
运算优先级’<’:当前运算符优先级别更高,不能进行运算。将其压入栈optr
运算优先级’=’:当前运算符优先级相等,只存在于左右括号的匹配和为了方便算法处理引入的左右哨兵匹配。
运算优先级’>’:当前运算符优先级较低,执行栈顶运算符的运算
//运算优先级表 private final char[][] pri = { /* |----------------当前运算符----------------|*/ /* + - * / ^ ! ( ) \0 */ /*-- +*/ {'>','>','<','<','<','<','<','>','>'}, /*| -*/ {'>','>','<','<','<','<','<','>','>'}, /*栈 **/ {'>','>','>','>','<','<','<','>','>'}, /*顶 /*/ {'>','>','>','>','<','<','<','>','>'}, /*运 ^*/ {'>','>','>','>','>','<','<','>','>'}, /*算 !*/ {'>','>','>','>','>','>',' ','>','>'}, /*符 (*/ {'<','<','<','<','<','<','<','=',' '}, /*| )*/ {' ',' ',' ',' ',' ',' ',' ',' ',' '}, /*-- \0*/ {'<','<','<','<','<','<','<',' ','='} };
/** * 中缀表达式求值,并产生逆波兰式RPN * */ public int evaluate(String s,ArrayList<String> RPN) { //用来存放运算数的栈 StackBaseLinkedList<Integer> opnd = new StackBaseLinkedList<Integer>(); //用来存放运算符的栈 StackBaseLinkedList<Character> optr = new StackBaseLinkedList<Character>(); char op ; int p0opnd,p1opnd,lo = 0; //添加右哨兵,方便算法。否则需要额外处理数组脚标越界问题 s = s + "\0"; //添加左哨兵,方便算法 optr.push('\0'); //循环条件有问题,最后一位是运算符号时会少算一次// for(int lo = 0 ; lo < s.length() ;) { while(!optr.empty()) { if(isDigit(s.charAt(lo))) { lo = readNumber(s,lo,opnd); append(RPN,opnd.top()); } else { switch(orderBetween(optr.top(),s.charAt(lo))) { case '<' : optr.push(s.charAt(lo)); lo++; break; //只有左右括号或者表达式结束才会出现'=' case '=' : optr.pop(); lo++; break; //栈顶运算符优先于当前运算符,先处理栈顶运算符 case '>' : op = optr.pop(); append(RPN,op); if(op == '!') { //一元运算符 p0opnd = opnd.pop(); opnd.push(calcu(op,p0opnd)); } else { //二元运算符 p0opnd = opnd.pop(); p1opnd = opnd.pop(); opnd.push(calcu(op,p0opnd,p1opnd)); } break; default : System.exit(1); } } } return opnd.pop(); } private boolean isDigit(char c) { // (int)'0' = 48 , (int)'9' = 57 if((int)c < 58 && (int)c > 47) { return true; } else { return false; } } private char orderBetween(char op1,char op2) { return pri[oprtRank(op1)][oprtRank(op2)]; } private int oprtRank(char op) { switch(op) { case '+' : return 0; case '-' : return 1; case '*' : return 2; case '/' : return 3; case '^' : return 4; case '!' : return 5; case '(' : return 6; case ')' : return 7; case '\0': return 8; } return -1; } private int readNumber(String s,int lo,StackBaseLinkedList<Integer> opnd) { int temp = 0, i = lo; opnd.push((int)s.charAt(i) - 48); while((++i < s.length()) && isDigit(s.charAt(i))) { temp = opnd.pop(); temp = temp * 10 + ((int)s.charAt(i) - 48); opnd.push(temp); } return i; } public int calcu(char op,int p0pnd) { // 0的阶乘等于1 if(p0pnd == 0 ) { return 1; } for(int i = p0pnd - 1 ; i > 0 ; i--) { p0pnd = p0pnd * i; } return p0pnd; } private int calcu(char op,int p0opnd,int p1opnd) { int j = p1opnd; switch(op) { case '+' : return p1opnd + p0opnd; case '-' : return p1opnd - p0opnd; case '*' : return p1opnd * p0opnd; case '/' : return p1opnd / p0opnd; case '^' : for(int i = 0 ; i < p0opnd - 1 ; i++) { p1opnd = p1opnd * j; } break; } return p1opnd; } } private void append(ArrayList<String> RPN,int opnd) { RPN.add(Integer.toString(opnd)); } private void append(ArrayList<String> RPN,char op) { RPN.add(Character.toString(op)); }
逆波兰式求值
/** * RPN式求值 * 这里RPN表达式不能用StringBuffer,StringBuffer无法分辨个位数与多位数。 * */ public int rpnEvaluation(ArrayList<String> RPN) { StackBaseLinkedList<Integer> s = new StackBaseLinkedList<Integer>(); int p0opnd,p1opnd,lo = 0; char op; while(lo < RPN.size()) { op = RPN.get(lo).charAt(0); if(Character.isDigit(op)) { s.push(Integer.parseInt(RPN.get(lo))); lo++; } else { if(op == '!') { //一元运算符 p0opnd = s.pop(); s.push(calcu(op,p0opnd)); } else { //二元运算符 p0opnd = s.pop(); p1opnd = s.pop(); s.push(calcu(op,p0opnd,p1opnd)); } lo++; } } return s.pop(); }
阅读全文
0 0
- 栈与栈的一些典型应用
- 递归的一些典型应用
- 【数据结构】栈的两个典型应用
- 嵌入式测控与通讯的典型应用
- JAVA迷宫算法&进制转换---栈的典型应用
- 数据结构:栈的典型应用之一:行编辑(C++)
- 栈的典型应用 —— 逆序输出
- 栈的典型应用 —— 嵌套递归
- 栈的典型应用 —— 括号匹配
- 栈的典型应用 —— 延迟缓冲
- 与字符串有关的一些典型问题的C++解法
- 信号灯的典型应用
- zookeeper的典型应用
- 接口的典型应用
- 信息化的典型应用
- 互联网的典型应用
- 构造函数与析构函数的典型应用
- 项目经理的一些典型要求
- 学习笔记-链表查询优化
- java中面向对象思想和三大特性的理解
- 查找程序的bug
- spring aop学习10:Aop的坑(Aop对于内部调用无效)
- 如何让自己成为一个优秀的Java架构师,而不是码农
- 栈与栈的一些典型应用
- javascript判断是否为数组的方法
- UE4-UI设计之UMG
- retain和release 的使用
- js调用扫描仪twain进行网页图像扫描
- linux0.11源码阅读笔记1-启动流程-bootsect.s
- C/C++中产生随机数函数(rand,srand)的用法
- Web 框架的要素
- 深入理解python中的浅拷贝和深拷贝