📅  最后修改于: 2023-12-03 14:49:53.464000             🧑  作者: Mango
堆栈(Stack)是一种后进先出(Last In First Out,LIFO)的数据结构,在计算机科学中有广泛的应用。通常用于表达式求值、递归函数的调用、回溯算法等。
单链表(Singly Linked List)是一种简单的数据结构,它通过指针将多个节点连接起来。由于每个节点只有一个指向下一个节点的指针,因此可以实现高效的插入和删除操作。
本文将介绍如何使用单链表实现堆栈的抽象数据类型,以及如何使用此 ADT 将中缀表达式转换为后缀、前缀以及后缀和前缀表达式的求值。
我们定义一个 Stack 类,它具有以下属性和方法:
top
:堆栈顶部节点的指针push(value)
:将指定的值压入堆栈顶部pop()
:弹出堆栈顶部节点并返回其值peek()
:返回堆栈顶部节点的值isEmpty()
:返回堆栈是否为空class Stack {
constructor() {
this.top = null;
}
push(value) {
const node = { value, next: this.top };
this.top = node;
}
pop() {
if (this.top === null) throw new Error('Stack is empty');
const value = this.top.value;
this.top = this.top.next;
return value;
}
peek() {
if (this.top === null) throw new Error('Stack is empty');
return this.top.value;
}
isEmpty() {
return this.top === null;
}
}
中缀表达式(Infix Notation)是常见的表达式书写方式,例如 2 + 3 * 4
。为了方便计算,我们可以将中缀表达式转换为后缀表达式(Postfix Notation),例如 2 3 4 * +
,这样可以方便地使用堆栈进行计算。
转换的方法是使用一个辅助栈,从左到右扫描中缀表达式,并将每个数字直接输出到结果中。对于运算符,我们需要判断它和栈顶运算符的优先级,然后进行入栈或者出栈。具体的方法如下:
stack
和一个空字符串 output
output
output
中,重复步骤3。output
中,重复步骤4,直到遇到左括号,将其弹出但不输出。output
中。output
中。function infixToPostfix(expression) {
const stack = new Stack();
let output = '';
const precedence = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
'^': 3,
};
for (let token of expression.split(/\s+/)) {
if (/^\d+$/.test(token)) {
output += `${token} `;
} else if (precedence[token] !== undefined) {
while (!stack.isEmpty() && precedence[token] <= precedence[stack.peek()]) {
output += `${stack.pop()} `;
}
stack.push(token);
} else if (token === '(') {
stack.push(token);
} else if (token === ')') {
while (stack.peek() !== '(') {
output += `${stack.pop()} `;
}
stack.pop();
}
}
while (!stack.isEmpty()) {
output += `${stack.pop()} `;
}
return output.trim();
}
前缀表达式(Prefix Notation)是另一种常见的表达式书写方式,例如 + 2 * 3 4
。与后缀表达式相比,前缀表达式的好处是可以使用相同的方式求值。
转换的方法与转换为后缀表达式类似,不同之处在于运算符的出栈顺序。具体的方法如下:
expression'
expression'
转换为后缀表达式 postfix'
postfix'
反转,得到前缀表达式 prefix
function infixToPrefix(expression) {
const reverseExpression = expression.split('').reverse().join('');
const postfix = infixToPostfix(reverseExpression);
const prefix = postfix.split(/\s+/).reverse().join(' ');
return prefix;
}
后缀表达式求值(Postfix Evaluation)是一种高效的表达式求值方式,它只需要使用一个堆栈即可完成计算。具体的方法是从左到右扫描后缀表达式,遇到数字则将其压入堆栈中,遇到运算符则从堆栈中弹出相应数量的数字进行计算,将结果再次压入堆栈中,最后堆栈中只剩下一个数字,即为计算结果。
function evaluatePostfix(expression) {
const stack = new Stack();
for (let token of expression.split(/\s+/)) {
if (/^\d+$/.test(token)) {
stack.push(parseInt(token, 10));
} else {
const operand2 = stack.pop();
const operand1 = stack.pop();
switch (token) {
case '+':
stack.push(operand1 + operand2);
break;
case '-':
stack.push(operand1 - operand2);
break;
case '*':
stack.push(operand1 * operand2);
break;
case '/':
stack.push(operand1 / operand2);
break;
case '^':
stack.push(Math.pow(operand1, operand2));
break;
default:
throw new Error(`Invalid operator: ${token}`);
}
}
}
return stack.pop();
}
前缀表达式求值(Prefix Evaluation)与后缀表达式求值类似,不同之处在于遍历顺序为从右到左。具体的方法是从右到左扫描前缀表达式,遇到数字则将其压入堆栈中,遇到运算符则从堆栈中弹出相应数量的数字进行计算,将结果再次压入堆栈中,最后堆栈中只剩下一个数字,即为计算结果。
function evaluatePrefix(expression) {
const reverseExpression = expression.split('').reverse().join('');
const postfix = infixToPostfix(reverseExpression);
return evaluatePostfix(postfix);
}
本文介绍了如何使用单链表将堆栈实现为抽象数据类型,并使用此 ADT 将中缀表达式转换为后缀、前缀以及后缀和前缀表达式的求值。堆栈是一种简单但是常用的数据结构,对于计算机科学专业的学生来说,熟练掌握堆栈的使用是非常重要的。