栈与栈的一些典型应用

来源:互联网 发布:淘宝联盟鹊桥手机登录 编辑:程序博客网 时间: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();    }