Структуры данных — Разбор выражений
Разбор выражений — наиболее классический сценарий применения структуры данных стек.
Разбор выражений демонстрирует, как использовать стеки для преобразования инфиксных, префиксных и постфиксных выражений, а также для вычисления выражений.
Три формы записи выражений
При обработке математических выражений в компьютере существуют три способа представления:
| Type | Позиция оператора | Пример | Сложность вычисления | Необходимость скобок |
|---|---|---|---|---|
| Инфиксное выражение | Между операндами (человеческое соглашение) | A + B * C | Необходимо учитывать приоритет и скобки | Необходимы |
| Префиксное выражение(польская нотация) | Перед операндами | + A * B C | Достаточно одного прохода | Не требуется |
| Постфиксное выражение(обратная польская запись) | После операндов | A B C * + | Достаточно одного прохода | Не требуется |
Самое большое преимущество постфиксных и префиксных выражений заключается в том, что при вычислении не нужно учитывать приоритет операторов или скобки; достаточно последовательно просматривать слева направо.
Алгоритм преобразования инфиксной записи в постфиксную (идея алгоритма сортировочной станции)
Основной алгоритм преобразования инфиксного выражения в постфиксное реализуется с помощью стека. Основной процесс следующий:
- Сканировать инфиксное выражение слева направо
- При обнаруженииоперанда(цифра/буква) выводите его напрямую
- При обнаруженииоператора, сравните его приоритет с оператором на вершине стека:
- Если оператор на вершине стека имеет более высокий или равный приоритет, извлеките верхний элемент и выведите его
- Повторяйте этот процесс, пока текущий оператор не сможет быть помещён в стек
- При обнаружениилевой круглой скобки, поместите её прямо в стек
- При встречеправой скобки, продолжайте извлекать операторы с вершины стека и выводить их, пока не встретится левая скобка (левая скобка извлекается, но не выводится)
- После завершения сканирования извлекайте и выводите оставшиеся в стеке операторы по одному
Пример
#include <ctype.h> /* функция isalnum */
#include <string.h>
#define MAX 100
char stack[MAX];
int top = -1;
void push(char c) { stack[++top] = c; }
char pop() { return (top == -1) ? -1 : stack[top--]; }
/* Возвращает приоритет оператора: чем больше число, тем выше приоритет */
int precedence(char op) {
if (op == '+' || op == '-') return 1; /* Сложение и вычитание имеют самый низкий приоритет */
if (op == '*' || op == '/') return 2; /* Умножение и деление имеют более высокий приоритет */
if (op == '^') return 3; /* Возведение в степень имеет наивысший приоритет */
return 0; /* Не является оператором */
}
Инфиксное выражение в постфиксное выражение
Параметры: infix - входная строка инфиксного выражения
postfix - выходной буфер постфиксного выражения */
void infixToPostfix(char* infix, char* postfix) {
int i = 0, j = 0;
char c;
while ((c = infix[i++]) != '\0') {
/* Операнд (буква или цифра): выводить напрямую */
if (isalnum(c)) {
postfix[j++] = c;
}
/* Левая скобка: поместить в стек напрямую */
else if (c == '(') {
push(c);
}
/* Правая скобка: выполнять pop, пока не встретится левая скобка */
else if (c == ')') {
while (top != -1 && stack[top] != '(') {
postfix[j++] = pop();
}
pop(); /* Выполнить pop для левой скобки '(' но не выводить */
}
/* Оператор: обработать приоритет */
else {
while (top != -1 &&
precedence(stack[top]) >= precedence(c) &&
stack[top] != '(') {
postfix[j++] = pop();
}
push(c);
}
}
/* Выполнить pop всех оставшихся операторов из стека */
while (top != -1) {
postfix[j++] = pop();
}
postfix[j] = '\0'; /* Конец строки */
}
int main() {
char infix[] = "A+B*C";
char postfix[MAX];
infixToPostfix(infix, postfix);
printf("Инфикс: %s\n", infix); /* Вывод: Инфикс: A+B*C */
printf("Постфикс: %s\n", postfix); /* Вывод: Постфикс: ABC*+ */
return 0;
}
Вычисление постфиксного выражения
При использовании постфиксного выражения вычисление с помощью стека очень просто: помещайте операнды в стек; при встрече оператора извлеките два операнда, вычислите и поместите результат обратно.
Пример
#include <ctype.h>
#include <stdlib.h>
#define MAX 100
int stack[MAX];
int top = -1;
void push(int val) { stack[++top] = val; }
int pop() { return stack[top--]; }
/* Вычислить значение постфиксного выражения
Параметры: postfix — постфиксное выражение (операнды — однозначные цифры 0-9)
Возвращаемое значение: вычисленный результат выражения */
int evaluatePostfix(char* postfix) {
int i = 0;
char c;
while ((c = postfix[i++]) != '\0') {
/* Операнд: преобразовать в число и поместить в стек */
if (isdigit(c)) {
push(c - '0'); /* '3' преобразовано в целое число 3 */
}
/* Оператор: извлечь два операнда, вычислить и поместить обратно */
else {
int b = pop(); /* Второй операнд (правый операнд) */
int a = pop(); /* Первый операнд (левый операнд) */
switch (c) {
case '+': push(a + b); break;
case '-': push(a - b); break;
case '*': push(a * b); break;
case '/': push(a / b); break;
}
}
}
return pop(); /* Последний элемент в стеке — окончательный результат */
}
int main() {
/* Постфиксное выражение "23+5*" эквивалентно инфиксному выражению "(2+3)*5" = 25 */
char postfix[] = "23+5*";
int result = evaluatePostfix(postfix);
printf("Postfix %s = %d\n", postfix, result); /* Вывод: Postfix 23+5* = 25 */
return 0;
}
При вычислении постфиксного выражения порядок извлечения операндов критически важен: первым извлекаетсяправый операнд(b), а позже —левый операнд(a). Это особенно важно для некоммутативных операций, таких как вычитание и деление; изменение порядка приведёт к неверным результатам.
Полный пример: вычисление инфиксного выражения
Связывание двух вышеописанных шагов вместе даёт полноценный калькулятор: инфикс → постфикс → вычисление.
Пример
#include <ctype.h>
#define MAX 100
/* Вспомогательная функция приоритета операторов (как выше), функция вычисления (как выше), функция преобразования (как выше) */
/* Для краткости определённые выше функции здесь опущены; включите их при фактическом использовании */
int main() {
char infix[] = "5+3*2"; /* Инфиксное выражение */
char postfix[MAX];
infixToPostfix(infix, postfix); /* Шаг 1: Преобразование в постфиксную запись */
printf("Infix: %s\n", infix); /* Вывод: Infix: 5+3*2 */
printf("Postfix: %s\n", postfix); /* Вывод: Postfix: 532*+ */
int result = evaluatePostfix(postfix); /* Шаг 2: Вычислить */
printf("Result: %s = %d\n", infix, result);
/* Вывод: Result: 5+3*2 = 11 */
return 0;
}