基本思路

首先,用户输入的待求表达式,也就是中缀表达式,对于人来说,这个很好理解,但是对于计算机,后缀表达式求值更加容易。如果看成一棵二叉树,其实中缀表达式就是对一个二叉树的中序遍历,后缀表达式(也叫逆波兰表达式)就是后序遍历的结果。那么主要思路就来了:先把中缀表达式转换成后缀表达式,再对后缀表达式进行求值。

步骤一,中缀表达式转后缀表达式

两个数据结构:

1,后缀表达式队列

用于存放最后的后缀表达式

2,操作符栈

对用户输入的操作符进行处理

算法伪代码:

/*
依次读入用户输入的中缀表达式
如果是数字,直接入队
如果是操作符,如果栈为空,直接入栈如果操作符为 ( 直接入栈如果操作符为 ) ,依次出栈,直到遇到第一个(,(和)都不入栈如果是其他操作符(+ - * /),则和栈顶元素进行比较优先级如果栈顶元素优先级大于等于(*和/ > +和-)操作符 ,则出栈,直到栈顶元素优先级小于操作符或栈为空
*/
//伪代码
//后缀表达式队列
queue num;
//操作符栈
stack option;
while(scanf("%c",&x)){if(x 是数){num.addBack(x);    //入队}else if(option.empty() || x == '('){//如果栈为空,直接入栈,如果操作符为 ( 直接入栈option.push(x) }else if(x == ')'){//如果操作符为 ) ,依次出栈,直到遇到第一个(,(和)都不入栈while((tmp = option.pop()) != '('){num.addBack(tmp); }}else{//只是获取栈首元素,没有出栈while(1){tmp = option.head();if(tmp 优先级大于等于x){tmp1 = option.pop();num.addBack(tmp1); }else{break;}}option.push(x);}
}
//最后把所有操作符入队
while(!option.empty()){tmp = option.pop();num.addBack(tmp);
}

步骤二,后缀表达式计算值

数据结构:

结果栈

用于存放计算的中间过程的值和最终结果

算法伪代码:

/*
依次从后缀表达式队列中取值
如果是值,直接入栈
如果是操作符,从队列中取出两个数,进行运算(后取出来的数作为第一个运算符),计算结果再次压栈
*/
//用于存放结果的栈
stack res;
//后缀表达式队列,上一步求出来的
queue num;
while(!num.empty()){//取队首tmp = num.getFront();if(tmp 是数){res.push(tmp);}else{num1 = num.pop();num2 = num.pop();num3 = num2 tmp num1;res.push(num3);}
}

步骤三,转二叉树

算法伪代码:

  /*主要是在计算逆波兰表达式的时候,构造出一棵二叉树,构造思路和计算时差不多,如果是数,就直接构造节点,如果是操作符,就把前面两个节点提出来作为左右节点*///用于存放所有临时节点和最后根节点的数组
treeNode all[MAX];
int n=0;
//后缀表达式队列,上一步求出来的
queue num;
while(!num.empty()){//取队首tmp = num.getFront();if(tmp 是数){res[n++] = tmp;}else{num1 = all[--n];num2 = all[--n];tmp.left = num1;tmp.right = num2;all[n++] = tmp;}
}

运行情况

完整代码

  /*** 计算算术表达式的值,支持 加减乘除括号 运算* 思路是,先把用户输入的表达式(中缀表达式,中序遍历),格式化成逆波兰表达式(后缀表达式,后序遍历序列)* 再对后缀表达式进行求值*/
#include <stdio.h>
#include <string.h>
//通用链表
#include "cl_link.h"#define MAX 1024
#define IS_OPTION '0'
#define IS_NUM '1'char* optionAll = "+-*/()";typedef struct bTree{char type;double num;char option;struct bTree* left;struct bTree* right;
}bTree;//运算符节点
typedef struct optionNode{//用于标志运算符char type;cl_link_node node;char option;
}optionNode;//操作数节点
typedef struct numNode{// 用于标志操作数char type;cl_link_node node;double num;
}numNode;//运算符栈
cl_link* optionStack = NULL;
//数字队列
cl_link* numQueue = NULL;
/*** 比较是否是运算符* @param  optionAll 运算符表* @param  aim       比较对象* @return           1 是 0 不是*/
int isOption(const char* optionAll,const char aim)
{for (size_t i = 0; i < strlen(optionAll); i++) {if(aim == *(optionAll+i)){return 1;}}return 0;
}
/*** 比较优先级* @return 1 src >= des*         0 src < des*/
int isMaxLevel(const char src, const char des)
{if(src == '*' || src == '/')return 1;if((src == '+' || src == '-') && (des == '+' || des == '-'))return 1;return 0;
}
void* show(void* arg)
{numNode* node = cl_link_get_data(arg, numNode, node);if(node->type == IS_NUM){printf("%0.2f,", node->num);}else{optionNode* node1 = cl_link_get_data(arg, optionNode, node);printf("%c,", node1->option);}return NULL;
}
//计算
double cal(double num1, double num2, char option)
{switch (option) {case '+':return num1+num2;case '-':return num1-num2;case '*':return num1*num2;case '/':return num1/num2;}return 0;
}void pre(bTree* root){if(root != NULL){if(root->type == IS_NUM){printf("%0.2f,", root->num);}else{printf("%c,", root->option);}pre(root->left);pre(root->right);}
}
void mid(bTree* root){if(root != NULL){mid(root->left);if(root->type == IS_NUM){printf("%0.2f,", root->num);}else{printf("%c,", root->option);}mid(root->right);}
}
void back(bTree* root){if(root != NULL){back(root->left);back(root->right);if(root->type == IS_NUM){printf("%0.2f,", root->num);}else{printf("%c,", root->option);}}
}
int main()
{//初始化运算符栈optionStack = cl_link_create();//初始化逆波兰空队列,即后序遍历结果numQueue = cl_link_create();//把输入格式化成后缀表达式 开始char expr[MAX];scanf("%s", expr);for (size_t i = 0; i < strlen(expr); i++) {//假设输入完全合法,不是运算符就是运算数if(!isOption(optionAll, *(expr+i))){//运算数,直接入队,numQueuenumNode* node = malloc(sizeof(numNode));node->num = atoi(expr+i);node->type = IS_NUM;int tmp = node->num;while(tmp/10 > 0){i++;tmp /= 10;}//入队cl_link_add_back(numQueue, cl_link_get_node(node, numNode, node));}else{/*** 如果栈为空,直接入栈* 如果是( 直接入栈* 如果是) 则依次出栈到队列中,直到遇到第一个(* 如果X是其他运算符,则依次与栈顶元素比较优先级,* 如果栈顶元素的优先级大于等于X,* 则出栈,直到栈顶元素的优先级小于X或者栈为空。* 否则 入栈*/optionNode* node = malloc(sizeof(optionNode));node->type = IS_OPTION;node->option = *(expr+i);if(optionStack->sum == 0 || *(expr+i) == '('){//如果栈为空,或者是(,直接入栈cl_link_push(optionStack, cl_link_get_node(node, optionNode, node));}else if(*(expr+i) == ')'){//如果是) 则依次出栈到队列中,直到遇到第一个(while(1){optionNode* tmp = cl_link_get_data(cl_link_pop(optionStack), optionNode, node);if(tmp->option == '('){break;}else{cl_link_add_back(numQueue, cl_link_get_node(tmp, optionNode, node));}}}else{printf("%C,", *(expr+i));//如果X是其他运算符,则依次与栈顶元素比较优先级,如果栈顶元素的优先级大于等于X,则出栈,直到栈顶元素的优先级小于X或者栈为空。while(1){//得到栈顶元素optionNode* tmp = cl_link_get_data(cl_link_get_head(optionStack), optionNode, node);if(isMaxLevel(tmp->option, *(expr+i))){//如果优先级大于运算符,就出栈到逆波兰队列optionNode* tmp = cl_link_get_data(cl_link_pop(optionStack), optionNode, node);cl_link_add_back(numQueue, cl_link_get_node(tmp, optionNode, node));}else{break;}}optionNode* node = malloc(sizeof(optionNode));node->type = IS_OPTION;node->option = *(expr+i);cl_link_push(optionStack, cl_link_get_node(node, optionNode, node));}}}/*** 若运算符还有,则直接出栈*/while(optionStack->sum != 0){optionNode* tmp = cl_link_get_data(cl_link_pop(optionStack), optionNode, node);cl_link_add_back(numQueue, cl_link_get_node(tmp, optionNode, node));}//把输入格式化成后缀表达式 结束printf("\n逆波兰表达式(后序遍历):");//打印逆波兰式,就是后序遍历结果cl_link_each(numQueue, NULL, show);bTree* allTreeNode[MAX];int n = 0;//计算逆波兰表达式结果,用于存放临时结果和最终结果cl_link* res = cl_link_create();while(numQueue->sum != 0){numNode* tmp = cl_link_get_data(cl_link_pop(numQueue), numNode, node);if(tmp->type == IS_NUM){//如果是数,直接入栈cl_link_push(res, cl_link_get_node(tmp, numNode, node));bTree* newNode = malloc(sizeof(bTree));newNode->left = NULL;newNode->right = NULL;newNode->type = IS_NUM;newNode->num = tmp->num;allTreeNode[n++] = newNode;}else{//如果是操作符,则pop出栈顶两个元素,进行运算,再入栈optionNode* tmpOption = (optionNode*)tmp;numNode* num1 = cl_link_get_data(cl_link_pop(res), numNode, node);numNode* num2 = cl_link_get_data(cl_link_pop(res), numNode, node);numNode* node = malloc(sizeof(numNode));node->num = cal(num2->num, num1->num, tmpOption->option);node->type = IS_NUM;cl_link_push(res, cl_link_get_node(node, numNode, node));bTree* newNode = malloc(sizeof(bTree));newNode->left = NULL;newNode->right = NULL;newNode->type = IS_OPTION;newNode->option = tmpOption->option;newNode->right = allTreeNode[--n];newNode->left = allTreeNode[--n];allTreeNode[n++] = newNode;}}numNode* resData = cl_link_get_data(cl_link_get_head(res), numNode, node);printf("\n运算结果是%0.2f\n", resData->num);printf("前序遍历\n");pre(allTreeNode[0]);printf("\n中序遍历\n");mid(allTreeNode[0]);printf("\n后序遍历\n");back(allTreeNode[0]);printf("\n\n");return 0;
}

