栈与栈的一些典型应用

栈的特点

  • 栈属于线性序列结构。可基于链表或者向量结构实现。
  • 栈中元素的操作次序始终遵循“后进先出”(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();
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值