1. 程式人生 > >資料結構之棧的運用

資料結構之棧的運用

        這兩天覆習資料結構的時候在學習棧,學習中碰到幾個練習題,在這裡附上最後的結果,全部通過棧來實現。
        注:以下程式碼全部用的都是前面學習時自己新建的棧,如果有地方和系統自帶的棧不一樣的,可以去檢視前面釋出的《資料結構之棧》地址如下:
https://blog.csdn.net/qq_33575542/article/details/81464109

題目一

        利用棧的特點將2進位制轉化為其他進位制

        在瞭解二進位制與其他進位制的區別後可以很快寫出相應程式碼。
        二進位制與十進位制之間,二進位制的每個位乘以相對應位的2的位次冪再把所有結果相加就能得到答案。例如:“1001”,等於1*2^3+0*2^2+0*2^1+1*2^0=9。所以1001對應的十進位制就是9 。冪從0開始網上累加即可。
        而二進位制對於八進位制和十六進位制而言,一個二進位制“11111”,對應著八進位制的“37”也就是“011   111”,把這兩個三位的二進位制轉化為10進位制就能得到8進位制了。
        原因是因為,在計算機中,所有的資料都是以二進位制來儲存的。8是2的3次方,因此,每個8進位制的資料其實就是3個二進位制來儲存的。同理,16是2的4次方,因此16進位制的資料是4個二進位制來儲存的。
        綜上,可以得到以下程式碼,這裡列舉出兩種辦法,一種是手動寫出演算法,實現進位制間的轉化,另一種則是通過C#中自帶的演算法函式進行進位制的轉化。

