计算器的混合运算

        我们日常生活中 , 处处离不开计算 ,简单的买菜 , 去超市购物等 , 都需要计算消费的金额 , 我们一次性买了很多种类的菜 , 同一种饮料买了好几瓶 . 如果用手算的话 , 我们当然不可能笨到一个算式一个算式的去计算结果 . 我们一般会列一个混合运算的式子 , 然后再计算.

这时候 ,如果让一个一年级的小学生来算的话 ,他会感到一头雾水 , 到底先算哪一个 ,因为他现在只学了加减法 ,只会逐步计算.

我们学过算术优先级的同学 , 当然知道 , 我们要先算乘除, 再算加减 , 然后括号里的要优先运算 .

但是 , 计算机就是那个只会按顺序计算的小朋友 , 他只会计算两个数的运算,我们怎样给他赋予智慧呢? 就是怎样帮助他算出正确答案呢?

答案是: 既然他只会计算两个数 ,我们就只给他两个数 ,然后这两个数要进行什么运算 , 我们再按照符号的优先级的排序 , 告诉小朋友 ,这样他总会计算出最终结果的.

 接下来 , 就是我们如何告诉小朋友 , 该算哪两个数字 , 这两个数字的运算符号是什么.

我们朝着这个目标设计算法:

现在第一步的问题是:

我们已经有了一个混合运算的表达式 , 如何把他转化为固定的运算 ,我们只用依次去取元素,然后运算就可以了

要想这样做 , 就需要先对混合运算的表达式做手脚了 ,我们先认识一下后缀表达式:

例子:

        Exp = a × b + ( c - d  / e)  × f


后缀式

         a  b   ×   c  d  e  /  -  f   ×  +


我们的后缀表达式 , 不改变操作数之间的相对次序 ,然后一次只运算两个数 ,

运算符的相对次序不同 ,并且后缀表达式已经考虑了运算法的优先级 ,去掉了括号, 只有操作数和运算符号


我们现在就用人脑来模拟一遍后缀式的运算过程 

从开始开始遍历

 a  b   ×   c  d  e  /  -  f   ×  +

取到  a  记下来

取到   b 记下来

看到  乘号  ,因为已经有两个数,就直接运算  a 乘 b 的结果得到 记作 x1 (抹掉运算过的算式)


 X1  c  d  e  /  -  f   ×  +

接着看到 c  记下来

取到  d  记下来

取到  e 记下来

看到  除号 ,就近运算 d / e   得出结果    d/e (抹掉 d e) , 记作 X2


X1 c X2f   ×  +

 看到 减号  , 就近取  c   和  X2   , 计算   c - X2   ,记下结果  X3


X1  X3  f   ×  +

看到   f  记下来

看到  × ,就近取 X3 乘以  f 得出结果记作 X4,


X1  X4  +

接着看到 +  ,就近取两个数 ,  X1 + X4  ,记作 X5


X5


接着往下没有元素了,输出最后结果

我们的目标就是解决问题 , 我们假如现在已经设计出了 把一个混合运算式子转化为 后缀式 

现在我们如何实现算法来实现上述 ,我们的人脑运算的过程呢?

下面给出构思:

我们用后缀式来运算的时候 , 其运算顺序完全符合原式优先级运算顺序 .

遍历后缀式的时候 , 我们从第一个字符开始遍历 ,遇到数字 ,就直接存放起来 , 遇到符号的话, 如果我们存放的够两个数 ,就直接就近取出两个数进行运算 ,结果再存起来 

每次遇到符号 ,运算的总是就近的两个数,然后存储到起来


所以现在我们从上述描述中 , 提取出来 ,有规范性的信息

        ●遍历后缀式

        ●遇到数字记下来

        ●遇到符号,计算就近的两个数,然后接着存起来

        ●直到后缀式遍历完

        ●输出结果


承载数据的数据结构:

我们遇到符号,就直接就近取出两个数字进行运算 ,然后再插入 

后进的元素 , 就是就近的元素 ,所以这很符合我们的栈的数据结构

所以说 , 我们遇到元素,直接插入栈 , 遇到符号 ,就直接从栈中取出两个元素运算完,然后再压入栈中,直到我们把后缀式遍历完 , 弹出栈中元素 ,就算出结果了.

