Есть несколько методов, каждый из которых может быть быстрее в зависимости от вашей структуры, но в общем случае вам нужен обходной путь.
Поиск в глубину вначале проходит через каждый возможный маршрут, отслеживая уже посещенные узлы. Это рекурсивная функция, потому что на каждом узле вы должны разветвляться и пробовать каждый его дочерний узел. Нет более быстрого метода, если вы не знаете, какой способ искать объект, вам просто нужно попробовать каждый путь! Вам определенно нужно следить за тем, где вы уже были, иначе это было бы расточительно. Это должно потребовать порядка количества узлов, чтобы сделать полный обход.
Поиск в ширину аналогичен, но посещает каждого потомка узла, прежде чем "двигаться дальше", и, таким образом, создает слои расстояния от выбранного корня. Это может быть быстрее, если ожидается, что место назначения будет близко к корневому узлу. Это будет медленнее, если ожидается, что он будет проходить весь путь, потому что это заставляет вас пересекать все возможные ребра.
Вы правы в том, что, возможно, ведете список известных корневых узлов, компромисс здесь заключается в том, что вам в основном приходится выполнять поиск всякий раз, когда вы изменяете график. Если вы изменяете график редко, это приемлемо, но если вы изменяете график чаще, чем нужно для создания этой информации, то, конечно, это слишком дорого.
РЕДАКТИРОВАТЬ: Обновление информации.
Похоже, что мы на самом деле ищем путь между двумя произвольными узлами, семантика корня / листа продолжает переключаться. DepthFirstSearch (DFS) запускается на одном узле, а затем для каждого непосещенного дочернего элемента выполняется рекурсия. Перерыв, если вы найдете целевой узел. Из-за способа оценки рекурсии, он будет проходить весь путь вниз по «левому» пути, а затем перечислять узлы на этом расстоянии, прежде чем когда-либо попадет на «правильный» путь. Это дорогостоящее и неэффективное время, если целевой узел потенциально является первым дочерним элементом справа. BreadthFirst идет по ступеням, охватывая всех детей, прежде чем двигаться вперед. Поскольку ваш график является тяжелым снизу, как дерево, у обоих будет примерно одинаковое время выполнения.
Когда график тяжелый снизу, вас может заинтересовать обратный обход. Начните с целевого узла и идите вверх, потому что в этом направлении относительно меньше узлов. Пока узлы в целом имеют больше родителей, чем детей, это направление будет намного быстрее. Вы также можете объединить подходы, ступая один вверх и один вниз, затем сравнивая списки узлов и встречаясь где-то посередине. (эта комбинация может показаться самой быстрой, если вы игнорируете, что на каждом шаге выполняется вдвое больше работы).
Однако, так как вы сказали, что ваш график хранится в виде списка списков детей, у вас нет реального способа обхода графика в обратном направлении. Узел не знает, каковы его родители. Это проблема. Чтобы исправить это, вы должны получить узел, чтобы узнать, каковы его родители, добавив эти данные при обновлении графа или создав дубликат всей структуры (которую вы сказали, слишком большой). Потребуется переписать всю структуру, что, вероятно, не может быть и речи, поскольку на данный момент она является большой базой данных.
Там много работы.
http://en.wikipedia.org/wiki/Graph_(data_structure)