c# 四则混合运算算法
2015-12-13 02:10
579 查看
对于四则混合运算其实在大三的时候就接触过,但是由于当时自己太菜,只顾着玩游戏跳课了。所以这个算法一直没有用代码实现过。当时学霸还给我简单的讲了一下方法,只因身为学渣的我,只想对学霸说一句,道理我都懂,但是用代码怎么实现的呢。
这次写这个实现四则混合运算功能的原因是,在项目中看到大牛写了一个关于这个的功能,但是他用的是遍历二叉树和递归的原理,并且这段代码是在一个比较大的项目中层次结构比较复杂,实话说,我看得是似懂非懂,所以想自己实现一下,在下面可以提一下我对大牛写的代码的原理的理解,可能不是很对。
今天说的也是利用了二叉树遍历的知识,不过没有对二叉树进行操作,只是利用了二叉树的中序与后序遍历的知识。下面将讲解一下四则混合运算的原理,然后上部分代码。
一 . 原理
1. 中序表达式和后序表达式
例如表达式(A+B)*C+E/D,我们试图将它分解,放到一个二叉树中,那么怎么放到一个二叉树中的,我们可以将这个表达式中的元素拆解成三个部分:
(A+B),C,E/D
我们发现A+B是二叉树A+B中序遍历的结果。令A+B的结果是F,则F*C是F*C中序遍历的结果,依次类推,可以得出二叉树如下:
![](http://img.blog.csdn.net/20151212223115720)
我们将这个二叉树进行中序遍历得到的就是如上的表达式。那么为什么要说这个呢。我们光看这个二叉树中序遍历的结果,对这个表达式进行词法分析,是得不出什么算法的。但是我们可以对这个二叉树的后序遍历得到的表达式进行词法分析的。
这个二叉树后序遍历的结果是:AB+C*ED/+
通过对后序表达式的词法分析,我们令F=A+B,G=F*C,H=E/D,I=G*H,这样我们就可以得到一个解决方法:每次遇到一个操作符,就将操作符的前两个操作数与该操作符进行计算。
现在大概的思路有了,那么中间需要解决的核心问题有如何将中序表达式转换成后序表达式?如何对后序表达式进行计算?那么且往下看。
2. 运算符优先级
首先要说明的是这里的优先级不是四则运算中优先级那么简单。我先列出一张优先级表,然后再说明情况:
![](http://img.blog.csdn.net/20151213004103074)
这个表显示的是栈外的运算符与栈内运算符之间对比的优先级表。
我们可以将上面的表达式入队,然后进行压栈和出栈操作,来得出后序表达式。在表达式后面加一个“#”运算符,那么表达式就是(A+B)*C+E/D#,入队后就是#D/E+C*)B+A(,“#”为队尾元素,”(“为队头元素。设置一个栈,栈顶元素为”#”。
转化规则:将队列中的元素进行遍历,如果是操作符则直接出队,遇到操作符的时候要与栈顶进行比较,如果优先级大于栈顶元素则该操作符出队,如果小于栈顶元素,则栈顶元素出栈,该元素入栈,成为新的栈顶,这样直到遍历结束,最后得到的结果就是后序表达式。这里如果优先级相等则直接将栈顶元素出栈,该元素也出队,并且不加入到后序表达式中。
这里用了luoweifu的转化图进行说明:
![](http://img.blog.csdn.net/20151213013115042)
可以参照优先级看懂上面的图的就说明你理解了实现原理。这就是著名的逆波兰表达式。
3. 代码
下面给出源码下载链接:
http://download.csdn.net/detail/u012058778/9350639
说明:源码是本人用多半天时间纯手写的,如果各位测试的时候出现bug可以在回复中说明一下,或者代码有不妥的地方都可以回复一下,博主一定虚心接受。请不吝赐教。
然后上面说到大牛使用的方法,我是这样理解的,他可能是递归生成二叉树,然后对二叉树进行计算(这里的二叉树只有三个结点),将计算的结果在与后面的表达式进行递归生成二叉树,然后计算,直到得出结果。具体实现方法还有待研究,不过我觉得上面的压栈方法效率会高些。
这次写这个实现四则混合运算功能的原因是,在项目中看到大牛写了一个关于这个的功能,但是他用的是遍历二叉树和递归的原理,并且这段代码是在一个比较大的项目中层次结构比较复杂,实话说,我看得是似懂非懂,所以想自己实现一下,在下面可以提一下我对大牛写的代码的原理的理解,可能不是很对。
今天说的也是利用了二叉树遍历的知识,不过没有对二叉树进行操作,只是利用了二叉树的中序与后序遍历的知识。下面将讲解一下四则混合运算的原理,然后上部分代码。
一 . 原理
1. 中序表达式和后序表达式
例如表达式(A+B)*C+E/D,我们试图将它分解,放到一个二叉树中,那么怎么放到一个二叉树中的,我们可以将这个表达式中的元素拆解成三个部分:
(A+B),C,E/D
我们发现A+B是二叉树A+B中序遍历的结果。令A+B的结果是F,则F*C是F*C中序遍历的结果,依次类推,可以得出二叉树如下:
我们将这个二叉树进行中序遍历得到的就是如上的表达式。那么为什么要说这个呢。我们光看这个二叉树中序遍历的结果,对这个表达式进行词法分析,是得不出什么算法的。但是我们可以对这个二叉树的后序遍历得到的表达式进行词法分析的。
这个二叉树后序遍历的结果是:AB+C*ED/+
通过对后序表达式的词法分析,我们令F=A+B,G=F*C,H=E/D,I=G*H,这样我们就可以得到一个解决方法:每次遇到一个操作符,就将操作符的前两个操作数与该操作符进行计算。
现在大概的思路有了,那么中间需要解决的核心问题有如何将中序表达式转换成后序表达式?如何对后序表达式进行计算?那么且往下看。
2. 运算符优先级
首先要说明的是这里的优先级不是四则运算中优先级那么简单。我先列出一张优先级表,然后再说明情况:
这个表显示的是栈外的运算符与栈内运算符之间对比的优先级表。
我们可以将上面的表达式入队,然后进行压栈和出栈操作,来得出后序表达式。在表达式后面加一个“#”运算符,那么表达式就是(A+B)*C+E/D#,入队后就是#D/E+C*)B+A(,“#”为队尾元素,”(“为队头元素。设置一个栈,栈顶元素为”#”。
转化规则:将队列中的元素进行遍历,如果是操作符则直接出队,遇到操作符的时候要与栈顶进行比较,如果优先级大于栈顶元素则该操作符出队,如果小于栈顶元素,则栈顶元素出栈,该元素入栈,成为新的栈顶,这样直到遍历结束,最后得到的结果就是后序表达式。这里如果优先级相等则直接将栈顶元素出栈,该元素也出队,并且不加入到后序表达式中。
这里用了luoweifu的转化图进行说明:
可以参照优先级看懂上面的图的就说明你理解了实现原理。这就是著名的逆波兰表达式。
3. 代码
//判断是否为操作符 public bool isOperateors(string input) { if (input == "+" || input == "-" || input == "*" || input == "/"|| input == "(" || input == ")" || input == "#") { return true; } else return false; } /// <summary> /// 分割表达式,并入队列 /// </summary> /// <param name="express"></param> /// <returns>Queue</returns> public Queue<string> SplitExpress(string express) { express += "#"; Queue<string> q = new Queue<string>(); string tempNum=string.Empty; char[] arryExpress = express.ToArray<char>(); int i = 0; int j = 0; while (j<express.Length) { if (isOperateors(arryExpress[j].ToString())) { if (i != j) { tempNum = express.Substring(i, j - i); q.Enqueue(tempNum); q.Enqueue(arryExpress[j].ToString()); i = j + 1; } else { q.Enqueue(arryExpress[j].ToString()); i++; } } j++; } //q.Enqueue("#"); return q; } /// <summary> /// 中序表达式转换为后序表达式 /// </summary> /// <param name="q"></param> /// <returns>string:后序表达式</returns> public List<string> InorderToPostorder(Queue<string> q) { List<string> posterOrder = new List<string>(); Stack<string> inOrder = new Stack<string>(); inOrder.Push("#"); int count = q.Count; for (int i = 0; i < count;i++ ) { string item = q.Dequeue(); if (isOperateors(item)) { string m = inOrder.First(); int n = Priority.isPriority(Priority.dicOperators[inOrder.First()], Priority.dicOperators[item]); while (n == 1) { string temp = inOrder.Pop(); if (temp != "(" && temp != ")") { posterOrder.Add(temp); } n = Priority.isPriority(Priority.dicOperators[inOrder.First()], Priority.dicOperators[item]); } if (n == 2) { inOrder.Pop(); } else if (n != -1) { inOrder.Push(item); } else { return null; } } else { posterOrder.Add(item); } } return inOrder.Count == 0 ? posterOrder : null; } /// <summary> /// 计算后序表达式 /// </summary> /// <param name="PostorderExpress"></param> /// <param name="result"></param> /// <returns></returns> public bool IsResult(List<string> PostorderExpress, out decimal result) { if (PostorderExpress != null) { try { PostorderExpress.Add("#"); string[] tempArry = PostorderExpress.ToArray(); int length = tempArry.Length; int i = 0; while (tempArry[i] != "#") { if (isOperateors(tempArry[i])) { tempArry[i - 2] = Arithmetic(tempArry[i - 2], tempArry[i - 1], tempArry[i]); for (int j = i; j < length; j++) { if (j + 1 < length) tempArry[j - 1] = tempArry[j + 1]; } length -= 2; i -= 2; } i++; } result = decimal.Parse(tempArry[0]); return true; } catch (Exception e) { result = 0; return false; } } else { result = 0; return false; } } //计算方法 public string Arithmetic(string x,string y,string operators) { decimal a = decimal.Parse(x); decimal b = decimal.Parse(y); decimal result = 0; switch (operators) { case "+": result = a + b; break; case "-": result = a - b; break; case "*": result = a * b; break; case "/": result = a / b; break; } return result.ToString(); }
下面给出源码下载链接:
http://download.csdn.net/detail/u012058778/9350639
说明:源码是本人用多半天时间纯手写的,如果各位测试的时候出现bug可以在回复中说明一下,或者代码有不妥的地方都可以回复一下,博主一定虚心接受。请不吝赐教。
然后上面说到大牛使用的方法,我是这样理解的,他可能是递归生成二叉树,然后对二叉树进行计算(这里的二叉树只有三个结点),将计算的结果在与后面的表达式进行递归生成二叉树,然后计算,直到得出结果。具体实现方法还有待研究,不过我觉得上面的压栈方法效率会高些。
相关文章推荐
- C#委托例子
- C#哈希表用法
- C#5.0之委托
- 了解C#文件操作
- Code Style: C# write fixed structure code by using LAMDA expression
- C#属性和修饰符
- C#_垃圾回收
- C#_异常捕捉
- C#之代码构成
- C#_事件的使用
- c# Unicode字符串的解码
- C#_委托的使用
- C#学习之文件传输
- C#_索引器的使用
- C#_属性的封装(get和set)
- C#_可变长参数列表的使用
- C#_HashTable和SortedList的使用方法
- C#_Stack的使用方法
- C#新DataColumn类Type生成的方法类型参数
- C# 写入xml文件