接下来,我们就对构造这个运算,构建大致的框架 ,

我们对后缀表达式 postexp 求值


while(从 postexp 读取字符 ch ,ch != '\0')
{
	若 ch 为数字 ,将后续的所有数字构成一个整数 存放到数值栈st 中 
	若ch 为 "+", 则从数值栈 st 中退栈两个运算数 ,相加后进栈 st 中;
	若ch 为 "-" , 则从数值栈st 中退栈两个运算数, 相减后进栈 st 中;
	若 ch 为"*" , 则从数值栈 st 中退栈两个运算数 ,相乘后进栈 st 中;
	//若除数为零,则提示相应的错误信息
	若 ch 为 "/", 则从数值栈 st 中退栈两个运算数 ,相除后进栈 st 中;
	
}
若字符串 postexp 扫描完毕 ,则数值栈op中的栈顶元素就是表达式的值

以上后缀表达式求值的过程


下面进行算法实现,后缀表达式的求值:

(混合表达式给出 , 后缀表达式如何求? 我们下一讲细说, 现在的目标是先解决求值问题,我们要知道 ,有这一种混合表达式 ,可以让我们对混合运算实现求值 ,至于如何得出后缀式 ,我们下一讲根据优先级,依次程序化构思)

我们遍历后缀式 ,存字符 ,就近取两个数进行运算 ,需要用到栈的特性 ,后进先出 ,所以我们先构建栈,

来存储运算字符

传入 后缀表达式的字符串数组

float compvalue(char exp[])
{

//构建我们存储数值的栈 ,我们承载数据是用数组,然后我们定义的最大长度是多少呢?

这取决于我们的后缀式 ,我们为了一劳永逸,直接定义成 固定的长度 保证够用,MaxSize是后缀式的长度

struct 
{
    //定义数组长度 ,MaxSize为后缀式的元素个数
    float data[MaxSize];
    //定义栈顶
    int top;
}st;

接下来,定义栈顶 ,初始栈顶在 -1

st.top = -1;

定义遍历 后缀字符串的指针

char ch = exp[t];

定义遍历后缀字符串的计数器

int t = 0;

因为后缀式里面存放的都是字符 ,例如 23 +45 ,我们依次取  2  和   3 ,直到遇到 + ,才发现这是一个合并的整数 ,所以我们定义将数字字符转换成数值的变量

float d;

// 下面开始处理 exp 字符串

//从字符串开始开始遍历,直到遇到字符串结尾标志'\0'
while (ch !='\0')
{
	//判断遇到的字符类型
	switch(ch)
	{
		//当遇到的是加号时,就从栈顶取出两个数字进行运算,结果入栈
		case'+':
			//两个数字运算变成一个数,当然栈顶减一,变成 top-1
			//我们在这里巧妙的利用栈指针,来运算,栈顶的数字是后入栈的,所以在运算符右边
			st.data[st.top-1] = st.data[st.top-1]+st.data[st.top];
			st.top--;
	        //方便一会给 ch赋值
			t++;
			//跳出switch,继续遍历下一个字符
			break;
		//下面几个运算符同理
		case'-':
			st.data[st.top-1] = st.data[st.top-1] - st.data[st.top]
			st.top--;
           //方便一会给 ch赋值
            t++;
			break;
		case'*':
			st.data[st.top-1] = st.data[st.top-1]*st.data[st.top];
			st.top--;
			t++;
            break;
		case'/':
			st.data[st.top-1] = st.data[st.top-1]/st.data[st.top];
			st.top--;
           //方便一会给 ch赋值
             t++;
            
			break;
		//以上都不是,则判断是否遇到数字字符
		default:
		    d = 0; //我们将数字字符的元素转换为数值存放到d中
		    //那我们怎样转换成数字呢 ,这些都是Ascll码.
		    例如 23 ,我们先读到2 ,然后ch 接着遍历,又读到3,
		    所以我们就知道,我们要把两个联合起来
		    //首先,我们可以给第一个数字乘以10 ,然后再加上后来的数字就可以了
		    //但是现在我们的主要问题是,字符如何转换为int类型,我们可以利用
		    //Ascll码是有序的,用字符减去'0'的Ascll码,之间的差值就是整形,正好等于字符的int类型
		    while(ch>='0' && ch<='9')
		    {
			    d = 10*d+ch-'0';
			    //ch = exp[t] 是数字,通过了检查,接着判断下一个字符
                t++;
			    ch = exp[t];
			//直到遇到运算符,就跳出了循环,此时数字字符也串联起来了
                //注意:此时跳出的时候 ch = exp[t]指向的是'#'
			
	    	}
            //因为跳出的时候,后缀式每个数值 后面是 '#' ,所以我们再次指向下一个字符
                t++;
		    //我们既然遇到了数值数据, 就把联合整理后的 d 入栈
		    //栈顶指针先上移
		        st.top++;
		    //往栈顶指针赋值
		        st.data[st.top] = d;		
	}
	//此时跳出 switch是要继续将 ch=exp[t] 里的字符进行对比
	//进行遍历下一个字符,上面跳出后,已经对数组坐标进行了,处理,赋值对比即可
	
    ch = exp[t];	
	
}

//返回结果

        

    return st.data[st.top];
}

