Menu
Coddy logo textTech

Топологическая сортировка

Последнее обновление

Топологическая сортировка направленного ациклического графа (DAG) — это линейное упорядочение его вершин такое, что для каждого ребра u → v вершина u идёт перед v. Она отвечает на вопросы вроде «в каком порядке можно выполнять эти задачи, чтобы каждая предпосылка завершалась первой?». Нажмите «воспроизвести» выше, чтобы увидеть, как алгоритм Кана извлекает вершины в корректном порядке.

Алгоритм Кана многократно берёт вершину без оставшихся входящих рёбер (входящая степень 0), добавляет её в порядок и удаляет её исходящие рёбра — что может освободить новые вершины с входящей степенью 0. Он работает только на DAG: если существует цикл, некоторые вершины никогда не достигнут входящей степени 0 и корректного порядка не существует. Он выполняется за время O(V + E).

Временная и пространственная сложность

ПоказательСложностьПримечания
ВремяO(V + E)Каждая вершина выводится один раз, каждое ребро удаляется один раз
ПамятьO(V)Счётчики входящей степени + множество готовых + порядок
ТребуетDAGУ циклов нет топологической сортировки
РезультатНе единственныйМожет существовать много корректных порядков

Шаг за шагом (алгоритм Кана)

ШагЧто происходит
1Вычислить входящую степень (число входящих рёбер) каждой вершины.
2Собрать все вершины с входящей степенью 0 в множество готовых.
3Взять готовую вершину и добавить её в выходной порядок.
4Уменьшить входящую степень каждого её преемника.
5Любой преемник, достигший входящей степени 0, входит в множество готовых.
6Повторять, пока множество готовых не опустеет.

Разобранный пример

Сортировка DAG с рёбрами A→C, B→C, C→D, C→E, D→F, E→F (начальные входящие степени A:0 B:0 C:2 D:1 E:1 F:2):

ШагМножество готовыхПорядокДействие
0{A, B}[]A и B начинают с входящей степенью 0, поэтому обе готовы.
1{B}[A]Вывести A; её ребро A→C снижает входящую степень C с 2 → 1.
2{C}[A, B]Вывести B; её ребро B→C снижает C с 1 → 0, поэтому C становится готовой.
3{D, E}[A, B, C]Вывести C; рёбра C→D и C→E снижают D и E до 0, обе становятся готовыми.
4{E}[A, B, C, D]Вывести D; её ребро D→F снижает входящую степень F с 2 → 1.
5{F}[A, B, C, D, E]Вывести E; её ребро E→F снижает F с 1 → 0, поэтому F становится готовой.
6{}[A, B, C, D, E, F]Вывести F; множество готовых пусто и все 6 вершин упорядочены — готово.

Когда использовать топологическую сортировку

Используйте, когдаИзбегайте, когда
Вам нужен порядок, учитывающий зависимости (шаги сборки, установка пакетов, предпосылки курсов).Граф неориентированный — топологический порядок определён только для ориентированных графов.
Граф является DAG и вам нужен любой корректный линейный порядок.Граф может содержать циклы, а вам всё равно нужен полный порядок (его не существует).
Вы хотите дёшево обнаруживать циклы — неудавшаяся топологическая сортировка доказывает наличие цикла.Вам нужен кратчайший или оптимальный порядок по некоторому весу; простая топологическая сортировка игнорирует веса.
Вы обработаете порядок один раз за O(V + E).Рёбра постоянно меняются и вам нужно пересортировать при каждом обновлении, где лучше подходит инкрементная структура.

Код Topological Sort

Чистая, готовая к запуску реализация Topological Sort на Python, JavaScript, Java, C++, C. Выберите язык, скопируйте код или откройте его уже загруженным в плейграунде Coddy.

Код Topological Sort на Python

Python
1from collections import deque2
3
4def topological_sort(graph):5    # Kahn's algorithm: repeatedly remove nodes with no incoming edges6    in_degree = {node: 0 for node in graph}7    for node in graph:8        for neighbor in graph[node]:9            in_degree[neighbor] += 110    queue = deque(node for node in graph if in_degree[node] == 0)11    order = []12    while queue:13        node = queue.popleft()14        order.append(node)15        for neighbor in graph[node]:16            in_degree[neighbor] -= 117            if in_degree[neighbor] == 0:18                queue.append(neighbor)19    if len(order) != len(graph):20        raise ValueError("Graph has a cycle, no topological order")21    return order22
23
24graph = {25    "shirt": ["tie", "jacket"],26    "tie": ["jacket"],27    "pants": ["shoes", "jacket"],28    "socks": ["shoes"],29    "shoes": [],30    "jacket": [],31}32
33print(" -> ".join(topological_sort(graph)))
Запустите этот код в плейграунде Python

Часто задаваемые вопросы о топологической сортировке

Для чего используется топологическая сортировка?
Она упорядочивает задачи так, чтобы каждая зависимость шла перед тем, что в ней нуждается. Реальные применения включают системы сборки и менеджеры пакетов (сначала компилировать зависимости), составление расписания курсов с предпосылками и порядок вычисления формул в электронных таблицах.
Какова временная сложность топологической сортировки?
И алгоритм Кана, и подход на основе DFS выполняются за время O(V + E), поскольку каждая вершина обрабатывается один раз, а каждое ребро проверяется один раз. Они используют O(V) дополнительной памяти.
Почему топологическая сортировка требует DAG?
Направленный цикл создаёт противоречие: если a должна идти перед b, а b — перед a, никакой линейный порядок не удовлетворяет обоим условиям. Поэтому топологическая сортировка существует тогда и только тогда, когда граф является направленным ациклическим графом. Алгоритм Кана обнаруживает цикл, когда завершается, не выведя все вершины.
В чём разница между алгоритмом Кана и топологической сортировкой на основе DFS?
Алгоритм Кана итеративен и похож на BFS: он многократно удаляет вершины с входящей степенью 0, что делает обнаружение циклов и упорядочение полностью явными. Подход DFS рекурсивно посещает вершины и добавляет каждую в начало порядка по завершении её рекурсии, порождая обратный порядок времён завершения. Оба имеют сложность O(V + E); Кан избегает глубокой рекурсии и естественно раскрывает множество готовых, тогда как DFS обычно короче в написании.
Когда использовать топологическую сортировку вместо обычной?
Используйте топологическую сортировку, когда порядок определяется зависимостями между элементами, а не сравнимым ключом. Сортировка сравнением, например O(n log n) сортировка слиянием, упорядочивает по значению; топологическая сортировка упорядочивает по рёбрам «должно идти раньше» и, в отличие от сортировки сравнением, может давать много корректных ответов для одного и того же ввода.
Единственный ли результат топологической сортировки?
Обычно нет. Всякий раз, когда две или более вершины готовы (входящая степень 0) одновременно, вы можете выводить их в любом порядке, поэтому большинство DAG допускают несколько корректных топологических сортировок. Порядок единственный, только когда на каждом шаге ровно одна готовая вершина, что происходит, когда DAG образует единую цепь (гамильтонов путь).
Coddy programming languages illustration

Освойте алгоритмы с Coddy

НАЧАТЬ