算法--逆波兰表达式(数学逆波兰表达式和交并集逆波兰表达式)
一、前言
在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,所以,这种表示法也称为中缀表示。每一运算符都置于其运算对象之后,称为后缀表达式,后缀表达式又叫做逆波兰表达式。它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
二、一般算法
将一个普通的中序表达式转换为逆波兰表达式的一般算法是:
(1)首先构造一个运算符栈,此运算符在栈内遵循越往栈顶优先级越高的原则。
(2)读入一个用中缀表示的简单算术表达式,为方便起见,设该简单算术表达式的右端多加上了优先级最低的特殊符号“#”。
(3)从左至右扫描该算术表达式,从第一个字符开始判断,如果该字符是数字,则分析到该数字串的结束并将该数字串直接输出。
(4)如果不是数字,该字符则是运算符,此时需比较优先关系。做法如下:将该字符与运算符栈顶的运算符的优先关系相比较。如果,该字符优先关系高于此运算符栈顶的运算符,则将该运算符入栈。倘若不是的话,则将栈顶的运算符从栈中弹出,直到栈顶运算符的优先级低于当前运算符,将该字符入栈。
(5)重复上述操作(3)-(4)直至扫描完整个简单算术表达式,确定所有字符都得到正确处理,我们便可以将中缀式表示的简单算术表达式转化为逆波兰表示的简单算术表达式。
三、算法流程
程序化算法流程:
1、建立运算符栈stackOperator用于运算符的存储,压入'\0'。
2、预处理表达式,正、负号前加0(如果一个加号(减号)出现在最前面或左括号后面,则该加号(减号)为正负号) 。
3、顺序扫描表达式,如果当前字符是数字(优先级为0的符号),则直接输出该数字;如果当前字符为运算符或括号(优先级不为0的符号),则判断第4点 。
4、若当前运算符为'(',直接入栈;
若为')',出栈并顺序输出运算符直到遇到第一个'(',遇到的第一个'('出栈但不输出;
若为四则运算符,比较栈顶元素与当前元素的优先级: 如果栈顶元素运算符优先级 >= 当前元素的优先级,出栈并顺序输出运算符直到 栈顶元素优先级 < 当前元素优先级,然后当前元素入栈;如果栈顶元素 < 当前元素,直接入栈。
5、重复第3点直到表达式扫描完毕。
6、顺序出栈并输出运算符直到栈顶元素为'\0'。
四、相关类图
本文主要包括RpnExpression基类、MathExpression以及IntersectionUnionExpresion。
MathExpression主要用于计算简单数学运算(+、-、*和/)。
IntersectionUnionExpresion主要用于计算交并集运算(|、&)。
懒得用UML来画图,直接用VS2012生成的类图,如下所示:
五、RpnExpression的具体实现
RpnExpression为逆波兰表达式的抽象类,提供基础的方法和结构。
RpnExpression相关代码如下:
public abstract class RpnExpression { public static readonly char LeftBracket = '('; public static readonly char RightBracket = ')'; public static readonly char JoinChar = ','; public static readonly char EmptyChar = ' '; public bool IsBracket(string ch) { return ch == LeftBracket.ToString() || ch == RightBracket.ToString(); } public bool IsBracket(char ch) { return ch == LeftBracket || ch == RightBracket; } public abstract int GetOperationLevel(string operationChar); public abstract char[] OperationChars { get; } public bool IsBracketMatch(string expression) { if (string.IsNullOrWhiteSpace(expression)) { return true; } var bracketStack = new Stack<char>(); for (int index = 0; index < expression.Length; index++) { char currentChar = expression[index]; if (!IsBracket(currentChar)) { continue; } if (currentChar == LeftBracket) { bracketStack.Push(LeftBracket); } else { if (bracketStack.Count == 0) { return false; } if (bracketStack.Pop() != LeftBracket) { return false; } } } return bracketStack.Count == 0; } protected virtual string AdapteAndReplace(string expression) { return expression; } public string Value { get; private set; } /// <summary> /// 将中缀表达式转换为逆波兰表达式 /// </summary> /// <param name="expression">标准中缀表达式</param> /// <returns>标准逆波兰表达式</returns> public string ToExpression(string expression) { if (string.IsNullOrWhiteSpace(expression)) { return string.Empty; } this.Value = AdapteAndReplace(expression); if(string.IsNullOrWhiteSpace(this.Value)) { return string.Empty; } if(!IsBracketMatch(this.Value)) { throw new ArgumentException("It's not match for Bracket ')'."); } string[] splitArray = this.Value.Split(this.OperationChars, StringSplitOptions.RemoveEmptyEntries); if (!IsValid(splitArray)) { return string.Empty; } var operationStack = new Stack<string>(); var outputStack = new Stack<string>(); int currentIndex = 0; int splitIndex = 0; while (currentIndex < this.Value.Length) { string currentChar = this.Value.Substring(currentIndex, 1); int currentLevel = this.GetOperationLevel(currentChar); if (currentChar == EmptyChar.ToString()) { currentIndex++; continue; } if (currentLevel < 0 ) { outputStack.Push(splitArray[splitIndex]); currentIndex += splitArray[splitIndex].Length; splitIndex++; continue; } if (operationStack.Count == 0) { operationStack.Push(currentChar); currentIndex++; continue; } if (IsBracket(currentChar)) { if (currentChar == LeftBracket.ToString()) { operationStack.Push(currentChar); currentIndex++; } else { // 处理(),括号里面不存在任何内容的情况 if (operationStack.Peek() == LeftBracket.ToString()) { currentIndex++; operationStack.Pop(); continue; } // 处理右括号,一直检测到左括号 while (operationStack.Peek() != LeftBracket.ToString()) { string ch = operationStack.Pop(); outputStack.Push(ch); currentIndex++; if(operationStack.Count==0) { break; } } // 删除左括号 if(operationStack.Count==0) { throw new ArgumentException("It's not match for Bracket ')'."); } operationStack.Pop(); } continue; } string operation = operationStack.Peek(); //运算字符比运算符堆栈最后的级别高 直接推入运算符堆栈 if (currentLevel > GetOperationLevel(operation)) { operationStack.Push(currentChar); currentIndex++; } else { //运算字符不高于运算符堆栈最后的级别,则将运算符堆栈出栈,直到比其高为止 while (currentLevel <= GetOperationLevel(operation)) { outputStack.Push(operation); operationStack.Pop(); if (operationStack.Count == 0) { break; } operation = operationStack.Peek(); } operationStack.Push(currentChar); currentIndex++; } } while (operationStack.Count>0) { outputStack.Push(operationStack.Pop()); } if(outputStack.Count==0) { return string.Empty; } return string.Join(JoinChar.ToString(), outputStack.ToArray().Reverse()); } public abstract bool IsValid(string[] splitArray); public abstract object ComplieExpression(string expression, params object[] args); }
主要包括以下方法:
public abstract int GetOperationLevel(string operationChar); //获取操作级别
protected virtual string AdapteAndReplace(string expression) //转换和替换原有字符串
public string ToExpression(string expression) //将中缀表达式转换为逆波兰表达式
public abstract bool IsValid(string[] splitArray); //检测相关标识字符
public abstract object ComplieExpression(string expression, params object[] args); //编译表达式
六、MathExpression的具体实现和应用
MathExpression为解析数学逆波兰表达式的类,能够执行相应的数字运算。
MathExpression相关代码如下:
public class MathExpression: RpnExpression { public static readonly char AddChar = '+'; public static readonly char SubtractChar = '-'; public static readonly char DivideChar = '/'; public static readonly char MultiplyChar = '*'; private readonly char[] _operationChars = new char[] { AddChar, SubtractChar, MultiplyChar, DivideChar, LeftBracket, RightBracket }; public override char[] OperationChars { get { return this._operationChars; } } public override int GetOperationLevel(string operationChar) { switch (operationChar) { case "*": case "/": return 2; case "+": case "-": return 1; case "(": case ")": return 0; default: return -1; } } protected override string AdapteAndReplace(string expression) { if (string.IsNullOrWhiteSpace(expression)) { return string.Empty; } return expression.Replace(" ", ""); } /// <summary> /// 解逆波兰表达式 /// </summary> /// <param name="expression">标准逆波兰表达式</param> /// <returns>逆波兰表达式的解</returns> public override object ComplieExpression(string expression, params object[] args) { if (string.IsNullOrWhiteSpace(expression)) { return 0; } string[] splitValues = expression.Split(new char[] { JoinChar }); var numberStack = new Stack<double>(); for (int index = 0; index < splitValues.Length; index++) { int currentLevel = this.GetOperationLevel(splitValues[index]); if (currentLevel < 0) { numberStack.Push(ToDouble(splitValues[index])); } else if (currentLevel > 0) { // 为符号则将数字堆栈后两个数据解压并计算,将计算结果压入堆栈 if (numberStack.Count > 1) { double lastValue = numberStack.Pop(); double firstValue = numberStack.Pop(); double result = ComplieRpnExp(lastValue, firstValue, splitValues[index]); //压入计算结果 numberStack.Push(result); } } } return numberStack.Pop(); } public override bool IsValid(string[] splitArray) { if (splitArray == null || splitArray.Length == 0) { return false; } Regex regex = new Regex(@"^\d+$|^\-?\d*\.\d*$"); for (int index = 0; index < splitArray.Length; index++) { if (!regex.IsMatch(splitArray[index].Trim())) { throw new ArgumentException(splitArray[index]); } } return true; } public double ToDouble(string value) { double tempValue; if(double.TryParse(value,out tempValue)) { return tempValue; } return 0; } /// <summary> /// 计算逆波兰表达式 /// </summary> /// <param name="lastValue">最后压入数字堆栈的数字</param> /// <param name="firstValue">首先压入数字堆栈的数字</param> /// <param name="operation">操作运算符</param> /// <returns>返回计算结果</returns> private static double ComplieRpnExp(double lastValue, double firstValue, string operation) { switch (operation) { case "+": return firstValue + lastValue; case "-": return firstValue - lastValue; case "*": return firstValue * lastValue; case "/": return firstValue / lastValue; default: return 0; } } }
界面操作结果如下:
七、IntersectionUnionExpresion的具体实现和应用
IntersectionUnionExpresion为解析交并集逆波兰表达式的具体类,能够执行相应的交并运算。
IntersectionUnionExpresion相关代码如下:
public class IntersectionUnionExpresion : RpnExpression { public delegate void PushCompletedEventHandler(IndexInfoResult result, IList<IndexInfoResult> container); public event PushCompletedEventHandler PushCompleted; public delegate void CalculateCompletedEventHandler(IndexInfoResult firstValue, IndexInfoResult lastValue, IndexInfoResult result, IList<IndexInfoResult> container); public event CalculateCompletedEventHandler CalculateCompleted; public static readonly char AndChar = '&'; public static readonly char OrChar = '|'; private readonly char[] _operationChars = new char[] { OrChar, AndChar, LeftBracket, RightBracket }; public override char[] OperationChars { get { return this._operationChars; } } public override int GetOperationLevel(string operationChar) { switch (operationChar) { case "|": case "&": return 1; case "(": case ")": return 0; default: return -1; } } protected override string AdapteAndReplace(string expression) { if(string.IsNullOrWhiteSpace(expression)) { return string.Empty; } return expression.ToUpper().Replace("AND", AndChar.ToString()). Replace("OR", OrChar.ToString()).Replace(EmptyChar.ToString(), string.Empty); } public IList<IndexInfoResult> Container { get; set; } /// <summary> /// 解逆波兰表达式 /// </summary> /// <param name="expression">标准逆波兰表达式</param> /// <returns>逆波兰表达式的解</returns> public override object ComplieExpression(string expression, params object[] args) { if (string.IsNullOrWhiteSpace(expression)) { return null; } string[] splitValues = expression.Split(new char[] { JoinChar }); var codesStack = new Stack<IndexInfoResult>(); this.Container = this.Container ?? new List<IndexInfoResult>(); for (int index = 0; index < splitValues.Length; index++) { int currentLevel = this.GetOperationLevel(splitValues[index]); if (currentLevel < 0) { string condition = splitValues[index].Trim(); var result = this.Container.FirstOrDefault(p=>string.Equals(p.Mark,condition)); if (result == null) { throw new ArgumentNullException(condition); } codesStack.Push(result); if (this.PushCompleted != null) { this.PushCompleted(result, this.Container); } } else if (currentLevel > 0) { // 为符号则将数字堆栈后两个数据解压并计算,将计算结果压入堆栈 if (codesStack.Count > 1) { var lastValue = codesStack.Pop(); var firstValue = codesStack.Pop(); var result = this.ComplieRpnExp(firstValue.IndexInfos.Select(p=>p.StockCode), lastValue.IndexInfos.Select(p => p.StockCode), splitValues[index]); IndexInfoResult infoResult = new IndexInfoResult("("+firstValue.Mark + splitValues[index].Replace("&", " AND ").Replace("|", " OR ") + lastValue.Mark + ")"); foreach (var code in result) { infoResult.IndexInfos.Add(new IndexInfo(code)); } //压入计算结果 codesStack.Push(infoResult); if (this.CalculateCompleted != null) { this.CalculateCompleted(firstValue, lastValue, infoResult, this.Container); } } } } return codesStack.Pop(); } public override bool IsValid(string[] splitArray) { if (splitArray == null || splitArray.Length == 0) { return false; } Regex regex = new Regex(@"^#\d+$"); for (int index = 0; index < splitArray.Length; index++) { if (!regex.IsMatch(splitArray[index].Trim())) { throw new ArgumentException("error value:" + splitArray[index] + "."); } } return true; } public IEnumerable<string> GetAllMarks() { if (string.IsNullOrWhiteSpace(this.Value)) { yield return null; } Regex regex = new Regex(@"#\d+"); var collections = regex.Matches(this.AdapteAndReplace(this.Value)); foreach (Match match in collections) { yield return match.Groups[0].Value; } } private IEnumerable<string> ComplieRpnExp(IEnumerable<string> firstValue, IEnumerable<string> lastValue, string operation) { if (string.IsNullOrWhiteSpace(operation)) { return new List<string>(); } if(string.Equals(operation.Trim(),AndChar.ToString())) { return GetAndResult(firstValue,lastValue); } else if (string.Equals(operation.Trim(), OrChar.ToString())) { return GetOrResult(firstValue, lastValue); } return new List<string>(); } private IEnumerable<string> GetOrResult(IEnumerable<string> firstValue, IEnumerable<string> lastValue) { if (firstValue == null) { return lastValue ?? new List<string>(); } if (lastValue == null) { return firstValue ?? new List<string>(); } return firstValue.Union(lastValue); } private IEnumerable<string> GetAndResult(IEnumerable<string> firstValue, IEnumerable<string> lastValue) { if (firstValue == null || lastValue == null) { return new List<string>(); } return firstValue.Intersect(lastValue); } }
界面操作结果如下:
八、总结
针对于二元运算符操作,完全可以使用逆波兰表达式算法进行相应操作,而且,操作起来比较方便简单。以上只是2个简单的应用,也是去年的时候写的,今天整理了下,希望对各位有所帮助。
参考文献:http://baike.baidu.com/view/552648.htm
逆波兰表达式VS2012代码下载地址:RpnExpressionSolution.rar