栈的实现,栈在算术表达式计算中的应用及Java中栈的源码分析

来源:互联网 发布:生化危机mac版 编辑:程序博客网 时间:2024/06/08 04:32

栈的定义

栈是限定仅在一段进行插入和删除的线性表。虽然这个限制减少了栈的灵活性,但也使得栈更有效,更容易实现。栈也被叫做LIFO线性表(即后进先出表),习惯上称栈的可访问元素为栈顶元素,元素的插入称为入栈(push),元素的删除称为出栈(pop)。栈可分为顺序栈和链式栈,顺序栈底层是一个数组,栈顶元素就是数组尾部元素;而链式栈是以链表为基础。

栈的链式实现

栈的链式实现实质是对于链表的简化实现,由于栈只需要在一端进行删除和插入操作,因此只需要一个头部节点指向栈顶元素即可。栈的实现如下

import java.util.EmptyStackException;public class Stack<T> {    //定义链表节点    static class Node<T> {        //使用泛型        T t;        Node<T> next;        private Node(T t) {            this.t = t;            next = null;        }    }    //栈顶元素    private Node<T> top;    //栈中元素个数    private int size;    public Stack() {        size = 0;    }    public boolean isEmpty() {        return size > 0 ? false : true;    }    //入栈操作    public void push(T t) {        //如果栈是空的话,需要对top进行初始化操作        if (size == 0) {            top = new Node<T>(t);            size++;            return;        }        //定义一个tmp临时变量指向旧的栈顶元素        Node<T> tmp = top;        top = new Node<T>(t);        top.next = tmp;        size++;    }    //弹出栈顶元素并返回该元素    public T pop() {        if (size == 0) {            throw new EmptyStackException();        }        T tmp = top.t;        top = top.next;        size--;        return tmp;    }    //返回栈顶元素但不弹出    public T peek() {        //如果栈中无元素则抛出异常        if (size == 0) {            throw new EmptyStackException();        }        return top.t;    }}

算术表达式的计算

平时我们所接触的算术表达式从形式上看都是中缀表达式,这是一种我们容易接受的形式,但这种形式对于计算机来说就不是那么一回事了。通常我们都需要将中缀表达式转为后缀或前缀表达式然后再进行求值。下面我以中缀转后缀为例。

中缀表达式转后缀表达式

这个过程需要用到两个,一个是运算符栈,另一个是后缀表达式栈(用于存放结果),具体操作如下
1. 从左往右扫描中缀表达式
2. 如果遇到运算符(+,-,*,/),如果运算符栈为空则直接将运算符压人,如果不为空将该运算符和运算符栈中的栈顶元素进行优先级比较,如果该运算符优先级较大,那么直接将该运算符压人运算符栈;否则,依次从运算符栈中弹出栈顶元素并压人后缀表达式栈,直到运算符栈栈顶元素优先级小于我们扫描到的元素。(其实也就一句话,保证运算符栈的优先级有序,使栈顶元素优先级最大)
3. 如果遇到“(”,那么直接压人运算符栈
4. 如果遇到“)”,那么需要弹出运算符栈中的元素并压入后缀表达式栈直到遇到“(”,然后将“(”弹出
5. “(”只有“)”能使它弹出,其他运算符均不能使其弹出
6. 如果遇到数字直接压人后缀表达式栈
7. 扫描完毕时依次将运算符栈中的符号弹出压人后缀表达式栈

这里举个例子来说明该过程(栈顶元素在最右边)
假设现在有一个算术表达式为:(4-3)*2+2/2

运算符栈 后缀表达式栈 字符 ( ( ( 4 4 (- 4 - (- 43 3 43- ) * 43- * 43-2 2 + 43-2* + + 43-2*2 2 +/ 43-2*2 / +/ 43-2*22 2 43-2*22/+

代码实现

public class InfixToPostfixConverter {    public String convert(char[] infix) {        // 运算符栈,栈顶元素优先级始终最高,'('>'*'='/'>'+'>'-'        Stack<Character> stack1 = new Stack<>();        // 后缀表达式栈        Stack<String> stack2 = new Stack<>();        StringBuilder result = new StringBuilder();        //用来记录数字个数        int count = 0;、        //为了支持多位数计算,用一个变量来记录上一个运算符的下标        int operatorPos = 0;        //将operatorPos赋值为第一个数字的前一位        for(int i=0;i<infix.length;i++){            if(isNum(infix[i])){                operatorPos = i-1;                break;            }        }        for (int i = 0; i < infix.length; i++) {            if (isOperator(infix[i])) {                //如果扫描到一个运算符并且数字记录个数不为0                //那么从上一个运算符的下标的下一位到当前运算符的count个字符即为数字                if (count > 0) {                    StringBuilder tmp = new StringBuilder();                    tmp.append(new String(infix, operatorPos + 1, count));                    stack2.push(tmp.reverse().toString());                    count = 0;                }                operatorPos = i;                //左括号的情况                if (infix[i] == '(') {                    stack1.push(infix[i]);                } else if (infix[i] == '*' || infix[i] == '/') {                    while (!stack1.isEmpty() && (stack1.peek() == '*' || stack1.peek() == '/')) {                        stack2.push(stack1.pop() + "");                    }                    stack1.push(infix[i]);                } else if (infix[i] == '+' || infix[i] == '-') {                    while (!stack1.isEmpty() && (stack1.peek() == '*' || stack1.peek() == '/' || stack1.peek() == '+'                            || stack1.peek() == '-')) {                        stack2.push(stack1.pop() + "");                    }                    stack1.push(infix[i]);                } else if (infix[i] == ')') {                    while (!stack1.isEmpty() && stack1.peek() != '(') {                        stack2.push(stack1.pop() + "");                    }                    stack1.pop();                }            } else if (isNum(infix[i])) {                count++;            }        }        //扫描结束后,如果count大于0的话,需要将最后的数字取出放入stack2中        if (count > 0) {            stack2.push(new String(infix, operatorPos + 1, count));            stack2.push(stack1.pop() + "");        }        //将stack1中的元素一次弹出压人stack2中        while(!stack1.isEmpty()){            stack2.push(stack1.pop()+"");        }        while (!stack2.isEmpty()) {            result.append(stack2.pop());            result.append(',');        }        //最后需要将result反转返回        return result.reverse().toString();    }    private boolean isOperator(char c) {        return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';    }    private boolean isNum(char c) {        return c >= '0' && c <= '9' ||c=='.';    }}

利用后缀表达式进行计算

当我们将中缀表达式转化为后缀表达式时,可以利用一个栈完成计算过程,具体过程如下
1. 从左往右扫描后缀表达式
2. 遇到数字直接压人栈中
3. 遇到运算符则从栈中弹出出两个数根据该运算符进行计算并将结果压回栈中
4. 扫描结束后,从栈顶弹出的元素就是我们的计算结果了
利用上面得到的后缀表达式(43-2*22/+)举个实例

数字栈 字符 4 4 3 3 1 - 2 2 2 * 22 2 222 2 21 / 3 +

实现代码及一个测试样例

import java.util.Scanner;import java.text.DecimalFormat;public class PostfixEvaluator {    public int evaluate(char[] postfix){        StringBuilder sb = new StringBuilder();        for(int i=0;i<postfix.length;i++){            sb.append(postfix[i]);        }        String result = sb.toString();        String[] strings = result.split(",");        Stack<Integer> stack = new Stack<>();        int n1,n2;        for(int i=0;i<strings.length;i++){            if(isANum(strings[i])){                stack.push(Integer.parseInt(strings[i]));            }            else if(strings[i].equals("+")){                n2 = stack.pop();                n1 = stack.pop();                stack.push(n1+n2);            }            else if(strings[i].equals("-")){                n2 = stack.pop();                n1 = stack.pop();                stack.push(n1-n2);            }            else if(strings[i].equals("*")){                n2 = stack.pop();                n1 = stack.pop();                stack.push(n1*n2);            }            else if(strings[i].equals("/")){                n2 = stack.pop();                n1 = stack.pop();                stack.push(n1/n2);            }        }        return stack.pop();    }    private boolean isANum(String s){        try{            int n = Integer.parseInt(s);        }        catch(Exception e){            return false;        }        return true;    }    public static void main(String[] args){        //将结果保留三位小数        DecimalFormat format = new DecimalFormat("#.000");        System.out.print("Input the infix expression:");        Scanner scanner = new Scanner(System.in);        String line = scanner.nextLine();        while(!line.equals("Q")&&!line.equals("q")){            InfixToPostfixConverter converter = new InfixToPostfixConverter();            String postfix = converter.convert(line.toCharArray());            String[] result = postfix.split(",");            System.out.print("Postfix expression:");            for(String s:result){                System.out.print(s);            }            System.out.println();            PostfixEvaluator evaluator = new PostfixEvaluator();            System.out.print("Final value:");            System.out.println(format.format(evaluator.evaluate(postfix.toCharArray())));            System.out.print("Input the infix expression:");            line = scanner.nextLine();        }    }}

Java中栈的源码分析

那么Java中是如何实现栈这个数据结构的呢?

public class Stack<E> extends Vector<E> {    public Stack() {    }    //直接在数组尾部加入元素    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);    }    //调用父类方法size()获得元素个数    public boolean empty() {        return size() == 0;    }    //查找某个Object    public synchronized int search(Object o) {        int i = lastIndexOf(o);        if (i >= 0) {            return size() - i;        }        return -1;    }    private static final long serialVersionUID = 1224463164541339165L;}

从上面源码可以看到,Stack是直接继承Vector这个类的(我们知道Vector的底层是一个数组),可见Java中的栈是一个顺序栈,它是以数组作为基础的,其中的search(Object)可以在栈中查找某个元素的位置,它是通过调用父类Vector方法lastIndexOf(Object)获取查找元素在Vector中的下标位置,然后返回size()-1,如果找不到则返回-1。
源码下载地址:http://download.csdn.net/detail/mynameis121/9806031

0 0
原创粉丝点击