设计求解程序

#include <stdio.h>
#include <stdlib.h>
#define MaxOp 100
#define MaxSize 100
int main()
{
	char exp[]="(56-12)/(4+2)";
	//定义存储后缀式的数组
	char postexp[MaxSize];
	//把算术式转换成后缀式
	trans(exp,postexp);
	printf("中缀式:%s\n",exp);
	printf("后缀表达式:%s\n",postexp);
	printf("表达式的值:%g\n",compvalue(postexp));
	return 0;
}

完整代码如下:

#include <stdio.h>
#include <stdlib.h>
#define MaxOp 100
#define MaxSize 100
struct  //设定运算符优先级
{
    char ch;   //运算符
    int pri;   //优先级
}
lpri[]= {{'=',0},{'(',1},{'*',5},{'/',5},{'+',3},{'-',3},{')',6}},
rpri[]= {{'=',0},{'(',6},{'*',4},{'/',4},{'+',2},{'-',2},{')',1}};
int leftpri(char op)    //求左运算符op的优先级
{


    int i;
    for (i=0; i<MaxOp; i++)
        if (lpri[i].ch==op)
        {
                return lpri[i].pri;
        }
    return 0;
}
int rightpri(char op)  //求右运算符op的优先级
{
    int i;
    for (i=0; i<MaxOp; i++)
        if (rpri[i].ch==op)
            return rpri[i].pri;
    return 0;
}
bool InOp(char ch)       //判断ch是否为运算符
{
    if (ch=='(' || ch==')' || ch=='+' || ch=='-'
            || ch=='*' || ch=='/')
        return true;
    else
        return false;
}
int Precede(char op1,char op2)  //op1和op2运算符优先级的比较结果
{
    if (leftpri(op1)==rightpri(op2))
        return 0;
    else if (leftpri(op1)<rightpri(op2))
        return -1;
    else
        return 1;
}
void trans(char *exp,char postexp[])
//将算术表达式exp转换成后缀表达式postexp
{
    struct
    {
        char data[MaxSize]; //存放运算符
        int top;            //栈指针
    } op;               //定义运算符栈
    int i=0;                //i作为postexp的下标
    op.top=-1;
    op.top++;                  //将'='进栈
    op.data[op.top]='=';
    while (*exp!='\0')      //exp表达式未扫描完时循环
    {
        if (!InOp(*exp))        //为数字字符的情况
        {
            while (*exp>='0' && *exp<='9') //判定为数字
            {
                postexp[i++]=*exp;
                exp++;
            }
            postexp[i++]='#';   //用#标识一个数值串结束
        }
        else    //为运算符的情况
            switch(Precede(op.data[op.top],*exp))
            {
            case -1:           //栈顶运算符的优先级低:进栈
                op.top++;
                op.data[op.top]=*exp;
                exp++;     //继续扫描其他字符
                break;
            case 0:        //只有括号满足这种情况
                op.top--;      //将(退栈
                exp++;     //继续扫描其他字符
                break;
            case 1:             //退栈并输出到postexp中
                postexp[i++]=op.data[op.top];
                op.top--;
                break;
            }
    } //while (*exp!='\0')
    while (op.data[op.top]!='=')
    //此时exp扫描完毕,退栈到'='为止
    {
        postexp[i++]=op.data[op.top];
        op.top--;
    }
    postexp[i]='\0';    //给postexp表达式添加结束标识
}

