[C、C++]数据结构 :栈の应用之表达式

   分别讨论中缀表达式、后缀表达式和前缀表达式的概念和特点,以及如何利用栈的特性来实现它们之间的相互转换和求值。我们还将给出相应的算法和代码实现,并分析它们的时间复杂度和空间复杂度。

  中缀表达式是我们日常使用的最常见的表达式形式,它是按照运算符的优先级和括号来确定运算顺序的。但是,对于计算机来说,中缀表达式并不方便处理,因为它需要不断地扫描和判断运算符的优先级,以及处理括号的匹配问题。因此,我们可以将中缀表达式转换为后缀表达式或前缀表达式,这两种形式都可以避免使用括号,并且运算符的出现顺序就是运算顺序,从而简化了计算过程。

  后缀表达式又称为逆波兰表达式,它是将运算符放在操作数的后面,例如a+b*c可以写成abc*+。后缀表达式的计算方法是从左到右扫描,每遇到一个运算符,就从栈顶弹出两个操作数进行运算,并将结果压回栈顶。最终栈顶元素就是整个表达式的值。为了将中缀表达式转换为后缀表达式,我们可以使用一个栈来存储暂时不确定生效顺序的运算符,遵循左优先原则,即如果左边的运算符能先运算,就先运算左边的。具体来说,我们从左到右扫描中缀表达式,如果遇到操作数,直接加入后缀表达式;如果遇到左括号,入栈;如果遇到右括号,则依次弹出栈内元素并加入后缀表达式,直到遇到左括号为止;如果遇到其他运算符,则比较其与栈顶元素的优先级,如果低于或等于栈顶元素,则弹出栈顶元素并加入后缀表达式,重复此过程直到栈为空或者遇到更高优先级的运算符为止,然后将当前运算符入栈。最后扫描完中缀表达式后,将栈内剩余元素依次弹出并加入后缀表达式。

  前缀表达式又称为波兰表达式,它是将运算符放在操作数的前面,例如a+b*c可以写成+*abc。前缀表达式的计算方法是从右到左扫描,每遇到一个运算符,就从栈顶弹出两个操作数进行运算,并将结果压回栈顶。最终栈顶元素就是整个表达式的值。为了将中缀表达式转换为前缀表达式,我们可以使用一个栈来存储暂时不确定生效顺序的运算符,遵循右优先原则,即如果右边的运算符能先运算,就先运算右边的。具体来说,我们从右到左扫描中缀表达式,如果遇到操作数,直接加入前缀表达式的前面;如果遇到右括号,入栈;如果遇到左括号,则依次弹出栈内元素并加入前缀表达式的前面,直到遇到右括号为止;如果遇到其他运算符,则比较其与栈顶元素的优先级,如果低于或等于栈顶元素,则弹出栈顶元素并加入前缀表达式的前面,重复此过程直到栈为空或者遇到更高优先级的运算符为止,然后将当前运算符入栈。最后扫描完中缀表达式后,将栈内剩余元素依次弹出并加入前缀表达式的前面。

  通过上述方法,我们可以实现中缀表达式、后缀表达式和前缀表达式之间的相互转换和求值。这些方法的时间复杂度都是O(n),空间复杂度都是O(n),其中n是表达式的长度。在实际编程中,我们需要注意一些细节,例如如何处理多位数、小数、负数等。下面给出了使用C语言实现的部分代码示例。

一、手算

1.中缀表达式转后缀(手算)

左优先原则:若左边的运算符能先运算,就先运算左边的

用左优先原则得到的后缀表达式,运算符出现的顺序正好是原中缀表达式中运算符的运算顺序。

(好耶)

手算后缀表达式:从左往右扫,每扫到一个运算符,就把离得最近(栈顶)的两个操作数拿出来运算,并把结果压入栈顶。

注意:两个操作数の顺序是左边的先,右边的后,先出栈的是右操作数。

2.后缀表达式转成中缀(手算)

从左往右扫,遇到操作符,取出最近两个操作数运算,和基于栈计算后缀表达式一样。

3.中缀表达式转前缀表达式(手算)

中缀表达式转前缀

右优先原则:从右往左扫,只要靠右的运算符能先运算,就先算右边的。

运算符的运算顺序与前缀表达式中从右到左运算符出现的顺序相同。

注意:是从左往右扫描后缀表达式,是从右往左扫描前缀表达式;后缀表达式机算时先弹出的操作数是右操作数;前缀表达式机算时先弹出的栈顶元素是左操作数

二、机算实现中缀表达式の求值

中缀表达式计算=中缀转后缀+后缀表达式计算。对于计算机来说,后缀表达式比中缀好算。

注意一些区别:

(1)在中缀转后缀中,栈存放暂时不确定生效顺序の运算符;在后缀表达式の计算中,栈用来存放暂时不确定运算顺序の操作数

(2)中缀转后缀时,运用左优先原则,左优先原则下,先弹出的是右操作数。

1.中缀表达式转后缀(用栈实现)

初始化两个栈:操作数栈&运算符栈。

