Алгоритм Дейкстры (Сортировочная станция)
Читать дальше
Транслятор для машины Electrologica X1 Дейкстра и Яап Зонневельд закончили летом 1960 года, опередив все остальные группы мира. Сам алгоритм разбора выражений описан в техническом отчете 1961 года; имя shunting yard (сортировочная станция) родилось из сходства с работой железнодорожной сортировочной горки.
Для Дейкстры это был лишь эпизод большой карьеры: знаменитый алгоритм кратчайшего пути, носящий его имя, семафоры в операционных системах, структурное программирование и легендарное эссе "Go To Statement Considered Harmful" - тоже его работы. В 1972 году Дейкстра получил премию Тьюринга.
Алгоритм Дейкстры (Сортировочная станция): откуда имя
Название алгоритма Shunting-yard algorithm произошло из-за его схожести с железнодорожным узлом:
- Операнды (числа) - это обычные вагоны, они всегда едут прямо на "выходной путь".
- Операторы (+, -, *, /) - это локомотивы. Они временно загоняются в тупик ("Стек операторов"), пока не придет их очередь двигаться на выход в соответствии с приоритетом.
Правила работы алгоритма
Для работы требуется выходная очередь (массив ответа) и стек для хранения операторов. Читаем строку посимвольно слева направо:
- Если токен - число или переменная, он сразу добавляется в выходную очередь.
- Если токен - открывающая скобка
(, она помещается в стек операторов. - Если токен - закрывающая скобка
), операторы выталкиваются из стека в выход до тех пор, пока на вершине не покажется открывающая скобка. Сами скобки уничтожаются. - Если токен - оператор (
+,-,*,/):- Пока на вершине стека находится оператор с большим или равным приоритетом, выталкиваем его из стека в выходную очередь.
- Затем помещаем текущий оператор в стек.
- В конце строки все оставшиеся в стеке операторы выталкиваются в выход.
Тренажер: алгоритм Дейкстры, сортировочная станция
Переведем 2*3+(4-1) в обратную польскую запись.
Как вычислить ОПЗ?
Главная прелесть Обратной польской записи в том, что для ее вычисления больше не нужны ни скобки, ни приоритеты. Машина может вычислить все за один проход, используя простейший стек операндов.
Правила вычисления ОПЗ (чтение слева направо):
- Если число - кладем в стек.
- Если операция - достаем из стека два последних числа, применяем к ним операцию и результат кладем обратно в стек.
Тренажер: вычисление ОПЗ на стеке
Вычислим полученную выше запись 2 3 * 4 1 - +.
Алгоритм Дейкстры в коде на языке Си
Ядро алгоритма Дейкстры - это обработчик прихода нового оператора. Цикл `while` реализует логику вытеснения операторов на основе их приоритета.
// Функция приоритета
int priority(char op) {
if (op == '*' || op == '/') return 2;
if (op == '+' || op == '-') return 1;
return 0; // Для скобок и неизвестных символов
}
// Приход нового оператора из входной строки
void process_operator(char current_op, Stack* operator_stack, Queue* output) {
while (!is_empty(operator_stack)) {
char top_op = top(operator_stack);
// Если на вершине стека оператор с большим или равным приоритетом
if (priority(top_op) >= priority(current_op)) {
enqueue(output, pop(operator_stack)); // Выталкиваем его в ответ
} else {
break; // Вершина слабее, останавливаемся
}
}
// Кладем пришедший оператор в стек
push(operator_stack, current_op);
}
Коротко: главное
- Алгоритм Дейкстры переводит инфиксную запись (со скобками) в ОПЗ (постфиксную, без скобок).
- Операнды идут в выход, операторы - в стек.
- Новый оператор "выталкивает" из стека на выход все операторы, имеющие больший или равный приоритет, после чего сам заходит в стек.
- Закрывающая скобка выталкивает все операторы вплоть до открывающей.
- ОПЗ тривиально вычисляется на одном стеке чисел: встретил знак - достал два числа, вычислил, вернул результат.
Проверьте себя
В стеке операторов лежит * (на вершине). В алгоритм поступает знак +. Что произойдет?