План курса / Все задачи / Алгоритмы и структуры данных
Циклические зависимости
средне
Дан список зависимостей компонентов приложения deps в виде пар [fromFile, toFile], где файл fromFile импортирует файл toFile. Верните true, если в приложении есть циклическая зависимость, иначе — false.
Циклическая зависимость — это ситуация, при которой файл через цепочку импортов прямо или косвенно импортирует сам себя.
public class Solution {
// Если файл в процессе обхода, значит есть цикл
private static bool HasCycle(string fileName, Dictionary<string, List<string>> graph, Dictionary<string, int> state) {
if (state.GetValueOrDefault(fileName, 0) == 1) {
return true;
}
// Если файл уже посещён, цикла нет
if (state.GetValueOrDefault(fileName, 0) == 2) {
return false;
}
// Помечаем файл как находящийся в процессе обхода
state[fileName] = 1;
// Проверяем все файлы, импортируемые текущим файлом
foreach (var next in graph[fileName]) {
if (HasCycle(next, graph, state)) {
return true;
}
}
// Помечаем файл как полностью посещённый
state[fileName] = 2;
return false;
}
public static bool HasCircularDependency(List<List<string>> deps) {
var graph = new Dictionary<string, List<string>>();
// Создаём список смежности из списка зависимостей
foreach (var dep in deps) {
string from = dep[0];
string to = dep[1];
if (!graph.ContainsKey(from))
graph[from] = new List<string>();
graph[from].Add(to);
if (!graph.ContainsKey(to))
graph[to] = new List<string>();
}
var state = new Dictionary<string, int>();
// Проверяем наличие циклов в графе
foreach (var fileName in graph.Keys) {
if (HasCycle(fileName, graph, state)) {
return true;
}
}
// Циклов нет
return false;
}
}
#include <vector>
#include <unordered_map>
#include <string>
using namespace std;
// Если файл в процессе обхода, значит есть цикл
bool hasCycle(const string &fileName, unordered_map<string, vector<string>> &graph, unordered_map<string, int> &state) {
if (state[fileName] == 1) {
return true;
}
// Если файл уже посещён, цикла нет
if (state[fileName] == 2) {
return false;
}
// Помечаем файл как находящийся в процессе обхода
state[fileName] = 1;
// Проверяем все файлы, импортируемые текущим файлом
for (const auto& next : graph[fileName]) {
if (hasCycle(next, graph, state)) {
return true;
}
}
// Помечаем файл как полностью посещённый
state[fileName] = 2;
return false;
}
bool hasCircularDependency(vector<vector<string>> &deps) {
unordered_map<string, vector<string>> graph;
// Создаём список смежности из списка зависимостей
for (const auto& dep : deps) {
string from = dep[0];
string to = dep[1];
graph[from].push_back(to);
if (graph.find(to) == graph.end()) {
graph[to] = {};
}
}
unordered_map<string, int> state;
// Проверяем наличие циклов в графе
for (const auto& entry : graph) {
if (hasCycle(entry.first, graph, state)) {
return true;
}
}
// Циклов нет
return false;
}
package main
// Если файл в процессе обхода, значит есть цикл
func hasCycle(fileName string, graph map[string][]string, state map[string]int) bool {
if state[fileName] == 1 {
return true
}
// Если файл уже посещён, цикла нет
if state[fileName] == 2 {
return false
}
// Помечаем файл как находящийся в процессе обхода
state[fileName] = 1
// Проверяем все файлы, импортируемые текущим файлом
for _, next := range graph[fileName] {
if hasCycle(next, graph, state) {
return true
}
}
// Помечаем файл как полностью посещённый
state[fileName] = 2
return false
}
func hasCircularDependency(deps [][]string) bool {
graph := make(map[string][]string)
// Создаём список смежности из списка зависимостей
for _, dep := range deps {
from, to := dep[0], dep[1]
graph[from] = append(graph[from], to)
if _, exists := graph[to]; !exists {
graph[to] = []string{}
}
}
state := make(map[string]int)
// Проверяем наличие циклов в графе
for fileName := range graph {
if hasCycle(fileName, graph, state) {
return true
}
}
// Циклов нет
return false
}
import java.util.*;
public class Solution {
// Если файл в процессе обхода, значит есть цикл
private boolean hasCycle(String fileName, Map<String, List<String>> graph, Map<String, Integer> state) {
if (state.getOrDefault(fileName, 0) == 1) {
return true;
}
// Если файл уже посещён, цикла нет
if (state.getOrDefault(fileName, 0) == 2) {
return false;
}
// Помечаем файл как находящийся в процессе обхода
state.put(fileName, 1);
// Проверяем все файлы, импортируемые текущим файлом
for (String next : graph.get(fileName)) {
if (hasCycle(next, graph, state)) {
return true;
}
}
// Помечаем файл как полностью посещённый
state.put(fileName, 2);
return false;
}
public boolean hasCircularDependency(List<List<String>> deps) {
Map<String, List<String>> graph = new HashMap<>();
// Создаём список смежности из списка зависимостей
for (var dep : deps) {
String from = dep.get(0);
String to = dep.get(1);
graph.computeIfAbsent(from, k -> new ArrayList<>()).add(to);
graph.putIfAbsent(to, new ArrayList<>());
}
Map<String, Integer> state = new HashMap<>();
// Проверяем наличие циклов в графе
for (String fileName : graph.keySet()) {
if (hasCycle(fileName, graph, state)) {
return true;
}
}
// Циклов нет
return false;
}
}
from typing import *
# Если файл в процессе обхода, значит есть цикл
def has_cycle(file_name: str, graph: Dict[str, List[str]], state: Dict[str, int]) -> bool:
if state.get(file_name, 0) == 1:
return True
# Если файл уже посещён, цикла нет
if state.get(file_name, 0) == 2:
return False
# Помечаем файл как находящийся в процессе обхода
state[file_name] = 1
# Проверяем все файлы, импортируемые текущим файлом
for next_file in graph[file_name]:
if has_cycle(next_file, graph, state):
return True
# Помечаем файл как полностью посещённый
state[file_name] = 2
return False
def has_circular_dependency(deps: List[List[str]]) -> bool:
graph: Dict[str, List[str]] = {}
# Создаём список смежности из списка зависимостей
for dep in deps:
from_file, to_file = dep
graph.setdefault(from_file, []).append(to_file)
graph.setdefault(to_file, [])
state: Dict[str, int] = {}
# Проверяем наличие циклов в графе
for file_name in graph:
if has_cycle(file_name, graph, state):
return True
# Циклов нет
return False
/**
* @param {string} fileName
* @param {Object.<string, string[]>} graph
* @param {Object.<string, number>} state
* @returns {boolean}
*/
function hasCycle(fileName, graph, state) {
// Если файл в процессе обхода, значит есть цикл
if ((state[fileName] || 0) === 1) {
return true;
}
// Если файл уже посещён, цикла нет
if ((state[fileName] || 0) === 2) {
return false;
}
// Помечаем файл как находящийся в процессе обхода
state[fileName] = 1;
// Проверяем все файлы, импортируемые текущим файлом
for (const next of graph[fileName]) {
if (hasCycle(next, graph, state)) {
return true;
}
}
// Помечаем файл как полностью посещённый
state[fileName] = 2;
return false;
}
/**
* @param {string[][]} deps
* @returns {boolean}
*/
export function hasCircularDependency(deps) {
const graph = {};
// Создаём список смежности из списка зависимостей
for (const [from, to] of deps) {
if (!graph[from]) graph[from] = [];
graph[from].push(to);
if (!graph[to]) graph[to] = [];
}
const state = {};
// Проверяем наличие циклов в графе
for (const fileName in graph) {
if (hasCycle(fileName, graph, state)) {
return true;
}
}
// Циклов нет
return false;
}