涉及的队列和栈数据结构代码

cl_link.h

#ifndef _CL_LINK_H
#define _CL_LINK_H#include <pthread.h>
#include <malloc.h>
#include <unistd.h>
#include <stdlib.h>#define ADD_SUCCESS (0)
#define ADD_FAIL (-1)
#define DELETE_SUCCESS (0)
#define SELETE_FAIL (-1)
#define CANFIND (1)
#define NOTFIND (0)typedef struct cl_link_node cl_link_node;
typedef struct cl_link cl_link;/*** 链表节点,其他结构体需要使用链表数据结构,只需包含此节点* @return*/
typedef struct cl_link_node{cl_link_node* next;cl_link_node* prev;
}cl_link_node;/*** 通用链表对象* @return*/
typedef struct cl_link{pthread_mutex_t     cl_link_mutex;  //链表锁cl_link_node        cl_link_head;   //链表头cl_link_node        cl_link_tail;   //链表尾int                 sum;            //节点数
}cl_link;#define cl_link_get_node(aim, type, node)       \(cl_link_node *) ((u_char *) aim + offsetof(type, node))#define cl_link_get_data(aim, type, node)      \(type *) ((u_char *) aim - offsetof(type, node))/*** 创建一个链表对象* @return 链表对象地址*/
cl_link* cl_link_create();/*** 链表的压栈操作* @param  link 链表对象* @param  node 新节点* @return      压栈状态*/
int cl_link_push(cl_link* link, void* node);/*** 链表的出栈操作* @param link 链表对象*/
void* cl_link_pop(cl_link* link);
//获取栈顶元素
void* cl_link_get_head(cl_link* link);/*** 对每个节点进行操作* @param link    链表对象* @param res     返回值* @param handler 处理函数*/
void cl_link_each(cl_link* link, void* res[], void* (*handler)(void* node));/*** 队尾添加元素* @param  link 队列对象* @param  node 新节点* @return      添加状态*/
int cl_link_add_back(cl_link* link, void* node);/*** 队头获取元素* @param  link 队列对象* @return      取得的元素*/
void* cl_link_get_front(cl_link* link);/*** 根据key查找节点* @param link      链表对象* @param key       关键字* @param condition 条件*/
void* cl_link_find(cl_link* link, void* key, int (*condition)(void* node, void* key));#endif