float compvalue(char exp[]) //计算后缀表达式的值
{
    struct
    {
        float data[MaxSize];    //存放数值
        int top;            //栈指针
    } st;               //定义数值栈
    float d;
    char ch;
    int t=0; //t作为exp的下标
    st.top=-1;
    ch=exp[t];
	
    while (ch!='\0')    //exp字符串未扫描完时循环
    {
        switch (ch)
        {
        case'+':
            st.data[st.top-1]=st.data[st.top-1]+st.data[st.top];
            st.top--;
			t++;
            break;
        case '-':
            st.data[st.top-1]=st.data[st.top-1]-st.data[st.top];
            st.top--;
			t++;
            break;
        case '*':
            st.data[st.top-1]=st.data[st.top-1]*st.data[st.top];
            st.top--;
			t++;
            break;
        case '/':
            if (st.data[st.top]!=0)
			{
                st.data[st.top-1]=st.data[st.top-1]/st.data[st.top];
			}	
            else
            {
                printf("\n\t除零错误!\n");
                exit(0);  //异常退出
            }
            st.top--;
			t++;
            break;
        default:
            d=0; //将数字字符转换成数值存放到d中
            while (ch>='0' && ch<='9')   //为数字字符
            {
                d=10*d+ch-'0';
                t++;
				ch=exp[t];
            }
			t++;
            st.top++;
            st.data[st.top]=d;
        }
        ch=exp[t];
    }
    return st.data[st.top];
}

int main()
{
    char exp[]="(56-20)/(4+2)-33+9*32"; //可将exp改为键盘输入
    char postexp[MaxSize];
    trans(exp,postexp);
    printf("中缀表达式:%s\n",exp);
    printf("后缀表达式:%s\n",postexp);
    printf("表达式的值:%g\n",compvalue(postexp));
    return 0;
}

  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