public class Solution1
{
    //二進位制轉換為十進位制
    public string ConvertOf2To10(string binary)
    {
        int decimalNum = 0;
        MyStack<int> stack = new MyStack<int>(binary.Length);

        //把二進位制入棧
        for (int i = binary.Length - 1; i >= 0; i--)
        {
            stack.Push(int
.Parse(binary[i].ToString())); } int pow = 0; //計算冪 while (!stack.IsEmpty()) { decimalNum += stack.Pop() * (int)Math.Pow(2, pow); pow++; } return decimalNum.ToString(); } //二進位制轉換為八進位制 public string
ConvertOf2To8(string binary) { StringBuilder octonaryNumber = new StringBuilder(); MyStack<int> binaryStack = new MyStack<int>(); MyStack<int> octonaryStack = new MyStack<int>(); int number = 0, count = 0; for (int i = 0; i < binary.Length; i++) { binaryStack.Push(binary[i] - '0'); } //把二進位制轉化為8進位制存入棧中 while (!binaryStack.IsEmpty()) { number += binaryStack.Pop() * (int)Math.Pow(2, count); count++; //每取三個二進位制的數就轉化為八進位制儲存進八進位制的棧中 if (count == 3) { octonaryStack.Push(number); number = 0; count = 0; continue; } } //如果二進位制的長度不是3的倍數,那麼把剩餘的值存入棧,如“1111”完成迴圈後得到“1”和“111”,1也需要存入棧中 if (number != 0) octonaryStack.Push(number); while (!octonaryStack.IsEmpty()) { octonaryNumber.Append(octonaryStack.Pop()); } return octonaryNumber.ToString(); } //二進位制轉換為十六進位制,C#自帶方法解決 public string ConvertOf2To16(string binary) { string hexadecimalNumber = ""; //把二進數轉換為十進位制數 int intTen = Convert.ToInt32(binary.ToString(), 2); //把十進位制數轉換為十六進位制 hexadecimalNumber = Convert.ToString(intTen, 16); return hexadecimalNumber; } }

通過測試程式碼:

private static void Test1()
{
    Solution1 s = new Solution1();
    Console.WriteLine(s.ConvertOf2To10("1001"));
    Console.WriteLine(s.ConvertOf2To16("10010"));
    Console.WriteLine(s.ConvertOf2To10("11111"));
    Console.WriteLine(s.ConvertOf2To16("11111"));
    Console.WriteLine(s.ConvertOf2To8("11111"));
}

得到下列執行結果:
這裡寫圖片描述

題目二

對於表示式(1-2) * (4+5),轉化為逆波蘭表示式之後為:1 2 - 4 5 + *
求一個方法可以計算逆波蘭表示式並返回結果

        可以發現,當我們計算逆波蘭表示式時,只需要每次把數字入棧,然後當遇到運算子的時候,就從棧中取出棧頂的兩個元素與運算子進行計算,然後把得到的結果再壓入棧中,直到最後把表示式的所有字元全部讀取完畢,棧中只會剩下唯一的一個數字,這個數字就是這個逆波蘭表示式的結果啦!
        下列為參考的程式碼:

public class Solution2
{
    /// <summary>
    /// 計算波蘭表示式的結果並返回
    /// </summary>
    /// <param name="expression"></param>
    /// <returns></returns>
    public double CalculateBoLanExpression(string expression)
    {
        string[] splitExpression = expression.Split(' ');
        int expressionLength = splitExpression.Length;
        MyStack<double> myStack = new MyStack<double>(expressionLength);

        for (int i = 0; i < expressionLength; i++)
        {
            string temp = splitExpression[i];
            //如果需要入棧的是一個運算子,那麼就先計算,再把結果入棧
            if (CheckTemp(temp))
            {
                if (myStack.GetLength() < 2)
                {
                    throw new Exception("表示式有誤!");
                }
                CalculateResult(ref myStack, temp);
            }
            //如果壓棧的元素為數字,那麼就把數字壓入棧中
            if (CheackIsNumber(temp))
            {
                myStack.Push(double.Parse(temp));
            }
        }

        //當表示式計算完畢,棧裡面只會剩下一個值,彈出棧頂元素即能得到最後結果
        return myStack.Pop();
    }

    /// <summary>
    /// 檢查元素是否為數字
    /// </summary>
    /// <param name="temp"></param>
    /// <returns></returns>
    private bool CheackIsNumber(string str)
    {
        try
        {
            double num = Convert.ToDouble(str);
            return true;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    /// 把棧頂上的兩個資料進行運算並重新壓入棧中
    /// </summary>
    /// <param name="myStack"></param>
    /// <param name="operatorStr">運算子</param>
    private void CalculateResult(ref MyStack<double> myStack, string operatorStr)
    {
        if (myStack.GetLength() < 2)
        {
            Console.WriteLine("表示式有誤!");
            return;
        }
        double num2 = myStack.Pop(), num1 = myStack.Pop();
        double result = 0;
        switch (operatorStr)
        {
            case "+":
                result = num1 + num2;
                break;
            case "-":
                result = num1 - num2;
                break;
            case "*":
                result = num1 * num2;
                break;
            case "/":
                result = num1 / num2;
                break;
        }
        myStack.Push(result);
    }

    /// <summary>
    /// 檢查入棧的字元是否為運算子
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    private bool CheckTemp(string operatorStr)
    {
        if (operatorStr.Equals("+") || operatorStr.Equals("-") || operatorStr.Equals("*") || operatorStr.Equals("/"))
            return true;
        return false;
    }
}

測試程式碼為:

private static void Test2()
{
    //(1-2)*(4+5)
    string bolanExpression1 = "1 2 - 4 5 + *";
    //5+1*(3-2)
    string bolanExpression2 = "5 1 3 2 - * +";
    //5-(6+7)*8+9/4
    string bolanExpression3 = "5 6 7 + 8 * - 9 4 / +";
    Solution2 s2 = new Solution2();
    Console.WriteLine(s2.CalculateBoLanExpression(bolanExpression1));
    Console.WriteLine(s2.CalculateBoLanExpression(bolanExpression2));
    Console.WriteLine(s2.CalculateBoLanExpression(bolanExpression3));
}

下面為執行截圖:
這裡寫圖片描述

題目三

        對於現實中常用的中綴表示式,現在需要一個演算法來把輸入的中綴表示式變成字尾表示式(即波蘭表示式)

public class Solution3
{
    /// <summary>
    /// 把中綴表示式轉化為字尾表示式
    /// 中綴轉字尾表示式的規則是:
    /// 從左到右變數中綴表示式的每個數字和符號,若是數字就輸出,即成為後面表示式的一部分,若是符號,則判斷其與棧頂符號的優先順序,是右括號或者有限級低於棧頂符號(先乘除後加減),則棧頂元素依次出棧並輸出,並將當前符號進棧,一直到最後輸出後後綴表示式為止
    /// </summary>
    /// <returns></returns>
    public string ConvertSuffixExpression(string expression)
    {
        StringBuilder suffixExpression = new StringBuilder();
        MyStack<char> myStack = new MyStack<char>();
        bool isPushOperator = false;                //用於判斷插入數字之前是否有符號入棧,如果有,需要在插入數字前插入一個空格
        char temp;

        for (int i = 0; i < expression.Length; i++)
        {
            temp = expression[i];
            if (IsNumber(temp))
            {
                //如果新增數字之前出現過符號,則在字串中用一個空格隔開方便區分
                if (isPushOperator)
                {
                    suffixExpression.Append(' ');
                    isPushOperator = false;
                }
                //把數字加入字串中
                suffixExpression.Append(temp);
            }
            else if (IsOperator(temp))
            {
                isPushOperator = true;
                //棧不為空,最上層為"(",則運算子直接入棧
                if (!myStack.IsEmpty() && myStack.Peek().Equals('(') && !temp.Equals(')'))
                {
                    myStack.Push(temp);
                }
                //棧不為空,遇")"則pop至"("為止
                else if (!myStack.IsEmpty() && temp.Equals(')'))
                {
                    char c = myStack.Pop();
                    while (!c.Equals('('))
                    {
                        suffixExpression.Append(' ');
                        suffixExpression.Append(c);
                        c = myStack.Pop();
                    }
                }
                //如果棧頂元素優先順序高於入棧元素,把優先順序高的出棧,再入棧
                else if (!myStack.IsEmpty() && ComparerOperator(myStack.Peek(), temp) == 1)
                {
                    while (!myStack.IsEmpty() && ComparerOperator(myStack.Peek(), temp) == 1 && myStack.Peek().Equals('('))
                    {
                        suffixExpression.Append(' ');
                        suffixExpression.Append(myStack.Pop());
                    }
                    myStack.Push(temp);
                }
                //如果棧頂元素優先順序等於入棧元素,先出棧棧頂元素,再入棧
                else if (!myStack.IsEmpty() && ComparerOperator(myStack.Peek(), temp) == 0)
                {
                    suffixExpression.Append(' ');
                    suffixExpression.Append(myStack.Pop());
                    myStack.Push(temp);
                }
                //如果棧頂元素優先順序小於入棧元素,直接入棧
                else if (!myStack.IsEmpty() && ComparerOperator(myStack.Peek(), temp) == -1)
                {
                    myStack.Push(temp);
                }
                else
                    myStack.Push(temp);
            }
            else
                throw new Exception("表示式異常!!!");
        }

        while (!myStack.IsEmpty())
        {
            suffixExpression.Append(' ');
            suffixExpression.Append(myStack.Pop());
        }

        return suffixExpression.ToString();
    }

    /// <summary>
    /// 判斷符號的優先順序
    /// </summary>
    /// <param name="operator1"></param>
    /// <param name="operator2"></param>
    /// <returns>返回兩運算子優先順序的比較結果,相等為0,1大於2返回1,1小於2返回-1</returns>
    private int ComparerOperator(char operator1, char operator2)
    {
        int priority1 = GetOperPrio(operator1), priorityi2 = GetOperPrio(operator2);

        if (priority1 > operator2)
            return 1;
        else if (priority1 < priorityi2)
            return -1;

        return 0;
    }

    /// <summary>
    /// 得到運算子的優先順序
    /// ‘(’,‘)’優先順序10
    /// ‘*’,‘/’  優先順序2
    /// ‘+’,‘-’  優先順序1
    /// </summary>
    /// <param name="operator1"></param>
    /// <returns></returns>
    private int GetOperPrio(char oper)
    {
        int priority = 0;
        switch (oper)
        {
            case '+':
            case '-':
                priority = 1;
                break;
            case '*':
            case '/':
                priority = 2;
                break;
            case '(':
            case ')':
                priority = 3;
                break;
        }
        return priority;
    }

    /// <summary>
    /// 判斷是否為運算子
    /// </summary>
    /// <param name="temp"></param>
    /// <returns></returns>
    private bool IsOperator(char temp)
    {
        bool isOperator = false;
        switch (temp)
        {
            case '+':
            case '-':
            case '*':
            case '/':
            case '(':
            case ')':
                isOperator = true;
                break;
        }

        return isOperator;
        throw new NotImplementedException();
    }

    /// <summary>
    /// 判斷字元是否為數字
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    private bool IsNumber(char value)
    {
        if ((value >= '0' && value <= '9') || value.Equals('.'))
            return true;
        return false;
    }
}

測試程式碼:

private static void Test3()
{
    string expression1 = "1-(2*3+5)-9/4";
    string expression2 = "6.32+4.5*1.2-(2.8+1.02)/2.1";
    Solution3 s3 = new Solution3();
    Solution2 s2 = new Solution2();
    string result = s3.ConvertSuffixExpression(expression1);
    Console.WriteLine(expression1 + "\n" + result);
    Console.WriteLine(s2.CalculateBoLanExpression(result));
    result = s3.ConvertSuffixExpression(expression2);
    Console.WriteLine(expression2 + "\n" + result);
    Console.WriteLine(s2.CalculateBoLanExpression(result));
}

下列為執行的測試結果截圖:
這裡寫圖片描述