1,栈基本介绍
- 栈是一种先入后出的有序列表
- 栈限制线性表中元素的插入和删除只能在线性表的同一端进行的一种特殊线性表。允许插入和删除的一端,为变化的一端,称为栈顶,另一端为固定的一端,称为栈底
- 出栈(
pop
)入栈(push
)原理图如下:
2,栈模拟代码演示
2.1,数组模拟
package com.self.datastructure.stack;
import lombok.Data;
public class ArrayStackDemo {
public static void main(String[] args) {
ArrayStack arrayStack = new ArrayStack(10);
for (int i = 0; i < 10; i++, arrayStack.push(i));
arrayStack.showDetails();
}
@Data
private static class ArrayStack {
private int count;
private int capacity;
private int[] stack;
private static final int DEFAULT_LENGTH = 10;
public ArrayStack() {
this(DEFAULT_LENGTH);
}
public ArrayStack(int length) {
this.capacity = length;
this.stack = new int[length];
}
public void push(int data) {
if (isFull()) {
throw new IndexOutOfBoundsException("数组已满...");
}
stack[count++] = data;
}
public int pop() {
if (isEmpty()) {
throw new IndexOutOfBoundsException("数组为空...");
}
return stack[--count];
}
public boolean isFull() {
return count == capacity;
}
public boolean isEmpty() {
return count == 0;
}
public void showDetails() {
int tempCount = count;
for (;tempCount > 0;) {
System.out.println(stack[--tempCount]);
}
}
}
}
2.2,链表模拟
package com.self.datastructure.stack;
import lombok.Data;
public class LinkedStackDemo {
public static void main(String[] args) {
LinkedStack linkedStack = new LinkedStack();
for (int i = 0; i < 10; i++, linkedStack.push(i));
for (int i = 0; i < 10; i++)
System.out.println(linkedStack.pop().getData());
}
private static class LinkedStack {
private Node head;
public void push(int data) {
Node newNode = new Node(data);
if (null == head) {
head = newNode;
} else {
Node temp = head;
for (;null != temp.getNext(); temp = temp.getNext());
temp.setNext(newNode);
}
}
public Node pop() {
if (isEmpty()) {
throw new IndexOutOfBoundsException("链表为空...");
}
Node temp = head;
if (null == head.getNext()) {
head = null;
return temp;
} else {
for (temp = head; temp.getNext().getNext() != null; temp = temp.getNext());
Node resultData = temp.getNext();
temp.setNext(null);
return resultData;
}
}
public boolean isEmpty() {
return head == null;
}
}
@Data
private static class Node {
private int data;
private Node next;
public Node(int data) {
this(data, null);
}
public Node(Node node) {
this(node.getData(), null);
}
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
}
}
3,栈模拟计算器_中缀表达式
3.1,基本思路
- 定义两个栈对象进行数据存储,分别为数字栈对象和符号栈对象
- 从左至右对数据依次入栈, 对应数据入对应栈
- 对操作服务进行优先级排序, 排序优先级为
( > / > * > - > +
- 符号栈数据入栈时, 判断栈顶数据优先级是否大于当前优先级
- 如果小于或者等于当前优先级, 则数据依次入栈
- 如果大于当前优先级, 则先从符号栈弹出一个符号, 并从数字栈弹出两个数字进行计算, 计算完成后, 数字入栈并继续该操作直到符合优先级小于
- 对于带括号数据, 直接拎出括号部分进行计算完成后并入栈
- 表达式遍历完成后, 依次弹出栈元素进行计算
- 全部基于整数操作,未考虑小数
3.2,代码实现
package com.self.datastructure.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class MiddleCalculateDemo {
public static void main(String[] args) {
System.out.println("最终结果: " + calculate("4-(3+(2-1))"));;
}
public static int calculate(String permission) {
String[] permissionArray = transformPermission(permission);
Stack<Integer> numberStack = new Stack<>();
Stack<String> operateStack = new Stack<>();
for (int index = 0; index < permissionArray.length; index++) {
try {
String element = permissionArray[index];
if (element.matches("^[+-]?[0-9]+$")) {
numberStack.push(Integer.valueOf(element));
continue;
} else if (element.matches("^[+\\-*/]$")) {
dealOperatePriority(numberStack, operateStack, element);
continue;
} else if (element.matches("^[(]$")) {
int endIndex = findMatchRightBracket(permissionArray, index);
String newPermission = combineBracketPermission(permissionArray, index, endIndex);
int bracketResult = calculate(newPermission);
numberStack.push(bracketResult);
index = endIndex;
}
} catch (Exception e) {
System.out.println("发生错误: index: " + index);
e.printStackTrace();
}
}
return doCalculate(numberStack, operateStack);
}
private static void dealOperatePriority(Stack<Integer> numberStack, Stack<String> operateStack, String operate) {
if (operateStack.isEmpty()) {
operateStack.push(operate);
return;
}
String preOperate = operateStack.peek();
if (higgerPriority(operate, preOperate)) {
operateStack.push(operate);
} else {
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
operateStack.pop();
Integer result = doCalculate(number1, number2, preOperate);
numberStack.push(result);
dealOperatePriority(numberStack, operateStack, operate);
}
}
private static boolean higgerPriority(String operate, String preOperate) {
int priorityCount = getPriorityCount(operate);
int prePriorityCount = getPriorityCount(preOperate);
return priorityCount >= prePriorityCount;
}
private static int getPriorityCount(String operate) {
int priorityCount = 0;
switch (operate) {
case "+": priorityCount = 1;
break;
case "-": priorityCount = 2;
break;
case "*": priorityCount = 3;
break;
case "/": priorityCount = 4;
break;
}
return priorityCount;
}
private static int doCalculate(Stack<Integer> numberStack, Stack<String> operateStack) {
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
String operate = operateStack.pop();
Integer result = doCalculate(number1, number2, operate);
numberStack.push(result);
if (!numberStack.isEmpty() && !operateStack.isEmpty()) {
result = doCalculate(numberStack, operateStack);
}
return result;
}
private static Integer doCalculate(Integer number1, Integer number2, String operate) {
switch (operate) {
case "+" :
return number1 + number2;
case "-" :
return number2 - number1;
case "*" :
return number1 * number2;
case "/" :
return number2 / number1;
default:
throw new RuntimeException("运算符无效...");
}
}
private static String combineBracketPermission(String[] permissionArray, int index, int endIndex) {
StringBuffer sb = new StringBuffer();
for (index = index + 1; index < endIndex; index++) {
sb.append(permissionArray[index]);
}
return sb.toString();
}
private static int findMatchRightBracket(String[] permissionArray, int currIndex) {
int matchingIndex = 0;
for (currIndex = currIndex + 1; currIndex < permissionArray.length; currIndex++) {
if (")".equals(permissionArray[currIndex])) {
matchingIndex = currIndex;
}
}
return matchingIndex;
}
private static String[] transformPermission(String permission) {
List<String> lstPer = new ArrayList<>(10);
char[] perArray = permission.toCharArray();
StringBuffer sb = new StringBuffer();
boolean isFirst = true;
for (char data : perArray) {
if ((data >= '0' && data <= '9') || (String.valueOf(data).matches("^[+-]$") && isFirst)) {
sb.append(data);
isFirst = false;
} else {
if (0 != sb.length()) {
lstPer.add(sb.toString());
sb.setLength(0);
}
lstPer.add(String.valueOf(data));
if (String.valueOf(data).equals("(")) {
isFirst = true;
}
}
}
if (0 != sb.length()) {
lstPer.add(sb.toString());
}
System.out.println("表达式转数组后: " + lstPer);
String[] permissionAarray = new String[lstPer.size()];
for (int i = 0; i < lstPer.size(); i++) {
permissionAarray[i] = lstPer.get(i);
}
return permissionAarray;
}
}
4,中缀表达式转后缀表达式
4.1,转换规则
- 初始化两个栈,运算符栈
s1
和数据元素栈s2
- 从左至右扫描中缀表达式
- 遇到数据元素时,直接压入到
s2
栈 - 遇到运算符时,比较其与
s1
栈顶运算符的优先级
- 如果
s1
栈为空,或者栈顶运算符为左括号(
,则直接将运算符入栈 - 如果当前运算符优先级比栈顶运算符优先级高,则将运算符直接压入
s1
- 如果当前运算符优先级小于等于栈顶运算符,则将符号栈
s1
的栈顶运算符弹出并压入到数据元素栈s2
中,并重复该动作比较下一个栈顶运算符
- 遇到括号时
- 如果是左括号
(
,则直接压入s1
,包括左括号处理及栈顶为左括号场景 - 如果是右括号
)
,则依次弹出s1
栈顶的运算符,并压入到s2
,直到遇到左括号为止,此时可以将这一对括号丢弃
- 重复步骤2到步骤5,直到扫描到表达式尾部
- 将
s1
中剩余的运算符依次弹出并压入s2
- 逆序输出
s2
中的元素并拼接为表达式(因为栈是后进先出),即为中缀表达式的后缀表达式
4.2,转换举例说明
- 中缀表达式
1+((2+3)*4)-5
转换为123+4*+5-
4.3,代码实现
package com.self.datastructure.stack;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class TransformPermission {
public static void main(String[] args) {
System.out.println(transformPermission("10+((20+30)*40)-50"));;
}
public static String transformPermission(String permission) {
String[] permissionArray = MiddleCalculateDemo.transformPermission(permission);
return doTransformPermission(permissionArray);
}
private static String doTransformPermission(String[] permissionArray) {
if (0 == permissionArray.length) {
return null;
}
Stack<String> numberStack = new Stack<>();
Stack<String> operateStack = new Stack<>();
for (int i = 0; i < permissionArray.length; i++) {
if (permissionArray[i].matches("^[+-]?[0-9]+$")) {
numberStack.push(permissionArray[i]);
} else if(permissionArray[i].matches("^\\($")) {
operateStack.push(permissionArray[i]);
} else if(permissionArray[i].matches("^\\)$")) {
moveElement(numberStack, operateStack);
} else if(permissionArray[i].matches("^[+\\-*/]$")) {
while (operateStack.size() != 0 && !higgerPriority(permissionArray[i], operateStack.peek())) {
numberStack.push(operateStack.pop());
}
operateStack.push(permissionArray[i]);
}
}
for (;0 != operateStack.size(); numberStack.push(operateStack.pop()));
List<String> lstElement = new ArrayList<>(10);
for (;0 != numberStack.size(); lstElement.add(numberStack.pop()));
StringBuffer sb = new StringBuffer();
for (int i = lstElement.size() - 1; i >= 0; i--) {
sb.append(lstElement.get(i) + "#");
}
return sb.toString();
}
private static boolean higgerPriority(String operate, String preOperate) {
int priorityCount = getPriorityCount(operate);
int prePriorityCount = getPriorityCount(preOperate);
return priorityCount > prePriorityCount;
}
private static int getPriorityCount(String operate) {
int priorityCount = 0;
switch (operate) {
case "+": priorityCount = 1;
break;
case "-": priorityCount = 1;
break;
case "*": priorityCount = 2;
break;
case "/": priorityCount = 2;
break;
case "(": priorityCount = 0;
break;
}
return priorityCount;
}
private static void moveElement(Stack<String> numberStack, Stack<String> operateStack) {
for (String currOperate = operateStack.pop(); !"(".equals(currOperate); numberStack.push(currOperate), currOperate = operateStack.pop());
}
}
5,栈模拟计算器_后缀表达式
5.1,思路分析
- 从左至右扫描后缀表达式(逆波兰表达式)
- 对扫描的数字位压栈处理,继续向后扫描
- 扫描到运算符时,从栈中弹出两个数字位,并根据运算符进行对应操作
- 计算完成后,将计算结果压栈并进行后续处理
package com.self.datastructure.stack;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
public class SuffixCalculateDemo {
public static void main(String[] args) {
String permission = TransformPermission.transformPermission("10+((20+30)*40)-50");
System.out.println(calculate(permission));
}
public static int calculate(String suffixPermission) {
List<String> lstElement = Arrays.asList(suffixPermission.split("#", -1));
Stack<Integer> numberStack = new Stack<>();
for (String element : lstElement) {
if (element.matches("^[+-]?[0-9]+$")) {
numberStack.push(Integer.valueOf(element));
} else if (element.matches("^[+\\-*/]$")) {
doCalculate(numberStack, element);
}
}
return numberStack.pop();
}
private static void doCalculate(Stack<Integer> numberStack, String operate) {
if (numberStack.size() < 2) {
throw new RuntimeException("表达式有误...");
}
Integer number1 = numberStack.pop();
Integer number2 = numberStack.pop();
numberStack.push(getResult(number2, number1, operate));
}
private static Integer getResult(Integer firstNumber, Integer secondNumber, String operate) {
switch (operate) {
case "+":
return firstNumber + secondNumber;
case "-":
return firstNumber - secondNumber;
case "*":
return firstNumber * secondNumber;
case "/":
return firstNumber / secondNumber;
}
throw new RuntimeException("操作符无效...");
}
}