cl_link.c

#include "cl_link.h"/*** 创建一个链表对象* @return 链表对象地址*/
cl_link* cl_link_create()
{cl_link* link = malloc(sizeof(cl_link));if(link == NULL){write(STDERR_FILENO, "malloc error", sizeof("malloc error"));return NULL;}pthread_mutex_init(&(link->cl_link_mutex), NULL);pthread_mutex_lock(&(link->cl_link_mutex));link->cl_link_head.next = &(link->cl_link_tail);link->cl_link_head.prev = &(link->cl_link_tail);link->cl_link_tail.next = &(link->cl_link_head);link->cl_link_tail.prev = &(link->cl_link_head);link->sum = 0;pthread_mutex_unlock(&(link->cl_link_mutex));return link;
}/*** 链表的压栈操作* @param  link 链表对象* @param  node 新节点* @return      压栈状态*/
int cl_link_push(cl_link* link, void* node)
{cl_link_node* new_node = (cl_link_node*)node;pthread_mutex_lock(&(link->cl_link_mutex));if(link){new_node->next = link->cl_link_head.next;link->cl_link_head.next->prev = new_node;link->cl_link_head.next = new_node;new_node->prev = &(link->cl_link_head);link->sum++;pthread_mutex_unlock(&(link->cl_link_mutex));return ADD_SUCCESS;}else{pthread_mutex_unlock(&(link->cl_link_mutex));return ADD_FAIL;}
}/*** 链表的出栈操作* @param link 链表对象*/
void* cl_link_pop(cl_link* link)
{pthread_mutex_lock(&(link->cl_link_mutex));if(link->sum){cl_link_node* aim = link->cl_link_head.next;link->cl_link_head.next = aim->next;aim->next->prev = &(link->cl_link_head);link->sum--;pthread_mutex_unlock(&(link->cl_link_mutex));return aim;}else{pthread_mutex_unlock(&(link->cl_link_mutex));return NULL;}
}void* cl_link_get_head(cl_link* link)
{return link->cl_link_head.next;
}/*** 对每个节点进行操作* @param link    链表对象* @param res     返回值* @param handler 处理函数*/
void cl_link_each(cl_link* link, void* res[], void* (*handler)(void* node))
{pthread_mutex_lock(&(link->cl_link_mutex));int n = link->sum;void* r;cl_link_node* p = link->cl_link_head.next;cl_link_node* todo = p;while(n){todo = p;p = p->next;r = handler(todo);if(res != NULL){res[link->sum-n] = r;}n--;}pthread_mutex_unlock(&(link->cl_link_mutex));
}/*** 队尾添加元素* @param  link 队列对象* @param  node 新节点* @return      添加状态*/
int cl_link_add_back(cl_link* link, void* node)
{cl_link_node* new_node = (cl_link_node*)node;pthread_mutex_lock(&(link->cl_link_mutex));if(link){new_node->next = &(link->cl_link_tail);new_node->prev = link->cl_link_tail.prev;link->cl_link_tail.prev->next = new_node;link->cl_link_tail.prev = new_node;link->sum++;pthread_mutex_unlock(&(link->cl_link_mutex));return ADD_SUCCESS;}else{pthread_mutex_unlock(&(link->cl_link_mutex));return ADD_FAIL;}
}/*** 队头获取元素* @param  link 队列对象* @return      取得的元素*/
void* cl_link_get_front(cl_link* link)
{return cl_link_pop(link);
}/*** 根据key查找节点* @param link      链表对象* @param key       关键字* @param condition 条件*/
void* cl_link_find(cl_link* link, void* key, int (*condition)(void* node, void* key))
{pthread_mutex_lock(&(link->cl_link_mutex));int n = link->sum;cl_link_node* p = link->cl_link_head.next;while(n){if(condition(p, key) == CANFIND){pthread_mutex_unlock(&(link->cl_link_mutex));return p;}p = p->next;n--;}pthread_mutex_unlock(&(link->cl_link_mutex));return NULL;
}

