数据结构与算法之栈
前言
在前面几篇我们有讲到数组,链表等数据结构,这些都是存储数据的最基本的结构。而本篇文章讲解的则是利用算法和数据结构来实现一些小工具,来解决现实中某种需求。它就是栈。
什么是栈?
栈(stack
) 是一种数据结构,是一个先入后出(Filo-First In Last Out) 的有序列表,只能在一端进行插入和删除操作的特殊线性表。允许插入的一端和删除的一端,为变化的一端,称为 栈顶(top),另一端为固定的一端,称为 栈底(bottom)。
它按照先进后出的原则存储数据,先进入的数据被压入栈底,最后的数据在栈顶,需要读数据的时候从栈顶开始弹出数据(最后一个数据被第一个读出来)。
出栈(pop)和入栈(push)示意图
实现一个简单的栈
由于栈是一个有序列表,我们可以使用数组来实现一个栈。
代码示例:
public class ArrayStack {
private int[] dataArray; // 存储栈数据的数组
private int maxSize; // 栈的最大长度
private int top; // 指向栈顶部的指针
public ArrayStack(int maxSize){
this.maxSize = maxSize;
this.dataArray = new int[maxSize];
this.top = -1;
}
/**
* 判断栈是否为空
*
* @return
*/
public boolean isEmpty(){
return top == -1;
}
/**
* 判断栈是否满了
*
* @return
*/
public boolean isFull() {
return top == maxSize - 1;
}
/**
* 入栈
*
* @param value
*/
public void push(int value) {
if (!this.isFull()) {
dataArray[++top] = value;
}
}
/**
* 出栈
*
* @return
*/
public int pop() {
if (this.isEmpty()) {
throw new RuntimeException("stack is empty");
}
return dataArray[top--];
}
/**
* 获取栈顶部数据
*
* @return
*/
public int peek(){
return dataArray[top];
}
}
测试:
public class ArrayStackTest {
public static void main(String[] args) {
ArrayStack arrayStack = new ArrayStack(5);
arrayStack.push(1);
arrayStack.push(2);
arrayStack.push(3);
arrayStack.push(4);
arrayStack.push(5);
System.out.println("stack peek:" + arrayStack.peek());
while (!arrayStack.isEmpty()) {
System.out.printf("[%d]", arrayStack.pop());
}
}
}
输出结果:
stack peek:5
[5][4][3][2][1]
上述代码内部定义了一个数组(dataArray
),用来存储栈的数据,maxSize
表示栈的最大容量, top
是指向栈顶部的指针,初始化时为-1
。
构造方法传入一个参数:maxSize
,用来指定栈的最大容量来创建一个新的栈,push
()方法是向栈中放入元素,每放一个top+1
,使它指向原顶端数据项上面的一个位置,并在这个位置上存储一个数据。pop
()方法返回top
变量指向的元素,然后将top
变量减一,便实现了数据出栈。
利用栈实现综合计算器
实现思路:
- 通过一个 index 值(索引),来遍历我们的计算表达式
- 当遍历的当前值是数字的话, 直接放入数据栈中
- 当遍历的当前值是字符的话, 就分如下情况
3.1 如果发现当前的符号栈为空,就直接入栈
3.2 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中取出两个数,在从符号栈中取出一个符号,进行运算,将得到的结果,入数据栈,然后将当前的操作符入符号栈, 如果当前的操作符的优先级大于栈中的操作符, 就直接入符号栈. - 当表达式扫描完毕,就顺序的从 数栈和符号栈中pop出相应的数和符号,并进行计算
- 最后在数栈只有一个数字,就是表达式的结果
代码实现:
public class Calculator {
public static void main(String[] args) {
ArrayStack numberStack = new ArrayStack(10); // 数据栈
ArrayStack operatorStack = new ArrayStack(10); // 运算符栈
String expression = "6*3*9-8+5-7"; // 运算表达式
// 定义相关临时辅助变量
int index = 0;
int num1 = 0;
int num2 = 0;
int oper = 0;
int res = 0;
char ch = ' ';
String keepNum = "";
while (true) {
// 遍历依次得到表达式中的每一个字符
ch = expression.substring(index, index + 1).charAt(0);
// 判断ch是什么
if (Calculator.isOperator(ch)) {// 如果是运算符
// 判断当前运算符栈是否为空
if (!operatorStack.isEmpty()) {
// 如果符号栈有操作符,就进行比较,如果当前的操作符的优先级小于或者等于栈中的操作符, 就需要从数栈中取出两个数,在从符号栈中取出一个符号,进行运算,
// 将得到的结果,入数据栈,然后将当前的操作符入符号栈
if (Calculator.getPriority(ch) <= Calculator.getPriority(operatorStack.peek())) {
num1 = numberStack.pop();
num2 = numberStack.pop();
oper = operatorStack.pop();
// 进行计算
res = Calculator.cal(num1, num2, oper);
// 将计算得到的结果放入数据栈
numberStack.push(res);
// 将当前的运算符放入运算符栈
operatorStack.push(ch);
} else {
// 如果当前的运算符的优先级大于栈中的运算符,就直接放入运算符栈
operatorStack.push(ch);
}
} else {
// 直接入运算符栈
operatorStack.push(ch); // 1 + 3
}
} else { // 如果是数字直接入数据栈
// 处理多位数
keepNum += ch;
// 判断是否是表达式最后一个字符,如果是,直接入栈
if (index == expression.length() - 1) {
numberStack.push(Integer.parseInt(keepNum));
} else {
// 判断下一个字符是不是数字,如果是数字,就继续遍历,如果是运算符,就直接入栈
if (Calculator.isOperator(expression.substring(index + 1, index + 2).charAt(0))) {
numberStack.push(Integer.parseInt(keepNum));
// 清空
keepNum = "";
}
}
}
// 让index+1,判断表达式是否遍历完了
index++;
if (index >= expression.length()) {
break;
}
}
// 当表达式遍历完了之后,就顺序从数据栈和运算符栈中取出相应的数字和运算符进行计算
while (true) {
// 如果运算符栈为空,则代表计算到了最后的结果,数据栈中就只有一个结果数据了
if (operatorStack.isEmpty()) {
break;
}
num1 = numberStack.pop();
num2 = numberStack.pop();
oper = operatorStack.pop();
// 计算结果
res = Calculator.cal(num1, num2, oper);
numberStack.push(res);//ÈëÕ»
}
System.out.printf("运算表达式 %s = %d", expression, numberStack.pop());
}
/**
* 返回运算符的优先级
*
* @param operator 运算符
* @return
*/
public static int getPriority(int operator) {
if (operator == '*' || operator == '/') {
return 1;
} else if (operator == '+' || operator == '-') {
return 0;
} else {
return -1; // 目前只支持 +, - , * , /
}
}
/**
* 是否是运算符判断
*
* @param val
* @return
*/
public static boolean isOperator(char val) {
return val == '+' || val == '-' || val == '*' || val == '/';
}
/**
* 计算
*
* @param num1 数据一
* @param num2 数据二
* @param oper 运算符
* @return
*/
public static int cal(int num1, int num2, int oper) {
int result = 0;
switch (oper) {
case '+':
result = num1 + num2;
break;
case '-':
result = num2 - num1;
break;
case '*':
result = num1 * num2;
break;
case '/':
result = num2 / num1;
break;
default:
break;
}
return result;
}
}
测试结果:
运算表达式 6*3*9-8+5-7 = 152
还没有评论,来说两句吧...