(二)堆栈的定义和实现

1.堆栈的定义

堆栈(Stack)可以认为是具有一定约束的线性表, 插入和删除操作都作用在一个称为栈顶 ( T o p ) 的端点位置 \color{red}{插入和删除操作都作用在一个称为栈顶(Top)的端点位置} 插入和删除操作都作用在一个称为栈顶(Top)的端点位置
其实,我们日常生活中也可以看到堆栈的例子,比如,我们厨房中叠放的盘子,使用盘子(删除操作)时我们是从顶端拿走盘子,用完放回(插入操作)时也是放到顶端。
正是堆栈所具有的这种特性,通常把数据插人称为压入栈(Push),而数据删除可看作从堆栈中取出数据,叫做弹出栈(Pop)。
也正是由于这一特性,最后入栈的数据将被最先弹出,所以堆栈也被称为后入先出(Last In First Out,LIFO)表。

堆栈的抽象数据类型定义为:
类型名称:堆栈(Stack)。
数据对象集:一个有0个或多个元素的有穷线性表。
操作集:对于一个具体的长度为正整数MaxSize的堆栈S∈Stack,记堆栈中的任一元素X∈ElementType ,堆栈的基本操作主要有:
(1) Stack CreateStack( int MaxSize):生成空堆栈,其最大长度为MaxSize;
(2) bool IsFull( Stack S):判断堆栈S是否已满。若S中元素个数等于MaxSize时返回true; 否则返回false;
(3) bool Push( Stack s, ElementType X):将元素X压人堆栈。若堆栈已满,返回false;否则将数据元素X插入到堆栈S栈顶处并返回true;
(4)bool IsEmpty(StackS):判断堆栈S是否为空,若是返回true;否则返回false。
(5)Element TypePop(StackS):删除并返回栈顶元素。若堆栈为空,返回错误信息;否则将栈顶数据元素从堆栈中删除并返回。

2.堆栈的顺序存储实现

2.1 入栈

设我们要在堆栈中插入ai的元素,只需要将栈顶上移再将新元素入栈即可。
(二)堆栈的定义和实现_第1张图片

2.2 出栈

设我们要将堆栈中ai的元素出栈,只需要取出元素后将栈顶下级一位即可。
(二)堆栈的定义和实现_第2张图片

2.3 堆栈的顺序存储实现

Java实现



import java.lang.reflect.Array;

/**
 * 堆栈顺序存储实现
 */
public class LinList03 {
    public static void main(String[] args) {
        System.out.println("LinList03 -> staring...");
        test();
        System.out.println("LinList03 -> end");
    }

    public static void test() {
        SNode s = new SNode(Integer.class, 100);
        s.push(1);
        s.push(2);
        s.push(3);
        System.out.println(s.pop());
        System.out.println(s.pop());
        System.out.println(s.pop());
        System.out.println(s.pop());
    }
}


/**
 * 堆栈顺序存储实现
 */
class SNode {
    private T[] _data;

    public T[] get_data() {
        return _data;
    }


    private int _top;

    public int get_top() {
        return _top;
    }


    private int _maxSize;

    public int get_maxSize() {
        return _maxSize;
    }

    public SNode(Class clazz, int maxSize) {
        _maxSize = maxSize;
        _top = -1;
        _data = (T[]) Array.newInstance(clazz, _maxSize);
    }

    public boolean isFull() {
        return _top == _maxSize;
    }

    public boolean isEmpty() {
        return _top == -1;
    }

    public boolean push(T x) {
        if (isFull()) {
            System.out.println("堆栈满");
            return false;
        } else {
            _data[++_top] = x;
            return true;
        }
    }

    public T pop() {
        if (isEmpty()) {
            System.out.println("堆栈空");
            return null;
        } else {
            return _data[_top--];
        }
    }

}

3.堆栈的顺序存储实现

3.1 入栈

设我们要将元素x入栈,只需要在链表的头部加入一个新元素,并修改头指针即可。

(二)堆栈的定义和实现_第3张图片

3.2 出栈

出栈先将元素取出再修改头指针即可。
(二)堆栈的定义和实现_第4张图片

3.3 代码实现

Java实现


/**
 * 堆栈链式存储实现
 */
public class LinList04 {
    public static void main(String[] args) {
        System.out.println("LinList04 -> staring...");
        test();
        System.out.println("LinList04 -> end");
    }

    public static void test() {
        LinkStack s = new LinkStack();
        s.push(1);
        s.push(2);
        s.push(3);
        System.out.println(s.pop());
        System.out.println(s.pop());
        System.out.println(s.pop());
        System.out.println(s.pop());
    }
}

class LinkNode {
    private T _data;

    public T get_data() {
        return _data;
    }

    public void set_data(T _data) {
        this._data = _data;
    }

    private LinkNode _next;

    public LinkNode get_next() {
        return _next;
    }

    public void set_next(LinkNode _next) {
        this._next = _next;
    }
}

class LinkStack {
    private LinkNode _head;

