Алгоритмы на графах
Теория: Алгоритм Литтла: реализация в коде
В этом уроке мы перейдем от теории к практике. Вы узнаете, как реализовать алгоритм Литтла в коде.
Чтобы реализовать алгоритм Литтла, мы написали такой объемный код:
Нажмите, чтобы увидеть код
В таком объемном коде сложно разобраться самостоятельно, поэтому мы подробно обсудим, что происходит на каждом шаге.
Здесь мы реализовали вспомогательный класс Node, в котором сосредоточили функции для работы с узлами дерева решений.
JavaScript хранит ссылку на объекты, в том числе на массивы. Если мы хотим создать копию матрицы смежности в каждом узле, мы не можем просто скопировать массив. Нужно создать новый массив и скопировать в него все значения исходного массива.
Матрица — это массив массивов. Поэтому мы выполняем глубокое копирование:
Еще нам нужно создать две функции:
- Первая функция будет искать минимумы в каждой строке
- Вторая — редуцировать, то есть вычитать минимумы из каждого элемента каждой строки
Так выглядит реализация в коде:
Далее мы напишем похожие функции и для столбцов. Затем реализуем метод редуцирования матрицы и по строкам и по столбцам:
Последний метод в классе Node — это getCellWithMaxPenalty. Он находит ячейку матрицы с наибольшим штрафом.
Как мы помним, метод пробегает по всем нулевым элементам и складывает минимумы в той же строке и в том же столбце. При вычислении минимума сам элемент не учитывается:
Во время работы алгоритма Литтла мы должны исключить из рассмотрения закрывающие ребра — те, которые могут завершить маршрут раньше времени.
Для поиска закрывающих ребер напишем функцию getCloseEdges. Для ее работы реализуем вспомогательные функции findNextStartCity и findNextEndCity:
На каждом шаге алгоритма мы строим левое и правое поддеревья из какого-то узла дерева решений.
Вынесем код построения в функцию makeChildren. Используем написанные нами ранее методы и функции — getCellWithMaxPenalty, cloneMatrix и reduce:
Наконец, реализуем функцию little. В стандартной библиотеке JavaScript нет структуры данных, которая представляла бы очередь с приоритетами. Поэтому мы сделаем собственную простую структуру и будем выбирать узел с наименьшей нижней границей каждом шаге алгоритма:
Выводы
В этом уроке мы закончили изучать алгоритм Литтла. Теперь вы знаете, как он работает в теории и как реализовать его в коде. Повторим ключевые выводы, к которым мы пришли в прошлом теоретическом уроке:
- Алгоритм Литтла находит оптимальное решение в дереве решений
- Для работы алгоритма важно, чтобы оценка нижней границы была корректной
- Обычно алгоритм Литтла работает быстрее метода перебора, в худшем случае — с той же скоростью
O(n!)