定义一个判断是否是运算符的函数,判断加减乘除。

定义一个判断运算符优先级的函数,加减比乘除低。括号的优先级比其他运算符高,至于要不要在改函数里也进行判断,可根据喜好而定。

// 栈的基本操作
typedef struct Stack {
    char* data;
    int top;
    int size;
} Stack;

Stack* createStack(int size) {
    Stack* stack = (Stack*)malloc(sizeof(Stack));
    stack->data = (char*)malloc(size * sizeof(char));
    stack->top = -1;
    stack->size = size; // 栈的最大容量
    return stack;
}

void push(Stack* stack, char c) {
    if (stack->top == stack->size - 1) {
        printf("Stack overflow\n");
        return;
    }
    stack->data[++stack->top] = c;
}

char pop(Stack* stack) {
    if (stack->top == -1) {
        printf("Stack underflow\n");
        return '\0';
    }
    return stack->data[stack->top--];
}

char peek(Stack* stack) { // 返回栈顶元素
    if (stack->top == -1) {
        printf("Stack underflow\n");
        return '\0'; // 空字符
    }
    return stack->data[stack->top];
}

int isEmpty(Stack* stack) {
    return stack->top == -1;
}

// 中缀转后缀
int isOperator(char c) {
    return c == '+' || c == '-' || c == '*' || c == '/';
}

int precedence(char c) { // 优先级
    if (c == '+' || c == '-')
        return 1;
    if (c == '*' || c == '/')
        return 2;
    return 0;
}

void infixToPostfix(char* infix, char* postfix) { // 中缀转后缀,infix中缀表达式,postfix后缀表达式
    Stack* stack = createStack(strlen(infix));
    int i, j;
    for (i = 0, j = 0; infix[i]; ++i) { // 遍历中缀表达式
        char c = infix[i]; // 存储当前字符
        if (isOperator(c)) { // 如果是运算符
            while (!isEmpty(stack) && precedence(peek(stack)) >= precedence(c)) // 栈不为空,且栈顶元素优先级大于等于当前运算符
                postfix[j++] = pop(stack); // 栈顶元素出栈,加入后缀表达式
            push(stack, c); // 当前运算符入栈
        }
        else if (c == '(') { // 如果是左括号,直接入栈
            push(stack, c);
        }
        else if (c == ')') { // 如果是右括号
            while (!isEmpty(stack) && peek(stack) != '(') // 栈不为空,且栈顶元素不是左括号
                postfix[j++] = pop(stack); // 栈顶元素出栈,加入后缀表达式
            pop(stack); // 左括号出栈
        }
        else { // 如果是操作数
            postfix[j++] = c; // 直接加入后缀表达式
        }
    }
    while (!isEmpty(stack)) // 栈不为空
        postfix[j++] = pop(stack); // 栈顶元素出栈,加入后缀表达式
    postfix[j] = '\0';
}

定义一个函数实现中缀转后缀:

创建一个栈用于存储运算符,遍历中缀表达式,如果扫描到当前字符是运算符,有两种情况:如果当前运算符的优先级低于或等于栈顶元素,根据左优先原则,则栈顶元素出栈,加入后缀表达式,最终当前运算符入栈;如果优先级更高,则入栈。

如果扫描到的是左括号,入栈(相当于优先级最高的运算符)。

如果扫描到的是右括号(相当于优先级最低的运算符),在安全的前提下(栈非空),所有元素出栈加入后缀表达式,然后左括号出栈(不要了)。

如果扫描到的是操作数,直接加入后缀表达式。

不要忘记!遍历完中缀表达式后,栈内剩余元素出栈,加入后缀表达式。

2.后缀表达式の计算

运算符栈每弹出一个运算符(运算符的弹出即为生效),从操作数栈栈顶弹出两个操作数(先弹出的是右操作数op2),运算结果再作为为操作数压回栈顶。

// 后缀表达式的求值
int evaluatePostfix(char* postfix) {
	Stack* stack = createStack(strlen(postfix));
	int i, op1, op2;
	for (i = 0; postfix[i]; ++i) { // 遍历后缀表达式
    		char c = postfix[i]; // 存储当前字符
    		if (isdigit(c)) // 如果是操作数
    			push(stack,c - 0); // 将操作数入栈, c - 0将字符转换为数字
    		else { // 如果是运算符
            			op2 = pop(stack); // 第二个操作数
            			op1 = pop(stack); // 第一个操作数
            			switch (c) { // 根据运算符进行运算
                        			case '+': push(stack, op1 + op2); break;
                        			case '-': push(stack, op1 - op2); break;
                        			case '*': push(stack, op1 * op2); break;
                        			case '/': push(stack, op1 / op2); break;
                        			}
            		}
    	}
	return pop(stack); // 返回栈顶元素,即为最终结果
}

int main() {
	char infix[100], postfix[100];
	printf("Enter infix expression: ");
	cin>>infix;
	infixToPostfix(infix, postfix);
	printf("Postfix expression: %s\n", postfix);
	printf("Result: %d\n", evaluatePostfix(postfix));
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值