    public LinkNode get_head() {
        return _head;
    }

    public void set_head(LinkNode _head) {
        this._head = _head;
    }

    public boolean isEmpty() {
        return _head == null;
    }

    public boolean push(T element) {
        LinkNode newNode = new LinkNode();
        newNode.set_data(element);
        newNode.set_next(_head);
        _head = newNode;

        return true;
    }

    public T pop() {
        T topElement;
        LinkNode tmp;

        if (isEmpty()) {
            System.out.println("堆栈空");
            return null;
        } else {
            tmp = _head;
            topElement = tmp.get_data();
            _head = tmp.get_next();
            tmp.set_next(null);
            return topElement;
        }
    }

}

3 堆栈的应用:后缀表达式求值

后缀表达式的求值方法
四则运算器

中缀表达式

(3+4/2) * 5-10

后缀表达式,以空格隔开每个数据项

3 4 2 / + 5 * 10 -

1)如果是运算数则入栈;

2)如果是操作符则将栈顶的两个元素出栈进行运算,并将运算的结果压回栈顶;

3)直到最后一个结果压进栈顶,后缀表达式求值完成,栈顶的元素就是表达式最终的结果。

Java实现


import com.sun.deploy.util.StringUtils;

import java.util.Scanner;

/**
 * 后缀表达式求值
 */
public class LinList05 {
    public static void main(String[] args) {
        System.out.println("LinList05 -> staring...");
        try {
            test();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("LinList05 -> end");
    }

    public static void test() throws Exception {
        double result;
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入后缀表达式,不同数据项请以空格隔开:");
        String str = scanner.nextLine();
        String[] expArr = StringUtils.splitString(str, " ");
        //var expr = "3 4 2 / + 5 * 10 -".Split(new char[] { ' ' });
        result = new Calculator().postfixExp(expArr);
        System.out.println(String.format("运算结果为:%f", result));
    }

}

class Calculator {
    // 操作序列最大长度
    public static final int MAX_OP = 100;

    public boolean isDigit(String str) {
        boolean isDigit = false;
        boolean isHaveDot = false;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            isDigit = ((c - '0' >= 0)
                    && (c - '0' <= 9))
            ;

            if (i > 0 && !isDigit && !isHaveDot) {
                isHaveDot = isDigit = c == '.';
            }
            if (!isDigit) {
                break;
            }
        }


        return isDigit;
    }

    public double postfixExp(String[] expr) throws Exception {
        if (expr == null || expr.length == 0)
            throw new Exception("请输入后缀表达式");


        SNode s;
        CharTypeEnum typeEnum;
        double opt1, opt2;

        s = new SNode(Double.class, MAX_OP);
        opt1 = 0;
        opt2 = 0;

        // 读取整个后缀表达式
        for (int i = 0; i < expr.length; i++) {
            String item = expr[i];
            typeEnum = isDigit(item) ? CharTypeEnum.Num : CharTypeEnum.Opr;

            if (typeEnum == CharTypeEnum.Num)
                s.push(Double.parseDouble(item));
            else {
                opt2 = s.pop();
                opt1 = s.pop();
                switch (item) {
                    case "+":
                        s.push(opt1 + opt2);
                        break;
                    case "-":
                        s.push(opt1 - opt2);
                        break;
                    case "*":
                        s.push(opt1 * opt2);
                        break;
                    case "/":

                        if (opt2 == 0)
                            throw new Exception("除数不能为零");

                        s.push(opt1 / opt2);
                        break;
                    default:
                        throw new Exception(String.format("未知运算符%s", item));
                }
            }
        }

        double result = s.pop();

        return result;
    }

}

enum CharTypeEnum {
    /**
     * 运算数
     */
    Num,
    /**
     * 运算符
     */
    Opr
}

4 堆栈的应用:中缀表达式转换为后缀表达式

4.1 概念

中缀表达式是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。

后缀表达式,又称逆波兰式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)

4.2 思路

中缀表达式

(3+4/2)*5-10

后缀表达式,以空格隔开每个数据项

3 4 2 / + 5 * 10 -

1)如果是左括号入栈;

2)如果运算数则拼接到后缀表达式末尾;

3)如果是运算符和栈内运算符比较优先级,将栈内运算符优先高的全部出栈;

4)左括号或空栈停止出栈操作;

5)如果是右括号则将栈内符号出栈,直到碰到左括号;

6)入栈当前运算符回到第一步,直到栈为空

示例:

2*(9+6/3-5)+4

2 9 6 3 /

