算法--逆波蘭表達式(數學逆波蘭表達式和交並集逆波蘭表達式)


一、前言

在通常的表達式中,二元運算符總是置於與之相關的兩個運算對象之間,所以,這種表示法也稱為中綴表示。每一運算符都置於其運算對象之后,稱為后綴表達式,后綴表達式又叫做逆波蘭表達式。它的優勢在於只用兩種簡單操作,入棧和出棧就可以搞定任何普通表達式的運算。其運算方式如下:如果當前字符為變量或者為數字,則壓棧,如果是運算符,則將棧頂兩個元素彈出作相應運算,結果再入棧,最后當表達式掃描完后,棧里的就是結果。

二、一般算法

將一個普通的中序表達式轉換為逆波蘭表達式的一般算法是:

(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);
    }
RpnExpression具體代碼

主要包括以下方法:
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;

            }
        }
    }
MathExpression

界面操作結果如下:

七、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);
        }
    }
IntersectionUnionExpresion

界面操作結果如下:

 八、總結

針對於二元運算符操作,完全可以使用逆波蘭表達式算法進行相應操作,而且,操作起來比較方便簡單。以上只是2個簡單的應用,也是去年的時候寫的,今天整理了下,希望對各位有所幫助。

參考文獻:http://baike.baidu.com/view/552648.htm

逆波蘭表達式VS2012代碼下載地址:RpnExpressionSolution.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM