📜  使用单链表将堆栈实现为抽象数据类型,并使用此 ADT 将中缀表达式转换为后缀、前缀以及后缀和前缀表达式的求值. - Javascript(1)

📅  最后修改于: 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 * +,这样可以方便地使用堆栈进行计算。

转换的方法是使用一个辅助栈,从左到右扫描中缀表达式,并将每个数字直接输出到结果中。对于运算符,我们需要判断它和栈顶运算符的优先级,然后进行入栈或者出栈。具体的方法如下:

  1. 创建一个空栈 stack 和一个空字符串 output
  2. 从左到右扫描中缀表达式:
    • 如果遇到数字,将其直接输出到 output
    • 如果遇到运算符:
      • 如果栈为空或者栈顶是左括号,将其压入栈中。
      • 如果栈顶运算符的优先级小于等于它,将它压入栈中。
      • 否则,将栈顶运算符弹出并加入到 output 中,重复步骤3。
    • 如果遇到左括号,将其压入栈中。
    • 如果遇到右括号,将栈中运算符弹出并加入到 output 中,重复步骤4,直到遇到左括号,将其弹出但不输出。
  3. 如果表达式还有剩余,将它们直接输出到 output 中。
  4. 将栈中剩余的运算符弹出并加入到 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。与后缀表达式相比,前缀表达式的好处是可以使用相同的方式求值。

转换的方法与转换为后缀表达式类似,不同之处在于运算符的出栈顺序。具体的方法如下:

  1. 将中缀表达式反转,得到反转后的表达式 expression'
  2. expression' 转换为后缀表达式 postfix'
  3. 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 将中缀表达式转换为后缀、前缀以及后缀和前缀表达式的求值。堆栈是一种简单但是常用的数据结构,对于计算机科学专业的学生来说,熟练掌握堆栈的使用是非常重要的。