要实现一个能够进行混合运算的Android计算器,可以按照以下步骤进行: 1. 创建一个布局文件,包含数字按钮、运算符按钮、清除按钮、等于号按钮等组件。 2. 在Java代码中,为每个按钮添加点击事件监听器,实现相应的功能。 3. 在计算器逻辑中,可以使用栈来实现混合运算。当输入一个数字时,将其压入栈中;当输入一个运算符时,从栈中弹出两个数字,进行相应的运算,并将结果压入栈中。 4. 当按下等于号按钮时,从栈中弹出最终结果,并将其显示在计算器屏幕上。 下面是一个简单的示例代码,用于实现一个简单的支持混合运算的Android计算器: MainActivity.java ```java import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import java.util.Stack; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView mResultTextView; private StringBuilder mInputString = new StringBuilder(); private Stack<Double> mNumberStack = new Stack<>(); private Stack<Character> mOperatorStack = new Stack<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mResultTextView = findViewById(R.id.result_text_view); Button clearButton = findViewById(R.id.clear_button); clearButton.setOnClickListener(this); Button equalsButton = findViewById(R.id.equals_button); equalsButton.setOnClickListener(this); Button addButton = findViewById(R.id.add_button); addButton.setOnClickListener(this); Button subtractButton = findViewById(R.id.subtract_button); subtractButton.setOnClickListener(this); Button multiplyButton = findViewById(R.id.multiply_button); multiplyButton.setOnClickListener(this); Button divideButton = findViewById(R.id.divide_button); divideButton.setOnClickListener(this); Button decimalButton = findViewById(R.id.decimal_button); decimalButton.setOnClickListener(this); Button zeroButton = findViewById(R.id.zero_button); zeroButton.setOnClickListener(this); Button oneButton = findViewById(R.id.one_button); oneButton.setOnClickListener(this); Button twoButton = findViewById(R.id.two_button); twoButton.setOnClickListener(this); Button threeButton = findViewById(R.id.three_button); threeButton.setOnClickListener(this); Button fourButton = findViewById(R.id.four_button); fourButton.setOnClickListener(this); Button fiveButton = findViewById(R.id.five_button); fiveButton.setOnClickListener(this); Button sixButton = findViewById(R.id.six_button); sixButton.setOnClickListener(this); Button sevenButton = findViewById(R.id.seven_button); sevenButton.setOnClickListener(this); Button eightButton = findViewById(R.id.eight_button); eightButton.setOnClickListener(this); Button nineButton = findViewById(R.id.nine_button); nineButton.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.clear_button: handleClearButtonClick(); break; case R.id.equals_button: handleEqualsButtonClick(); break; case R.id.add_button: case R.id.subtract_button: case R.id.multiply_button: case R.id.divide_button: handleOperatorButtonClick((Button) v); break; case R.id.decimal_button: case R.id.zero_button: case R.id.one_button: case R.id.two_button: case R.id.three_button: case R.id.four_button: case R.id.five_button: case R.id.six_button: case R.id.seven_button: case R.id.eight_button: case R.id.nine_button: handleNumberButtonClick((Button) v); break; } } private void handleClearButtonClick() { mInputString.setLength(0); mNumberStack.clear(); mOperatorStack.clear(); mResultTextView.setText(""); } private void handleEqualsButtonClick() { if (mInputString.length() == 0) { return; } double result = evaluateExpression(); mResultTextView.setText(String.valueOf(result)); } private void handleOperatorButtonClick(Button button) { if (mInputString.length() == 0) { return; } char operator = button.getText().charAt(0); while (!mOperatorStack.isEmpty() && hasPrecedence(mOperatorStack.peek(), operator)) { double rightOperand = mNumberStack.pop(); double leftOperand = mNumberStack.pop(); char op = mOperatorStack.pop(); double result = applyOperator(op, leftOperand, rightOperand); mNumberStack.push(result); } mOperatorStack.push(operator); mInputString.append(operator); mResultTextView.setText(mInputString.toString()); } private void handleNumberButtonClick(Button button) { char digit = button.getText().charAt(0); mInputString.append(digit); mResultTextView.setText(mInputString.toString()); } private double evaluateExpression() { while (!mOperatorStack.isEmpty()) { double rightOperand = mNumberStack.pop(); double leftOperand = mNumberStack.pop(); char op = mOperatorStack.pop(); double result = applyOperator(op, leftOperand, rightOperand); mNumberStack.push(result); } return mNumberStack.pop(); } private boolean hasPrecedence(char op1, char op2) { if (op2 == '(' || op2 == ')') { return false; } if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-')) { return false; } return true; } private double applyOperator(char op, double leftOperand, double rightOperand) { switch (op) { case '+': return leftOperand + rightOperand; case '-': return leftOperand - rightOperand; case '*': return leftOperand * rightOperand; case '/': return leftOperand / rightOperand; default: throw new IllegalArgumentException("Invalid operator: " + op); } } } ``` activity_main.xml ```xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/result_text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:textSize="24sp" android:textAlignment="viewEnd" android:paddingTop="16dp" android:paddingBottom="16dp" android:paddingEnd="16dp" android:paddingStart="16dp"/> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/clear_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="C" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/divide_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="/" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/multiply_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="*" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/subtract_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="-" android:layout_weight="1" android:textSize="18sp"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/seven_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="7" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/eight_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="8" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/nine_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="9" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/add_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="+" android:layout_weight="1" android:textSize="18sp"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/four_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="4" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/five_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="5" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/six_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="6" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/decimal_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="." android:layout_weight="1" android:textSize="18sp"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/one_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="1" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/two_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="2" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/three_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="3" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/equals_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="=" android:layout_weight="1" android:textSize="18sp"/> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:id="@+id/zero_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="0" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/placeholder_button" android:layout_width="0dp" android:layout_height="wrap_content" android:text="" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/placeholder_button_2" android:layout_width="0dp" android:layout_height="wrap_content" android:text="" android:layout_weight="1" android:textSize="18sp"/> <Button android:id="@+id/placeholder_button_3" android:layout_width="0dp" android:layout_height="wrap_content" android:text="" android:layout_weight="1" android:textSize="18sp"/> </LinearLayout> </LinearLayout> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值