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