資料結構之棧的運用
這兩天覆習資料結構的時候在學習棧,學習中碰到幾個練習題,在這裡附上最後的結果,全部通過棧來實現。
注:以下程式碼全部用的都是前面學習時自己新建的棧,如果有地方和系統自帶的棧不一樣的,可以去檢視前面釋出的《資料結構之棧》地址如下:
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));
}
下列為執行的測試結果截圖: