一种通过字符串检索拆分转换成二叉树方式递归计算表达式的算法

        本算法实现了计算算数表达式的运算,实现了加、减、乘、除四则运算,逻辑运算,三元运算等功能,类似javascript里的eval(expression)函数,完全可以用eval(expression)取代。

先看测试结果

!true||true ==> true
!false ==> true
!!false ==> false
!true ==> false
!!true ==> true
3>2&&3>4 ==> false
(1+1) ==> 2
(5-1)*2 ==> 8 
2-(3-(1+4)) ==> 4
(1 + (2 - (3 * 4 / 5 + 6 - (7 + 8))) + (9 - 10) * 11 + 12) + 13 ==> 23.6
3<4&&5<4 ==> false
true||true ==> true
true||false ==> true
true&&!false ==> true
true||!false ==> true
true&&!(4<5) ==> false
!!true&&!!false ==> false
false?true:!false ==> true
false?4-3:4+3 ==> 7
on=='on'?'开启':'关闭' ==> 开启

        可以使用二叉树拆分方式、堆栈方式等多种方式来实现该算法功能,之前文章介绍过堆栈方式实现,本文介绍二叉树方式实现该功能算法。本文实现了计算算数表达式的运算,实现了加、减、乘、除四则运算,逻辑运算,三元运算等功能。可以继续扩展函数的支持、参数变量的支持。有错误的请指教,下面直接上代码。

运算优先级,优先级相同从左到右
 1    小括号      ( )
 2    一元运算符  ++ -- !
 3    算数运算符  * / %
 4    算数运算符  + -
 5    关系运算符  > >= < <=
 6    相等运算符  == != === !==
 7    逻辑运算符  &&
 8    逻辑运算符  ||
 9    三元运算符  :
 10   三元运算符  ?

const operators = ['!','*','/','%','+','-','>','>=','<','<=','==','!=','===','!==','&&','||',':','?'];
const operleves = [2,3,3,3,4,4,5,5,5,5,6,6,6,6,7,8,9,10];

// 括号处理,先进后出,每一次出栈,即一对 ()
const stock = []; 
const parenthesesPairPosition = {}

//是否是运算操作符
const isOperator = (oper) => {
    return operators.indexOf(oper)>-1;
}

//获取优先级
const getOperatorLeve = (oper) => {
    return operleves[operators.indexOf(oper)];
}

//判断是否是数字,校验只要是数字(包含正负整数,0以及正负浮点数)就返回true
const isNumber = (num) => {
    let regPos = /^[-+]?\d+(\.\d+)?$/;
    if(regPos.test(num)){
        return true;
    }else{
        return false;
    }
}

// 剔除两侧空格
const removeBlank = (expression, l, r) => {
    while (expression[l] === ' ') {
        l++
    }
    while (expression[r] === ' ') {
        r--
    }
    return [l, r]
}

// 剔除两侧小括号
const removeParentheses = (l, r) => {
    if (parenthesesPairPosition[l] === r) return [++l, --r]
    return [l, r]
}

const findOperator = (v, v1, v2) => {
    let op = '';
    //'>=','<=','==','!=','===','!==','&&','||'
    if(v==='&' || v==='=' || v==='|'){
        if(v1==='>' || v1==='<' || v1==='=' || v1==='!' || v1==='&' || v1==='|'){
            if(v2==='=' || v2==='!'){//'===','!=='
                op = v2+v1+v;
                let opleve = getOperatorLeve(op);
                return [false, op, opleve, 3];
            }else{
                op = v1+v;
                let opleve = getOperatorLeve(op);
                return [false, op, opleve, 2];
            }
        }else{
            if(isOperator(v)){
                op = v;
                let opleve = getOperatorLeve(op);
                return [false, op, opleve, 2];
            }
        }
    }else if(isOperator(v)){
        op = v;
        let opleve = getOperatorLeve(op);
        return [false, op, opleve, 1];
    }
    return [true, op, 0, 0];
}

按照运算优先级拆分字符串表达式,装换成二叉树算法

//将表达式转换成二叉树结构
const parse = (expression, l = 0, r = expression.length - 1, skipSearchTime = false) => {
    let isNum = true;
    let parenthesesDep = 0; // 记录小括号深度
    let firstTimeOperator = null; // 记录遇到的第一个 * / 运算符
    let firstTimeOperatorIdx = null; // 记录遇到的第一个 * / 运算符的位置
    let firstTimeOperatorLeve = -1;
    let firstTimeOperatorlength = 1;
    [l, r] =  removeBlank(expression, l, r);
    [l, r] = removeParentheses(l, r);
    for (let i = r; i >= l; i--) {
        const v = expression[i];
        if(v===' '){
            continue;
        }
        if (v === ')') {
            stock.push(i);
            parenthesesDep++;
            continue;
        } else if (v === '(') {
            const lnode = stock.pop();
            parenthesesPairPosition[i] = lnode;
            parenthesesDep--;
            isNum = false;
        }

        //遍历到最左边
        if (i === l && v!=='!') {
            if (isNum && firstTimeOperator==null) {
                let val = isNumber(expression.slice(l, r + 1).trim())?Number(expression.slice(l, r + 1).trim()):expression.slice(l, r + 1).trim();
                return {
                    type: 'number',
                    value: val,
                };
            }
            if (parenthesesPairPosition[i] === r) {//去掉前后括号后进行递归表达式
                return parse(expression, l + 1, r - 1);
            }
            // 高优先级 拆分,需要遍历到最左侧,所以拆分逻辑写这里
            return {
                type: firstTimeOperator,
                left: parse(expression, l, firstTimeOperatorIdx - 1, true),
                right: parse(expression, firstTimeOperatorIdx + firstTimeOperatorlength, r),
            }
        }

        //找到优先级最高的运算符
        let oplen = 0;
        let opleve = 0;
        let op = '';

        let v1, v2;
        if(i>l){
            v1 = expression[i-1];
        }
        if(i>(l+1)){
            v2 = expression[i-2];
        }

        [isNum, op, opleve, oplen] = findOperator(v, v1, v2);
        
        if(isNum){
            continue;
        }else{
            if(oplen==2){
                i--;
            }else if(oplen==3){
                i = i-2;
            }
        }

        if(opleve>firstTimeOperatorLeve && parenthesesDep==0){
            firstTimeOperator = op;
            firstTimeOperatorIdx = i;
            firstTimeOperatorLeve = opleve;
            firstTimeOperatorlength = oplen;
        }

        //单目运算符!的支持
        if(i === l && v==='!'){
            if(firstTimeOperator==='!'){
                return {
                    type: firstTimeOperator,
                    right: parse(expression, l + 1, r),
                }
            }else{
                return {
                    type: firstTimeOperator,
                    left: parse(expression, l, firstTimeOperatorIdx - 1, true),
                    right: parse(expression, firstTimeOperatorIdx + firstTimeOperatorlength, r),
                }
            }
        }
    }
}