转自:https://www.jianshu.com/p/1f3622924fde

算术表达式的实现,支持加减乘除,括号运算,表达式转二叉树相关推荐

  1. 用计算机源码计算加法,MFC实现简单计算器(支持加减乘除和括号运算)

    [实例简介] 自己写的计算器,支持加减乘除和括号运算.开发环境为VS2010,MFC框架.代码内容简单不复杂适合初学者参考. [实例截图] [核心代码] Calculator[VS2010_MFC] ...

  2. sybase不支持的条件表达式_包教包会!7段代码带你玩转Python条件语句(附代码)...

    来源:大数据 本文约5200字,建议阅读10分钟. 本文介绍了Python条件语句常用的7段代码. [ 导 读 ]条件语句通过一个或多个布尔表达式的执行结果(真值或假值)决定下一步的执行方向.所谓布尔 ...

  3. 把算术表达式 表示为一个c语言表达式,正确的写法是____,C与C++程序设计-中国大学mooc-题库零氪...

    第一周:从问题到C语言程序设计 1.1 计算机的问题求解方法随堂测验 1.算法的描述方法除了自然语言之外,还包括: A.流程图 B.NS流程图 C.伪代码 D.程序语言 2.结构化程序设计方法包含三种 ...

  4. 设计一个算法,将一般算术表达式转化为逆波兰表达式,并求逆波兰表达式的值

    栈的设计与使用 实验内容 设计一个算法,将一般算术表达式转化为逆波兰表达式,并求逆波兰表达的值 解题思路 (1)一般算术表达(中缀表达),如#3×(4+2)/2-5#,#为表达式界定符,逆波兰表达式( ...

  5. 在javascript中使用el表达式(jsp中的javascript中支持写el表达式,毋庸置疑,单独的js文件中不支持写el表达式,别钻牛角尖)

    哎,够背的,最后4张图怎么调整都很小,看不清楚,所以大家还是看我这篇文章吧在javascript中使用el表达式(图片清晰版,有图有真相),这篇文章的图片可以正常看清楚,跟我这篇文章是一样的内容! 有 ...

  6. php逆波兰表达式,PHP根据数字的字符表达式计算出结果(转换成逆波兰式再求解)[转]...

    这个简单的计算器采用的是逆波兰式来做的,仅支持加减乘除四种运算,纯粹个人练习记录一下,还望多多支持. 用法 require 'Calc.php'; $calc = new Calc('(1+9)/2' ...

  7. java juel表达式_activiti 自定义函数解析juel表达式

    activiti是支持juel表达式的,这个也很好用,但实际过程中需要支持类方法及变量. 原来项目中解析juel,这边有自定义一个方法的 代码如下: public String getStringBy ...

  8. 前缀、中缀和后缀表达式详解,中缀表达式到后缀表达式的转换规则,以及后缀表达式的计算规则,附计算代码

    1. 中缀.前缀和后缀表达式 1.1 中缀表达式 首先,中缀表达式的这个"缀"指运算符在两个操作数的位置.中缀表达式其实就是我们常用的算术表达式,比如 2 + 9 - (32 * ...

  9. C# Lambda表达式详解,及Lambda表达式树的创建

    每次写博客,第一句话都是这样的:程序员很苦逼,除了会写程序,还得会写博客!当然,希望将来的一天,某位老板看到此博客,给你的程序员职工加点薪资吧!因为程序员的世界除了苦逼就是沉默.我眼中的程序员大多都不 ...

最新文章

  1. Java基础之Hibernate
  2. [原创]什么是CMM?
  3. 好文推荐 | MySQL binlog应用场景与原理深度剖析
  4. IoT 云服务加速产业创新,推进规模商用
  5. Linux复习资料(一)、VM虚拟机安装教程
  6. java的abstract的意思_java – spring中的abstract =“true”是什么意思?
  7. Liferay –简单主题开发
  8. PowerDesigner(二)-项目和框架矩阵
  9. php properties,PHP ReflectionClass getStaticProperties()用法及代码示例
  10. AVPlayer 音视频缓存方案
  11. mysql按每月分组统计_MySql按周,按月,按日分组统计数据
  12. 小象学院数据分析笔记:绘制常见的图形
  13. python aes加密_在不到5分钟的时间内用python编码aes128位加密
  14. 程序设计思维与实践 Week15 实验
  15. 如何去除图片中的白色背景(变透明)
  16. xml根据属性去重。如csprj去重
  17. BZOJ1067: [SCOI2007]降雨量(线段树)
  18. 手机测试定义和测试方法
  19. 如何使用手机里的Windows系统云桌面?
  20. 数据分析系列之Python数据分析简介

热门文章

  1. 网易2019实习生Java编程题
  2. Orion算法:GOOGLE干掉百度的核武器?
  3. 贝塞尔曲线 cubic-bezier()
  4. 谈谈你的GC调优思路?
  5. python 关键字参数为什么只能出现在最后_Python笔记2——默认参数,可变参数,关键字参数,参数组合...
  6. 电脑复制粘贴_手机扫一扫,现实物体隔空复制粘贴进电脑!北大校友的AI新研究,现在变成AR酷炫应用...
  7. java 如何将word 转换为ftl_如何把word转化为pdf?职场达人必需的转换技巧
  8. Mysql数据库(八)——mysql高阶语句(中)
  9. springboot介绍_Spring Boot 主类及目录结构介绍!
  10. android.process.media+sd,android P系统WRITE_MEDIA_STORAGE添加读写SD卡权限