波兰式和逆波兰式
波兰式 | 逆波兰式 | 中缀表达式 | |
---|---|---|---|
定义 | 在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之前,所以,这种表示法也称为前缀表达式。 | 将运算对象写在前面,而把运算符号写在后面。用这种表示法表示的表达式也称做后缀式。逆波兰式的特点在于运算对象顺序不变,运算符号位置反映运算顺序。 | 一个通用的算术或逻辑公式表示方法, 操作符是以中缀形式处于操作数的中间。不容易被电脑解析, |
示例 | 3*(2-(5+1)) --> * 3 - 2 + 5 1 | 3*(2-(5+1)) --> 3 2 5 1 + - * | 3*(2-(5+1)) |
阅读方式 | 从左至右读入表达式,如果一个操作符后面跟着两个操作数时,则计算,然后将结果作为操作数替换这个操作符和两个操作数,重复此步骤,直至所有操作符处理完毕。 | 从左往右读入表达式,当读到第一个操作符时,从左边取出两个操作数做计算,然后将这个结果作为操作数替换这个操作符和两个操作数,重复此步骤,直至所有操作符处理完毕。 | 此处不赘述 |
计算示例 | * 3 - 2 + 5 1 → * 3 - 2 6 → * 3 (-4) → * -12 | 3 2 5 1 + - * →3 2 6 - * →3 (-4) * → -12 | 计算过程中必须用括号将操作符和对应的操作数括起来,用于指示运算的次序。 |
波兰式表示法(PN):中缀表达式转换成前缀表达式
PN思路
- 初始化两个栈:运算符栈
S1
; 操作数栈S2
- 从右至左扫描中缀表达式
- 遇到
操作数
时,将其压入S2
- 遇到
运算符
时,比较其与S1
栈顶运算符的优先级- 如果
S1
为空,或栈顶运算符为右括号")"
,或其优先级比栈顶运算符的优先级较高或相等,则直接将此运算符
入栈 - 否则,将
S1
栈顶的运算符弹出并压入到S2
中,再次进行与S1
栈顶运算符的优先级比较
- 如果
- 遇到
括号
时- 如果是
右括号 ")"
,则直接压入S1
- 如果是
左括号 "("
,则依次弹出S1
栈顶的运算符,并压入S2
,直到遇到")"
为止,此时将这一对括号
丢弃
- 如果是
- 重复步骤 2 至 5,直到表达式的最左边
- 将
S1
剩余的运算符依次弹出并压入S2
- 依次弹出
S2
中的元素并输出,结果即为中缀表达式对应的前缀表达式
PN实现
// 中缀表达式转化为前缀表达式
function PN(str) {
const len = str.length
let charStack = [], pnStr = []
const op = {
'+' : 1,
'-' : 1,
'*' : 2,
'/' : 2,
'(' : 3,
')' : 3
}
for (let n = len -1; n > -1 ; n--){
const byte = str[n]
// 数字
if(/\d/.test(byte)) {
pnStr.unshift(byte)
} else if(/\(|\)/.test(byte)) {
// 左括号出栈
if(byte === '(') {
let nowChar = charStack.pop()
while(nowChar && nowChar !== ')') {
pnStr.unshift(nowChar)
nowChar = charStack.pop()
}
// 右括号入栈
} else {
charStack.push(byte)
}
// 符号
} else {
// 字符栈顶元素
let nowChar = charStack[charStack.length-1]
while(nowChar && op[byte] < op[nowChar] && nowChar !== ')') {
charStack.pop()
pnStr.unshift(nowChar)
nowChar = charStack[charStack.length-1]
}
charStack.push(byte)
}
}
while(charStack.length) {
pnStr.unshift(charStack.pop())
}
return pnStr.join('')
}
const stack = ['(','1','+', '2',')' ,'*', '3','-','(', '3','+', '5',')', '/', '7']
console.log(PN(stack)) // -*+123/+357
计算前缀表达式:
function calPN(arr) {
const op = ['+', '-', '*', '/']
const byte = []
while(arr.length) {
let now = arr.pop()
const index = op.indexOf(now)
// 是符号
if(index !== -1) {
// 栈顶两元素
const first = byte.pop(),
end = byte.pop();
switch(index){
case 0:
byte.push( first + end)
break;
case 1:
byte.push( first - end)
break;
case 2:
byte.push( first * end)
break;
case 3:
byte.push( first / end)
break;
}
// 不是符号
} else {
byte.push(now)
}
}
return byte
}
const stack = ['-','*','+', 1, 2,3,'/','+',3,5,7,]
calPN(stack)
逆波兰表示法(RPN):中缀表达式转后缀表达式
RPN思路
- 初始化两个栈:运算符栈
S1
; 操作数栈S2
- 从左至右扫描中缀表达式
- 遇到
操作数
时,将其压入S2
- 遇到
运算符
时,比较其与S1
栈顶运算符的优先级- 如果
S1
为空,或栈顶运算符为"("
,或其优先级比栈顶运算符的优先级较高,则直接将此运算符
入栈 - 否则,将
S1
栈顶的运算符弹出并压入到S2
中,再次进行与S1
栈顶运算符的优先级比较
- 如果
- 遇到括号时
- 如果是
"("
,则直接压入S1
- 如果是
")"
,则依次弹出S1
栈顶的运算符,并压入S2
,直到遇到"("
为止,此时将这一对括号
丢弃
- 如果是
- 重复步骤 2 至 5,直到表达式的最右边
- 将
S1
剩余的运算符依次弹出并压入S2
- 拼接
S2
中的元素并输出,结果即为中缀表达式对应的后缀表达式
RPN实现
// 中缀表达式转化为后缀表达式
function RPN(str) {
let len = str.length, charStack = [], rpnStr = []
const op = {
'+' : 1,
'-' : 1,
'*' : 2,
'/' : 2,
'(' : 3,
')' : 3
}
for( let n = 0; n < len; n++) {
const byte = str[n]
// 数字
if(/\d/.test(byte)) {
rpnStr.push(byte)
} else if(/\(|\)/.test(byte)) {
// 左括号入栈
if(byte === '(') {
charStack.push(byte)
// 右括号出栈
} else {
let nowChar = charStack.pop()
while(nowChar && nowChar !== '(') {
rpnStr.push(nowChar)
nowChar = charStack.pop()
}
}
// 符号
} else {
// 字符栈顶元素
let nowChar = charStack[charStack.length - 1]
while(nowChar && op[byte] < op[nowChar] && nowChar !== '(') {
charStack.pop()
rpnStr.push(nowChar)
nowChar = charStack[charStack.length - 1]
}
charStack.push(byte)
}
}
while(charStack.length) {
rpnStr.push(charStack.pop())
}
return rpnStr.join('')
}
const stack = ['(','1','+', '2',')' ,'*', '3','-','(', '3','+', '5',')', '/', '7']
console.log(RPN(stack)) // 12+3*35+7/-
计算后缀表达式
// 计算后缀表达式
function calRPN(arr) {
const op = ['+', '-', '*', '/']
const byte = []
while(arr.length) {
let now = arr.shift()
const index = op.indexOf(now)
// 是符号
if(index !== -1) {
// 栈顶两元素
const end = byte.pop(),
first = byte.pop();
switch(index){
case 0:
byte.push( first + end)
break;
case 1:
byte.push( first - end)
break;
case 2:
byte.push( first * end)
break;
case 3:
byte.push( first / end)
break;
}
// 不是符号
} else {
byte.push(now)
}
}
return byte
}
const stack = [1, 2,'+',3,'*',3,5,'+',7,'/','-']
calRPN(stack)
参考:
https://blog.csdn.net/weixin_42614080/article/details/106747483
https://zhuanlan.zhihu.com/p/143113551