← Назад к графовым алгоритмам

Изоморфизм корневых деревьев

Проверка структурного сходства деревьев с заданными корнями

🐍 Python
n = int(input())
r1, r2 = map(int, input().split())
r1 -= 1
r2 -= 1
G1 = [[] for _ in range(n)]
G2 = [[] for _ in range(n)]

for _ in range(n - 1):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    G1[a].append(b)
    G1[b].append(a)

for _ in range(n - 1):
    a, b = map(int, input().split())
    a -= 1
    b -= 1
    G2[a].append(b)
    G2[b].append(a)

def build(G, r):
    R = [[] for _ in range(n)]
    used = [0] * n
    Q = [r]
    used[r] = 1
    while Q:
        h = Q.pop()
        for a in G[h]:
            if not used[a]:
                used[a] = 1
                R[h].append(a)
                Q.append(a)
    return R

G1 = build(G1, r1)
G2 = build(G2, r2)

def f(G, r):
    def dfs(h):
        if not G[h]:
            return 1
        D = []
        for a in G[h]:
            D.append(dfs(a))
        D.sort()
        z = 1
        for a in D:
            z = (z * (a + 1)) % (10**9 + 7)
        return z
    return dfs(r)

h1 = f(G1, r1)
h2 = f(G2, r2)

if h1 == h2:
    print("YES")
else:
    print("NO")
⚙️ C++
#include <bits/stdc++.h>
using namespace std;

const int MOD = 1000000007;

vector<vector<int>> build(vector<vector<int>>& G, int r, int n) {
    vector<vector<int>> R(n);
    vector<bool> used(n, false);
    stack<int> Q;
    Q.push(r);
    used[r] = true;
    
    while (!Q.empty()) {
        int h = Q.top();
        Q.pop();
        for (int a : G[h]) {
            if (!used[a]) {
                used[a] = true;
                R[h].push_back(a);
                Q.push(a);
            }
        }
    }
    return R;
}

long long dfs(vector<vector<int>>& G, int h) {
    if (G[h].empty()) {
        return 1;
    }
    vector<long long> D;
    for (int a : G[h]) {
        D.push_back(dfs(G, a));
    }
    sort(D.begin(), D.end());
    long long z = 1;
    for (long long a : D) {
        z = (z * (a + 1)) % MOD;
    }
    return z;
}

long long f(vector<vector<int>>& G, int r) {
    return dfs(G, r);
}

int main() {
    int n;
    cin >> n;
    int r1, r2;
    cin >> r1 >> r2;
    r1--; r2--;
    
    vector<vector<int>> G1(n), G2(n);
    
    for (int i = 0; i < n - 1; i++) {
        int a, b;
        cin >> a >> b;
        a--; b--;
        G1[a].push_back(b);
        G1[b].push_back(a);
    }
    
    for (int i = 0; i < n - 1; i++) {
        int a, b;
        cin >> a >> b;
        a--; b--;
        G2[a].push_back(b);
        G2[b].push_back(a);
    }
    
    vector<vector<int>> R1 = build(G1, r1, n);
    vector<vector<int>> R2 = build(G2, r2, n);
    
    long long h1 = f(R1, r1);
    long long h2 = f(R2, r2);
    
    if (h1 == h2) {
        cout << "YES" << endl;
    } else {
        cout << "NO" << endl;
    }
    
    return 0;
}

Как работает проверка изоморфизма корневых деревьев?

Изоморфизм корневых деревьев — это структурное сходство двух деревьев с фиксированными корнями. Алгоритм использует хеширование поддеревьев с сортировкой хешей дочерних вершин.

Шаги алгоритма:

  1. Построение ориентированного дерева из неориентированного:
    • Запускаем обход (BFS/DFS) от заданного корня
    • Для каждой вершины сохраняем только её детей (в направлении от корня)
    • Получаем корневое дерево, где рёбра направлены от предка к потомкам
  2. Хеширование поддеревьев:
    • Для листа хеш = 1
    • Для внутренней вершины собираем хеши всех дочерних поддеревьев, сортируем их
    • Хеш = (хеш_1 + 1) * (хеш_2 + 1) * ... % MOD
  3. Сравнение:
    • Если хеш корня первого дерева равен хешу корня второго — деревья изоморфны
    • Иначе — не изоморфны

Почему это работает? Сортировка хешей детей обеспечивает независимость от порядка детей. Формула хеша инвариантна относительно перестановки поддеревьев.

Временная сложность: O(n log n), где n — количество вершин (из-за сортировки хешей детей).

Пространственная сложность: O(n).

Формат ввода и вывода

Ввод:

Вывод:

Пример ввода (изоморфные деревья):

5
1 2
1 2
1 3
1 4
2 5
1 2
1 3
1 4
2 5

Здесь:
Два одинаковых дерева с корнями 1 и 2
Первое дерево: корень 1, дети 2,3,4; у 2 ребёнок 5
Второе дерево: корень 2, дети 1,3,4; у 1 ребёнок 5

Вывод: YES

Пример ввода (неизоморфные деревья):

5
1 1
1 2
1 3
1 4
2 5
1 2
2 3
2 4
2 5

Вывод: NO

Попробовать проверку изоморфизма корневых деревьев онлайн

Введите два дерева с заданными корнями:

Результат появится здесь...