數據結構——逆波蘭式


很久沒有關注算法和數據結構,大部分知識都已經忘記了;是時間好好回爐一下了,說實話干讀數據機構這本書還是挺枯燥而且這本書原理性比較多,有一定的難度。這不剛看到逆波蘭式廢了好大勁才搞懂,老了。。。

逆波蘭式

逆波蘭式(Reverse Polish notation,RPN,或逆波蘭記法),也叫后綴表達式(將運算符寫在操作數之后)

一個 表達式E的后綴形式可以如下定義:
(1)如果E是一個變量或 常量,則E的 后綴式是E本身。
(2)如果E是E1 op E2形式的表達式,這里op是如何二元操作符,則E的后綴式為E1'E2' op,這里E1'和E2'分別為E1和E2的后綴式。
(3)如果E是(E1)形式的表達式,則E1的后綴式就是E的后綴式。
如:我們平時寫a+b,這是 中綴表達式,寫成 后綴表達式就是:ab+
(a+b)*c-(a+b)/e的后綴表達式為:
(a+b)*c-(a+b)/e
→((a+b)*c)((a+b)/e)-
→((a+b)c*)((a+b)e/)-
→(ab+c*)(ab+e/)-
→ab+c*ab+e/-

算法實現

將一個普通的中序 表達式轉換為 逆波蘭表達式的一般算法是:
首先需要分配2個棧,一個作為臨時存儲運算符的棧S1(含一個結束符號),一個作為輸入逆波蘭式的棧S2(空棧),S1棧可先放入優先級最低的運算符#,注意,中綴式應以此最低優先級的運算符結束。可指定其他字符,不一定非#不可。從中綴式的左端開始取字符,逐序進行如下步驟:
(1)若取出的字符是 操作數,則分析出完整的運算數,該操作數直接送入S2棧
(2)若取出的字符是 運算符,則將該運算符與S1棧棧頂元素比較,如果該 運算符優先級(不包括括號運算符)大於S1棧棧頂運算符優先級,則將該運算符進S1棧,否則,將S1棧的棧頂運算符彈出,送入S2棧中,直至S1棧棧頂運算符低於(不包括等於)該運算符優先級,最后將該運算符送入S1棧。
(3)若取出的字符是“(”,則直接送入S1棧頂。
(4)若取出的字符是“)”,則將距離S1棧棧頂最近的“(”之間的運算符,逐個 出棧,依次送入S2棧,此時拋棄“(”。
(5)重復上面的1~4步,直至處理完所有的輸入字符
(6)若取出的字符是“#”,則將S1棧內所有運算符(不包括“#”),逐個出棧,依次送入S2棧。
完成以上步驟,S2棧便為逆波蘭式輸出結果。不過S2應做一下逆序處理。便可以按照逆波蘭式的計算方法計算了!
 

代碼程序

//'1 + 2 * 3 + (4 * 5 + 6) * 7'

function ReversePolish() {
  this.operatorStack = [];
  // this.operator = ['+', '-', '*', '/', '(', ')'];
  this.operator = {
    '+': 1,
    '-': 1,
    '*': 2,
    '/': 2,
    '(': 10,
    ')': 10
  };
  this.rp = [];
}

ReversePolish.prototype.convert = function(str) {
  debugger;
  // ('15 + 2 * 3 + (4 * 5 + 6) * 7').trim().replace(/\s+/g, '').split(/([\+|\-|\*|\/|\(|\)])/)
  // ["15", "+", "2", "*", "3", "+", "", "(", "4", "*", "5", "+", "6", ")", "", "*", "7"]
  str
  .trim()
  .replace(/\s+/g, '')
  .split(/([\+|\-|\*|\/|\(|\)])/)
  .filter(e => !!e)
  .forEach(e => {
    if (/[0-9]/g.test(e)) { // 數字直接放入逆波蘭式數組
      this.rp.push(e);
    } else {
      if (this.operatorStack.length === 0) {// 操作符棧為空直接壓入棧
        this.operatorStack.push(e);
      } else {
        if (e === '(') { // 左括號直接入棧
          this.operatorStack.push(e);
        } else if (e === ')') { // 右括號彈出所有的操作符進入逆波蘭數組,直至遇到 (, (不進入逆波蘭數組
          let op = this.operatorStack.pop();
          while(op !== '(') {
            this.rp.push(op);
            op = this.operatorStack.pop();
          }
          // this.operatorStack.pop();
        } else { // 遇到其他操作符則彈出所有棧頂元素,直至遇到優先級更低的操作符,但是不處理(
          let op = this.operatorStack.pop();
          while(op && this.operator[op] >= this.operator[e] && op !== '(') {
            this.rp.push(op);
            op = this.operatorStack.pop();
          }
          if (op) {
            this.operatorStack.push(op);
          }
          this.operatorStack.push(e);
        }
      }
    }
  });
  // 運行結束后將所有的操作符棧彈出
  let op = this.operatorStack.pop();
  while(op) {
    this.rp.push(op);
    op = this.operatorStack.pop();
  }

  console.log(this.rp.join(' '));
};

//15 2 3 * + 4 5 * 6 + 7 * +
ReversePolish.prototype.eval = function(){
  let numberStack = [];
  this.rp.forEach(e => {
    if (/[0-9]/g.test(e)) {
      numberStack.push(Number(e));
    } else if (this.operator[e]) {
      let n2 = numberStack.pop();
      let n1 = numberStack.pop();
      switch(e) {
        case '+':
          numberStack.push(n1 + n2);
          break;
        case '-':
          numberStack.push(n1 - n2);
          break;
        case '*':
          numberStack.push(n1 * n2);
          break;
        case '/':
          numberStack.push(n1 / n2);
      }
    }
  });
  return numberStack.pop();
}

let rp = new ReversePolish();
rp.convert('15 + 2 * 3 + (4 * 5 + 6) * 7');
rp.eval();

  感覺逆波蘭式不僅是一種方法,更是一種思想,逆波蘭式這種計算方法沒有必要知道任何運算符優先規則。就像我們實際業務中有很多邏輯判斷、各種優先級的場景,是否也可以使用逆波蘭式的思想來解決?上面的例子也是比較簡單的情況,沒有考慮運算符的執行順序,對於2^2^3這個種,實際是等於2^8等於256,而不是4^3=64.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM