JavaScript中序表達(dá)式轉(zhuǎn)后序表達(dá)式并計(jì)算結(jié)果

為什么要轉(zhuǎn)換

  • 中序表達(dá)式:10(8-21)*2-30
  • 后序表達(dá)式: 10821-2*30-
  • 中序表達(dá)式是給人看的,可以直觀的根據(jù)優(yōu)先級計(jì)算出結(jié)果,但是計(jì)算機(jī)無法自動(dòng)識別優(yōu)先級,因此將帶有計(jì)算的優(yōu)先級和括號的中序表達(dá)式變成符合某文法的后序(或前序)表達(dá)式之后,計(jì)算機(jī)可以通過從左到右(或從右到左)掃描來計(jì)算結(jié)果。

中序轉(zhuǎn)后續(xù)

模擬棧
class Stack {
  constructor() {
    this.stack = []
    this.max = 0
    this.opts = {
      '+': 1,
      '-': 1,
      '*': 5,
      '/': 5,
      '(': 10,
      ')': 10
    }
  }
  in(char) {
    this.stack.push(char)
    this.max = this.opts[this.stack[this.stack.length - 1]]
  }
  out() {
    let pop = this.stack.pop()
    this.max = this.opts[this.stack[this.stack.length - 1]]
    return pop
  }
  empty() {
    return !this.stack.length
  }
  last() {
    return this.stack[this.stack.length - 1]
  }
}

中序轉(zhuǎn)后序的過程

  • 處理表達(dá)式:10*(8-2*1)*2-30 處理結(jié)果為 ["10", "*", "(", "8", "-", "2", "*", "1", ")", "*", "2", "-", "30"]
  • 遍歷處理過后的中序表達(dá)式
  • 遇到數(shù)字直接入棧
  • 遇到'(' 直接入棧
  • 遇到 ')' 則持續(xù)出棧至遇到'('停止
  • 如果 (棧為空 || 當(dāng)前運(yùn)算符優(yōu)先級較高 || 棧頂是'('字符 )則入棧
  • 否則 棧頂元素依次出棧 直至新操作符比棧頂優(yōu)先級高 新操作符入棧
  • 遍歷結(jié)束 輸出棧中所有元素
模擬演示
  • 只要后來的優(yōu)先級沒有高于棧頂?shù)?就把棧頂?shù)牟僮鞣贸鰜碇钡胶髞淼膬?yōu)先級高于棧頂?shù)臑橹?只有遇到右括號的時(shí)候需要一直往外拿 直到碰到左括號
  • 處理結(jié)果arr = ["10", "*", "(", "8", "-", "2", "*", "1", ")", "*", "2", "-", "30"]
  • 暫時(shí)存儲后序表達(dá)式的數(shù)組A,存儲操作符的模擬棧B
  • 遍歷arr
  • "10":數(shù)字 -- 存入A -- A ['10']
  • *:操作符 --入棧B -- B ['*']
  • (:操作符 --入棧B -- B ['*', '(']
  • "8":數(shù)字 -- 存入A -- A ['10', '8']
  • -:操作符 --入棧B -- B ['*', '(', '-']
  • "2":數(shù)字 -- 存入A -- A ['10', '8', '2']
  • *:操作符 --入棧B -- B ['*', '(', '-','*']
  • "1":數(shù)字 -- 存入A -- A ['10', '8', '2', '1']
  • ):操作符 --B開始出棧并存入A 直至(出現(xiàn)
    B ['*', '(', '-'] A ['10', '8', '2', '1', '*']
    B ['*', '('] A ['10', '8', '2', , '1','*', '-']
    B ['*'] A ['10', '8', '2', '1', '*', '-']
  • *:操作符 --優(yōu)先級不高于棧頂?shù)?' 所以棧頂出棧存入A 新的 ''入棧
    B [] A ['10', '8', '2', '1', '*'', '-', '*']
    B ['*'] A ['10', '8', '2', '1', '*', '-', '*']
  • "2":數(shù)字 -- 存入A -- A ['10', '8', '2', '1', '*', '-', '*', '2']
  • -:操作符 --優(yōu)先級不高于棧頂?shù)?*' 所以棧頂出棧存入A 新的 '-'入棧
    B [] A ['10', '8', '2', '1', '*', '-', '*', '2', '*']
    B ['-'] A ['10', '8', '2', '1', '*', '-', '*', '2', '*']
  • "30":數(shù)字 -- 存入A -- A ['10', '8', '2', '1', '*', '-', '*', '2', '*', '30']
  • 遍歷結(jié)束 棧B不為空 依次出棧
    B [] A ['10', '8', '2', '1', '*', '-', '*', '2', '*', '30', '-']
  • 結(jié)束 得到轉(zhuǎn)換后的后序數(shù)組['10', '8', '2', '1', '*', '-', '*', '2', '*', '30', '-']
