(二)堆栈的定义和实现

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的元素,只需要将栈顶上移再将新元素入栈即可。
在这里插入图片描述

2.2 出栈

设我们要将堆栈中ai的元素出栈,只需要取出元素后将栈顶下级一位即可。
在这里插入图片描述

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<Integer> s = new SNode<Integer>(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<T> {
    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<T> 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.2 出栈

出栈先将元素取出再修改头指针即可。
在这里插入图片描述

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<Integer> s = new LinkStack<Integer>();
        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<T> {
    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<T> {
    private LinkNode<T> _head;

    public LinkNode<T> get_head() {
        return _head;
    }

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

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

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

        return true;
    }

    public T pop() {
        T topElement;
        LinkNode<T> 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<Double> s;
        CharTypeEnum typeEnum;
        double opt1, opt2;

        s = new SNode<Double>(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<ItemInfo> 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<ItemInfo> 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;
    }
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,这是一个经典的问题,我来给你讲讲如何用堆栈和队列实现计算器仿真。 首先,我们需要定义一个结构体来表示计算器的操作数和操作符,可以定义如下: ```c typedef struct { int type; // 0表示操作数,1表示操作符 int value; // 操作数的值 char operator; // 操作符 } Element; ``` 然后,我们可以定义两个数组来分别表示堆栈和队列,如下: ```c Element stack[100]; int top = -1; Element queue[100]; int front = 0, rear = 0; ``` 接下来,我们需要实现入栈、出栈、入队、出队等基本操作。具体代码如下: ```c // 入栈 void push(Element e) { top++; stack[top] = e; } // 出栈 Element pop() { if (top == -1) { printf("Stack is empty!\n"); exit(0); } Element e = stack[top]; top--; return e; } // 入队 void enqueue(Element e) { queue[rear] = e; rear++; } // 出队 Element dequeue() { if (front == rear) { printf("Queue is empty!\n"); exit(0); } Element e = queue[front]; front++; return e; } ``` 接下来,我们可以实现一个函数来读取用户输入的表达式并将其转换成操作数和操作符的序列。具体代码如下: ```c void readExpression() { char input[100]; printf("Please enter an expression:\n"); scanf("%s", input); int i = 0; while (input[i] != '\0') { if (isdigit(input[i])) { // 如果是数字,将其转换成操作数 int value = input[i] - '0'; i++; while (isdigit(input[i])) { value = value * 10 + input[i] - '0'; i++; } Element e; e.type = 0; // 0表示操作数 e.value = value; enqueue(e); } else if (input[i] == '+' || input[i] == '-' || input[i] == '*' || input[i] == '/') { // 如果是操作符,直接入队 Element e; e.type = 1; // 1表示操作符 e.operator = input[i]; enqueue(e); i++; } else { // 如果是其它字符,忽略 i++; } } } ``` 最后,我们可以实现一个函数来计算表达式的值,具体代码如下: ```c int calculate() { while (front < rear) { Element e = dequeue(); if (e.type == 0) { // 如果是操作数,入栈 push(e); } else { // 如果是操作符,从栈中取出两个操作数进行计算,将结果入栈 Element e1 = pop(); Element e2 = pop(); int value; switch (e.operator) { case '+': value = e2.value + e1.value; break; case '-': value = e2.value - e1.value; break; case '*': value = e2.value * e1.value; break; case '/': value = e2.value / e1.value; break; default: printf("Invalid operator!\n"); exit(0); } Element e3; e3.type = 0; e3.value = value; push(e3); } } Element e = pop(); if (top != -1) { printf("Invalid expression!\n"); exit(0); } return e.value; } ``` 到这里,我们就完成了用堆栈和队列实现计算器仿真的代码。你可以尝试输入一些表达式进行测试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值