堆栈(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):删除并返回栈顶元素。若堆栈为空,返回错误信息;否则将栈顶数据元素从堆栈中删除并返回。
设我们要在堆栈中插入ai的元素,只需要将栈顶上移再将新元素入栈即可。
设我们要将堆栈中ai的元素出栈,只需要取出元素后将栈顶下级一位即可。
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--];
}
}
}
设我们要将元素x入栈,只需要在链表的头部加入一个新元素,并修改头指针即可。
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+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
}
中缀表达式是一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间(例:3 + 4),中缀表达式是人们常用的算术表示方法。
后缀表达式,又称逆波兰式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)
中缀表达式
(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 /
(
/
程序中的堆栈采用数组存储的方式来实现。
程序内元素
// 堆栈实现(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;
}
}