// 中序轉(zhuǎn)后序
function transform(str) {
  // 模擬棧 存儲運(yùn)算操作符
  var stack = new Stack()
  // 后序表達(dá)式數(shù)組
  var postfixExp = []
  // 切分中序表達(dá)式
  str = this.str2arr(str)
  // 遍歷處理過后的中序表達(dá)式
  for (let i = 0; i < str.length; i++) {
    // debugger
    let item = str[i]
    if(!isNaN(Number(item))) { // 數(shù)字直接入棧
      postfixExp.push(item)
    } else if (item === '(') { // '(' 直接入棧
      stack.in(item)
    } else if (item === ')') { // 遇到 ')' 則持續(xù)出棧至遇到'('停止
      let top = stack.out()
      while (top && top !== '(') {
        postfixExp.push(top)
        top = stack.out()
      }
    }else if (stack.opts[item]) { // 合法的 加減乘除字符
      if (stack.empty() || stack.opts[item] > stack.max || stack.last() === '(') {
        // 棧為空 || 當(dāng)前運(yùn)算符優(yōu)先級較高 || 棧頂是'('字符 
        stack.in(item)
      } else {
        // 棧頂元素依次出棧 直至新操作符比棧頂優(yōu)先級高 新操作符入棧
        let top = stack.last() // 目前棧頂?shù)脑?        let max = stack.opts[item] // 新操作符的權(quán)重
        while(top && max <= stack.max && stack.last() !== '(') {
          postfixExp.push( stack.out() )
          top = stack.last()
        }
        stack.in(item)
      }
    }
  }
  
  // 遍歷結(jié)束 輸出棧中所有?元素
  while(!stack.empty()) {
    postfixExp.push( stack.out() )
  }

  return postfixExp
}


function str2arr(str) {
  str = str.split('+').join('|+|')
  str = str.split('-').join('|-|')
  str = str.split('*').join('|*|')
  str = str.split('/').join('|/|')
  str = str.split('(').join('(|')
  str = str.split(')').join('|)')
  return str.split('|')
}

后序的計(jì)算過程

  • 遍歷后序表達(dá)式數(shù)組
  • 遇到數(shù)字入棧
  • 遇到操作符 則拿出棧頂?shù)膬蓚€(gè)元素 進(jìn)行運(yùn)算 并將運(yùn)算結(jié)果入棧
  • 遍歷結(jié)束時(shí)棧中的元素為運(yùn)算結(jié)果
模擬演示
  • 后序表達(dá)式數(shù)組:A ['10', '8', '2', '1', '*', '-', '*', '2', '*', '30', '-']
  • 存儲運(yùn)算數(shù)的模擬棧B
  • 遍歷A
  • "10":數(shù)字 -- 入棧B -- B ['10']
  • "8":數(shù)字 -- 入棧B -- B ['10', '8']
  • "2":數(shù)字 -- 入棧B -- B ['10', '8', '2']
  • "1":數(shù)字 -- 入棧B -- B ['10', '8', '2', '1']
  • "*":運(yùn)算符 -- 取出棧B最上邊的兩個(gè)元素 '1' 和'2'
    運(yùn)算 2 * 1 = 2
    運(yùn)算結(jié)果入棧B B ['10', '8', '2']
  • "-":運(yùn)算符 -- 取出棧B最上邊的兩個(gè)元素 '2' 和'8'
    運(yùn)算 8 - 2 = 6
    運(yùn)算結(jié)果入棧B B ['10', '6']
  • "*":運(yùn)算符 -- 取出棧B最上邊的兩個(gè)元素 '6' 和'10'
    運(yùn)算 10 * 6 = 60
    運(yùn)算結(jié)果入棧B B ['60']
  • "2":數(shù)字 -- 入棧B -- B ['60','2']
  • "*":運(yùn)算符 -- 取出棧B最上邊的兩個(gè)元素 '2' 和'60'
    運(yùn)算 60 * 2 = 120
    運(yùn)算結(jié)果入棧B B ['120']
  • "30":數(shù)字 -- 入棧B -- B ['120', '30']
  • "-":運(yùn)算符 -- 取出棧B最上邊的兩個(gè)元素 '30' 和'120'
    運(yùn)算 120 - 30 = 90
    運(yùn)算結(jié)果入棧B B ['90']
  • 遍歷結(jié)束 結(jié)果為棧B中的元素 90
// 后序表達(dá)式計(jì)算得出結(jié)果
function getResult(arr) {
  // 模擬棧 存儲運(yùn)算數(shù)字
  let stack = new Stack()
  for (let i = 0; i < arr.length; i++) {
    let item = arr[i]
    if(!isNaN(Number(item))) { // 數(shù)字直接入棧
      stack.in(item)
    } else { // 遇到操作符 則拿出棧頂?shù)膬蓚€(gè)元素 進(jìn)行運(yùn)算 并將運(yùn)算結(jié)果入棧
      let tempRes = 0
      let a = Number(stack.out())
      let b = Number(stack.out())
      switch (item) {
        case '+': tempRes = b + a; break;
        case '-': tempRes = b - a; break;
        case '*': tempRes = b * a; break;
        case '/': tempRes = b / a; break;
        default : throw new Error('操作符有誤')
      }
      stack.in(tempRes)
    }
  }
  return stack.out()
}
運(yùn)算結(jié)果打印
var InfixExp = '10*(8-2*1)*2-30'
var postfixExp = transform(InfixExp)
console.log(InfixExp,'的后序表達(dá)式--', postfixExp.join(''));
var result = getResult(postfixExp)
console.log('后序表達(dá)式--', postfixExp.join(''), '的計(jì)算結(jié)果是:', result);
運(yùn)算結(jié)果打印1
運(yùn)算結(jié)果打印2
運(yùn)算結(jié)果打印3
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容