数据结构之栈的运用

        这两天复习数据结构的时候在学习栈,学习中碰到几个练习题,在这里附上最后的结果,全部通过栈来实现。
        注:以下代码全部用的都是前面学习时自己新建的栈,如果有地方和系统自带的栈不一样的,可以去查看前面发布的《数据结构之栈》地址如下:
https://blog.youkuaiyun.com/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));
}

下面为运行截图:
这里写图片描述

题目三

        对于现实中常用的中缀表达式,现在需要一个算法来把输入的中缀表达式变成后缀表达式(即波兰表达式)

        在这里参考了七把刀博主的博客,感觉思路十分清晰,思路在这里就不多介绍了,可以通过下面链接查看思路,这里附上代码
七把刀大大的博客:https://blog.youkuaiyun.com/sgbfblog/article/details/8001651

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));
}

下列为运行的测试结果截图:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值