今天帮朋友写个大作业,记得当时leetcode的计算器算法题是个hard,朋友的作业要求还有开方乘方,本身C#就不太熟,还要写个算法,真是雪上加霜,花了得有四个小时才写完,可能还有bug没找到。
项目文件:
首先先编辑样式,这种简单的桌面应用就拖拖控件改改名字就好。
之后给每个按键设置函数,不管是数字还是符号就往数字栏的TextBox屁股后面加就行了。清零就是将TextBox.Text设置成空字符就行。
private void NumButton1_Click(object sender, EventArgs e)
{
NumText.Text += "1";
}
最主要就是写计算这个button对应的函数
主要思路还是使用栈来操作,一个栈存数字,一个栈存符号。运算过程简单来说就是比较运算符优先级,如果当前运算符优先级<=栈顶运算符,就把栈顶运算符弹出,与数字栈顶元素(加减乘除是两个,开方平方是一个)进行运算,把结果再压入数字栈。
先不考虑括号,需要注意的地方有以下几点
- 表达式为空的情况直接输出0
- 数字栈用double防止除法开方造成小数
- 平方为栈顶运算符或开方为当前运算符时,不应把当前数字压入数字栈
- 循环结束后运算符栈可能为空,就直接输出数字栈顶元素
- 注意输入负数的情况
考虑括号其实就是注意左括号入栈时要小心数字是否压入,尤其注意左括号在表达式第一位时的讨论,因此整个是否压入数字的条件判断比较复杂。
当循环到右括号时单独处理,计算直到左括号为运算符为堆顶元素。右括号后一个运算符处理时需要注意不能把数字压入。
实际代码实现时先写一个运算符优先级比较函数,一个弹出运算符栈顶元素进行计算的函数,注意传入栈时,C#没有指针,使用ref进行引用。
using System;
using System.Windows.Forms;
using System.Collections;
using System.Collections.Generic;
namespace Calculator
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void NumButton1_Click(object sender, EventArgs e)
{
NumText.Text += "1";
}
private void NumButton2_Click(object sender, EventArgs e)
{
NumText.Text += "2";
}
private void NumButton3_Click(object sender, EventArgs e)
{
NumText.Text += "3";
}
private void NumButton4_Click(object sender, EventArgs e)
{
NumText.Text += "4";
}
private void NumButton5_Click(object sender, EventArgs e)
{
NumText.Text += "5";
}
private void NumButton6_Click(object sender, EventArgs e)
{
NumText.Text += "6";
}
private void NumButton7_Click(object sender, EventArgs e)
{
NumText.Text += "7";
}
private void NumButton8_Click(object sender, EventArgs e)
{
NumText.Text += "8";
}
private void NumButton9_Click(object sender, EventArgs e)
{
NumText.Text += "9";
}
private void NumButton0_Click(object sender, EventArgs e)
{
NumText.Text += "0";
}
private void LeftBracketButton_Click(object sender, EventArgs e)
{
NumText.Text += "(";
}
private void RightBracketButton_Click(object sender, EventArgs e)
{
NumText.Text += ")";
}
private void PlusButton_Click(object sender, EventArgs e)
{
NumText.Text += "+";
}
private void MinusButton_Click(object sender, EventArgs e)
{
NumText.Text += "-";
}
private void SquareRootButton_Click(object sender, EventArgs e)
{
NumText.Text += "√";
}
private void MultiplyButton_Click(object sender, EventArgs e)
{
NumText.Text += "×";
}
private void DevideButton_Click(object sender, EventArgs e)
{
NumText.Text += "÷";
}
private void SquareButton_Click(object sender, EventArgs e)
{
NumText.Text += "²";
}
private void CleatButton_Click(object sender, EventArgs e)
{
NumText.Text = "";
}
private void ResultButton_Click(object sender, EventArgs e)
{
//获取计算框内现有字符
string expression = NumText.Text;
//如果表达式为空
if (expression.Length == 0)
{
NumText.Text = "0";
return;
}
//使用栈结构处理数字及符号
Stack<double> numStack = new Stack<double>();
Stack<char> symbolStack = new Stack<char>();
//临时数字
double tempNumber = 0;
//正负号
int plusOrMinus = 1;
//遍历当前字符
for(int i = 0; i < expression.Length; i++)
{
//获得当前字符
char tempChar = expression[i];
//如果是数字
if(char.IsDigit(tempChar))
{
if(plusOrMinus==1)
tempNumber = 10 * (int)tempNumber + (int)tempChar-(int)'0';
else
tempNumber = 10 * (int)tempNumber - (int)tempChar + (int)'0';
}
//如果是符号
else
{
//如果输入的是负号
//两种情况是负号 1开头 2跟在加减乘除后面
if (tempChar == '-')
{
if (i == 0 || expression[i - 1] == '+' || expression[i - 1] == '-' || expression[i - 1] == '×' || expression[i - 1] == '÷')
{
plusOrMinus = -1;
continue;
}
else
plusOrMinus = 1;
}
else
plusOrMinus = 1;
//如果当前符号为(就一定不压入数字
if (tempChar != '(')
{
//如果符号栈为空或栈顶符号不为平方或开方就将当前数字压入栈
if(symbolStack.Count == 0 || (symbolStack.Peek() != '²' && tempChar != '√'))
{
numStack.Push(tempNumber);
tempNumber = 0;
}
}
//当前运算符为右括号时单独处理
if (tempChar == ')')
{
double tempResult = 0;
//如果运算符栈顶不为(则一直计算
while(symbolStack.Peek() != '(')
{
tempResult = partCal(symbolStack.Pop(), ref numStack);
numStack.Push(tempResult);
}
//判断右括号是否为最后一个字符,如果不是就将栈顶弹出,交给tempNumber以便循环到下一个运算符时压入数字栈
if(i< expression.Length - 1)
{
tempNumber = numStack.Pop();
}
//将左括号弹出
symbolStack.Pop();
continue;
}
//进入循环,不断与运算符栈顶元素比较优先级
while (true)
{
//若运算符栈为空或当前运算符优先级高于栈顶运算符则将当前运算符入栈
//开方处理由于运算数在运算符后,所以永远直接入栈
if(symbolStack.Count==0 || priorityCompare(tempChar, symbolStack.Peek()) > 0 || tempChar== '√' || symbolStack.Peek() == '(')
{
symbolStack.Push(tempChar);
//压入栈后即可跳出循环
break;
}
//当前运算符优先级小于等于栈顶运算符时,将前项进行计算
else
{
//将计算结果压入栈顶
numStack.Push(partCal(symbolStack.Pop(), ref numStack));
}
}
}
}
//如果最后一个字符是数字,将最后一个数字压入栈
if (char.IsDigit(expression[expression.Length - 1]))
numStack.Push(tempNumber);
//读取两个栈中数据进行结果计算
double finalResult = 0;
//目前运算符栈顶可能是任意运算符,数字栈内有一至两个数字
if (symbolStack.Count == 0)
finalResult = numStack.Pop();
while (symbolStack.Count != 0)
{
finalResult = partCal(symbolStack.Pop(), ref numStack);
numStack.Push(finalResult);
}
ResultBox.Text = finalResult.ToString();
}
//运算符优先级比较函数
public int priorityCompare(char sym1, char sym2)
{
int s1 = 0;
int s2 = 0;
if (sym1 == '(')
s1 = 4;
if (sym1 == '√' || sym1 == '²')
s1 = 3;
if (sym1 == '×' || sym1 == '÷')
s1 = 2;
if (sym1 == '+' || sym1 == '-')
s1 = 1;
if (sym2 == '(')
s2 = 4;
if (sym2 == '√' || sym2 == '²')
s2 = 3;
if (sym2 == '×' || sym2 == '÷')
s2 = 2;
if (sym2 == '+' || sym2 == '-')
s2 = 1;
return (s1 - s2);
}
//计算函数
public double partCal(char symbol,ref Stack<double> num_stack)
{
double result = 0;
if (symbol == '√' || symbol == '²')
{
//弹出栈顶元素
double pop_num = num_stack.Pop();
if (symbol == '√')
pop_num = Math.Sqrt(pop_num);
if (symbol == '²')
pop_num *= pop_num;
result = pop_num;
}
else
{
//取栈顶元素为算式后数,再取一个为前数
double subNum = num_stack.Pop();
double preNum = num_stack.Pop();
if (symbol == '+')
result = preNum + subNum;
if (symbol == '-')
result = preNum - subNum;
if (symbol == '×')
result = preNum * subNum;
if (symbol == '÷')
result = preNum / subNum;
}
return result;
}
}
}
Comments NOTHING