(

/

4.3 实现

程序中的堆栈采用数组存储的方式来实现。
程序内元素
// 堆栈实现(char类型)
// 字符类型(运算符、左括号、右括号)
// 字符优先级
// 输入字符串 输出字符串

Java实现

package org.alfred.linearlist_03;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

/**
 * 求后缀表达式
 */
public class LinList06 {
    public static void main(String[] args) {
        System.out.println("LinList06 -> staring...");
        try {
            test();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("LinList06 -> end");
    }

    public static void test() throws Exception {
        String result;
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入中缀表达式:");
        String exp = scanner.nextLine();
        result = new Expression().getPostfixExp(exp);
        System.out.println(String.format("该表达式对应的后缀表达式为:%s", result));
    }
}

class Expression {

    private final static int MAX_ELEMENT = 100;

    public String getPostfixExp(String exp) throws Exception {
        StringBuilder result = new StringBuilder();
        // 拆分中缀表达式
        List list = new ArrayList<>();
        for (int i = 0; i < exp.length(); i++) {
            ExpInfo expInfo = new ExpInfo(exp, i);
            list.add(getItemInfo(expInfo));
            i = expInfo.getStart();
        }

        // 转换中缀表达式
        SNode s = new SNode<>(ItemInfo.class, MAX_ELEMENT);

        for (ItemInfo item : list) {
            switch (item.getType()) {
                case Number:
                    result.append(" ").append(item.getValue());
                    break;
                case Left:
                    s.push(item);
                    break;
                case Right:
                    while (!s.isEmpty()) {
                        ItemInfo tmp = s.pop();
                        if (tmp.getType() == CharType.Left)
                            break;
                        result.append(" ").append(tmp.getValue());
                    }
                    break;
                case Plus:
                case Subtract:
                case Multi:
                case Divide:
                    while (!s.isEmpty()
                            && s.getTopElement().getType() != CharType.Left) {
                        ItemInfo tmp = s.getTopElement();
                        if (item.getType().getValue() / 10 > tmp.getType().getValue() / 10)
                            break;
                        // 栈内运算符大于或等于当前运算符优先级
                        tmp = s.pop();
                        result.append(" ").append(tmp.getValue());
                    }
                    // 当前运算符入栈
                    s.push(item);
                    break;
                default:
                    throw new Exception(String.format("未知类型%s", item.getType().toString()));
            }
        } // end for

        // 栈内剩余运算符出栈
        while (!s.isEmpty()) {
            ItemInfo tmp = s.pop();
            result.append(" ").append(tmp.getValue());
        }


        return result.toString();
    }

    private ItemInfo getItemInfo(ExpInfo expInfo) throws Exception {
        ItemInfo result = new ItemInfo();
        int start = expInfo.getStart();
        String exp = expInfo.getExp();

        while (start < exp.length()) {
            char c = exp.charAt(start);
            if (c == ' ') {
                start++;
                continue;
            }
            CharType type = getCType(c);
            if (type != CharType.Number) {
                if (result.getType() == null) {
                    result.setValue(String.valueOf(c));
                    result.setType(type);
                } else
                    // 装填完本项后还原指针
                    start--;
                break;
            } else {
                result.setType(type);
                result.setValue(result.getValue() + c);
                start++;
            }
        }

        expInfo.setStart(start);

        return result;
    }

    private CharType getCType(char c) throws Exception {
        switch (c) {
            case '+':
                return CharType.Plus;

            case '-':
                return CharType.Subtract;

            case '*':
                return CharType.Multi;

            case '/':
                return CharType.Divide;

            case '(':
                return CharType.Left;

            case ')':
                return CharType.Right;

            default:
                boolean isDigit = ((c - '0' >= 0) && (c - '0' <= 9))
                        || c == '.';
                if (!isDigit)
                    throw new Exception(String.format("错误的字符%c", c));
                return CharType.Number;
        }
    }
}

enum CharType {
    /**
     * 数字
     */
    Number(0),
    /**
     * 运算符:加
     */
    Plus(11),
    /**
     * 运算符:加
     */
    Subtract(12),
    /**
     * 运算符:乘
     */
    Multi(21),
    /**
     * 运算符:除
     */
    Divide(22),
    /**
     * 左括号
     */
    Left(101),
    /**
     * 右括号
     */
    Right(102);

    private final int value;

    private CharType(int value) {
        this.value = value;
    }


    public CharType valueOf(int value) {
        switch (value) {
            case 0:
                return CharType.Number;
            case 11:
                return CharType.Plus;
            case 12:
                return CharType.Subtract;
            case 21:
                return CharType.Multi;
            case 22:
                return CharType.Divide;
            case 101:
                return CharType.Left;
            case 102:
                return CharType.Right;
            default:
                return null;
        }
    }

    public int getValue() {
        return value;
    }
}

class ItemInfo {
    public ItemInfo() {
        type = null;
        value = "";
    }

    private CharType type;
    private String value;

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public CharType getType() {
        return type;
    }

    public void setType(CharType type) {
        this.type = type;
    }
}

class ExpInfo {
    private String exp;
    private int start;

    public String getExp() {
        return exp;
    }

    public void setExp(String exp) {
        this.exp = exp;
    }

    public int getStart() {
        return start;
    }

    public void setStart(int start) {
        this.start = start;
    }

    public ExpInfo(String exp, int start) {
        this.exp = exp;
        this.start = start;
    }
}

你可能感兴趣的:(数据结构与算法,线性表,java,jvm,算法)