计算按照运算优先级转换而来的二叉树的结果

//计算二叉树表达式结果
const exec = node => {
    const recursion = node => {
        if (node.type === 'number') {
            let value = node.value;
            if(typeof value === 'string'){
                return value.replaceAll('\'', '')
            }
            return value;
        }else{
            if (node.type === '!') {
                let val = recursion(node.right);
                if(typeof val === 'boolean'){
                    return !val;
                }else{
                    return val=='false';
                }
            } else if (node.type === '*') {
                return recursion(node.left) * recursion(node.right);
            } else if (node.type === '/') {
                return recursion(node.left) / recursion(node.right);
            } else if (node.type === '%') {
                return recursion(node.left) % recursion(node.right);
            } else if (node.type === '+') {
                return recursion(node.left) + recursion(node.right);
            } else if (node.type === '-') {
                return recursion(node.left) - recursion(node.right);
            } else if (node.type === '>') {
                return recursion(node.left) > recursion(node.right);
            } else if (node.type === '<') {
                return recursion(node.left) < recursion(node.right);
            }else if (node.type === '<=') {
                return recursion(node.left) <= recursion(node.right);
            }else if (node.type === '>=') {
                return recursion(node.left) >= recursion(node.right);
            }else if (node.type === '==') {
                return recursion(node.left) == recursion(node.right);
            }else if (node.type === '!=') {
                return recursion(node.left) != recursion(node.right);
            }else if (node.type === '===') {
                return recursion(node.left) === recursion(node.right);
            }else if (node.type === '!==') {
                return recursion(node.left) !== recursion(node.right);
            }else if (node.type === '&&') {
                return recursion(node.left) && recursion(node.right);
            }else if (node.type === '||') {
                return recursion(node.left) || recursion(node.right);
            }else if (node.type === ':') {
                return recursion(node.left) +':'+ recursion(node.right);
            }else if (node.type === '?') {
                let val = recursion(node.left);
                let values = recursion(node.right);
                if(typeof val === 'boolean'){
                    if(val){
                        values = values.slice(0, values.indexOf(':')-values.length);
                    }else{
                        values = values.slice(values.indexOf(':')+1);
                    }
                }else{
                    if(val=='false'){
                        values = values.slice(values.indexOf(':')+1);
                    }else{
                        values = values.slice(0, values.indexOf(":")-values.length);
                    }
                }
                return values;
            }
        }
    }
    return recursion(node)
}

测试用例

//测试用例
let expression = '!true||true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!!true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '3>2&&3>4';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '(1+1)';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '(5-1)*2';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '2-(3-(1+4))';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '(1 + (2 - (3 * 4 / 5 + 6 - (7 + 8))) + (9 - 10) * 11 + 12) + 13';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '3<4&&5<4';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true||true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true||false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true&&!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true||!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true&&!(4<5)';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!!true&&!!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'false?true:!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'false?4-3:4+3';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = "on=='on'?'开启':'关闭'";
console.log(expression, '==>', exec(parse(expression)));

测试结果

!true||true ==> true  <--对比-->  true
!false ==> true  <--对比-->  true
!!false ==> false  <--对比-->  false
!true ==> false  <--对比-->  false
!!true ==> true  <--对比-->  true
3>2&&3>4 ==> false  <--对比-->  false
(1+1) ==> 2  <--对比-->  2
(5-1)*2 ==> 8  <--对比-->  8
2-(3-(1+4)) ==> 4  <--对比-->  4
(1 + (2 - (3 * 4 / 5 + 6 - (7 + 8))) + (9 - 10) * 11 + 12) + 13 ==> 23.6  <--对比-->  23.6
3<4&&5<4 ==> false  <--对比-->  false
true||true ==> true  <--对比-->  true
true||false ==> true  <--对比-->  true
true&&!false ==> true  <--对比-->  true
true||!false ==> true  <--对比-->  true
true&&!(4<5) ==> false  <--对比-->  false
!!true&&!!false ==> false  <--对比-->  false
false?true:!false ==> true  <--对比-->  true
false?4-3:4+3 ==> 7  <--对比-->  7
on=='on'?'开启':'关闭' ==> 开启

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芯晖闲云

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值