Найти все вторые пути данного графа

Это вопрос для собеседования. У меня есть неориентированный граф с n узлами из 1 to n. На графике нет циклов.

Граф представлен двумя списками: списком from и списком to. Ребро соединяет точки from[i] to to[i].

Предположим, что максимальное расстояние между любыми двумя узлами равно max.

Теперь узел считается первичным, если он лежит на простом пути между двумя узлами u и v так, что расстояние между u и v равно max. Все остальные узлы являются вторичными.

Теперь найдите сумму индексов всех второстепенных узлов. Эта сумма является ожидаемым решением.

Example n = 4 
from = [1, 1, 2] 
to = [2, 3, 4]

Answer : 0

Объяснение:

The maximum distance between any two nodes is 3, the pair (3, 4). All the nodes on the path 3 --> 1 --> 2 --> 4 are primary. Since no node is secondary, the answer is 0.

Другие тестовые случаи:

node = 6
from = [1,1,1,2,3]
to  [2,3,4,5,6]

Answer :
4


nodes = 5
from = [1,1,2,2]
to  [2,3,4,5]

Answer :
0

Вот мой код, который я пробовал:

import java.util.*;

public class Main {
    
    public static long solution(int n, List<Integer> from, List<Integer> to) {
        List<List<Integer>> adjList = new ArrayList<>();
        for (int i = 0; i <= n; i++) {
            adjList.add(new ArrayList<>());
        }
        
        // Build adjacency list
        for (int i = 0; i < from.size(); i++) {
            adjList.get(from.get(i)).add(to.get(i));
            adjList.get(to.get(i)).add(from.get(i));
        }
        
        // First DFS/BFS to find the farthest node from any node (let's start with node 1)
        int[] dist = bfs(1, n, adjList);
        int farthest1 = findFarthestNode(dist);
        
        // Second DFS/BFS to find the farthest node from farthest1
        dist = bfs(farthest1, n, adjList);
        int farthest2 = findFarthestNode(dist);
        int mx = dist[farthest2];
        
        // DFS/BFS to find all primary nodes on the path from farthest1 to farthest2
        Set<Integer> primaryNodes = findPrimaryNodes(farthest1, farthest2, n, adjList);
        
        // Sum of secondary influencer indices
        long secondarySum = 0;
        for (int i = 1; i <= n; i++) {
            if (!primaryNodes.contains(i)) {
                secondarySum += i;
            }
        }
        
        return secondarySum;
    }
    
    // BFS function to calculate distances from a start node
    private static int[] bfs(int start, int n, List<List<Integer>> adjList) {
        int[] dist = new int[n + 1];
        Arrays.fill(dist, -1);
        Queue<Integer> queue = new LinkedList<>();
        
        queue.add(start);
        dist[start] = 0;
        
        while (!queue.isEmpty()) {
            int node = queue.poll();
            for (int neighbor : adjList.get(node)) {
                if (dist[neighbor] == -1) {
                    dist[neighbor] = dist[node] + 1;
                    queue.add(neighbor);
                }
            }
        }
        
        return dist;
    }
    
    // Function to find the farthest node given distances
    private static int findFarthestNode(int[] dist) {
        int maxDist = 0;
        int node = 0;
        for (int i = 1; i < dist.length; i++) {
            if (dist[i] > maxDist) {
                maxDist = dist[i];
                node = i;
            }
        }
        return node;
    }
    
    // DFS/BFS to find all primary nodes on the path from farthest1 to farthest2
    private static Set<Integer> findPrimaryNodes(int farthest1, int farthest2, int n, List<List<Integer>> adjList) {
        Set<Integer> primaryNodes = new HashSet<>();
        Stack<Integer> stack = new Stack<>();
        boolean[] visited = new boolean[n + 1];
        int[] parent = new int[n + 1];
        
        stack.push(farthest1);
        visited[farthest1] = true;
        parent[farthest1] = -1;
        
        while (!stack.isEmpty()) {
            int node = stack.pop();
            for (int neighbor : adjList.get(node)) {
                if (!visited[neighbor]) {
                    parent[neighbor] = node;
                    visited[neighbor] = true;
                    stack.push(neighbor);
                    if (neighbor == farthest2) {
                        // Trace back to find the path from farthest2 to farthest1
                        int current = farthest2;
                        while (current != -1) {
                            primaryNodes.add(current);
                            current = parent[current];
                        }
                        return primaryNodes;
                    }
                }
            }
        }
        return primaryNodes;
    }

    public static void main(String[] args) {
        List<Integer> from = Arrays.asList(1, 1, 2);
        List<Integer> to = Arrays.asList(2, 3, 4);
        System.out.println(solution(4, from, to)); // Output: 0

        from = Arrays.asList(1, 1, 1, 2, 3);
        to = Arrays.asList(2, 3, 4, 5, 6);
        System.out.println(solution(6, from, to)); // Output: 4

        from = Arrays.asList(1, 1, 2, 2);
        to = Arrays.asList(2, 3, 4, 5);
        System.out.println(solution(5, from, to)); // Output: 0
    }
}

Я попробовал подход BFS, мой код не работает в третьем тестовом примере, вместо того, чтобы выдать результат как 0, он возвращает 5.

Также в интервью на хакерранке меня спросили: когда я попробовал этот подход, мне удалось решить только 4 тестовых случая из 15.

Каков правильный подход к решению этой проблемы.

Циклов гарантированно не будет? Другими словами, является ли граф лесом?

Unmitigated 05.09.2024 06:46

@unmitigated, на моем графике нет циклов

Sid 05.09.2024 13:36

@talex Почему ты так говоришь?

Unmitigated 06.09.2024 19:19
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
113
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Неориентированный граф без циклов представляет собой лес, каждая компонента которого является деревом. Для каждого компонента мы можем вычислить длину самого длинного пути, проходящего через каждый узел за линейное время с двумя обходами DFS. В конце мы можем просто сравнить максимальный путь через каждый узел с глобальным максимумом.

public static long solution(int n, List<Integer> from, List<Integer> to) {
    var adj = Stream.<List<Integer>>generate(ArrayList::new).limit(n + 1).toList();
    for (int i = 0; i < to.size(); i++) {
        adj.get(from.get(i)).add(to.get(i));
        adj.get(to.get(i)).add(from.get(i));
    }
    int[] up = new int[n + 1], down = new int[n + 1];
    var vis = new boolean[n + 1];
    // creating new object for easy recursion within the solution method
    return new Object() {
        int dfs(int u) {
            vis[u] = true;
            int deep = 0, deep2 = 0;
            for (int v : adj.get(u))
                if (!vis[v]) {
                    up[v] = Math.max(up[u], deep) + 1;
                    int path = dfs(v) + 1;
                    if (path >= deep) {
                        deep2 = deep;
                        deep = path;
                    } else if (path > deep2) deep2 = path;
                }
            down[u] = deep + deep2;
            return deep;
        }
        
        int dfs2(int u, int p) {
            int deep = 0, deep2 = 0;
            for (int i = adj.get(u).size() - 1; i >= 0; i--) {
                int v = adj.get(u).get(i);
                if (v != p) {
                    up[v] = Math.max(up[v], Math.max(up[u], deep) + 1);
                    int path = dfs2(v, u) + 1;
                    if (path >= deep) {
                        deep2 = deep;
                        deep = path;
                    } else if (path > deep2) deep2 = path;
                }
            }
            up[u] += deep;
            return deep;
        }

        long solve() {
            int maxPath = 0;
            for (int i = 1; i <= n; ++i) {
                if (!vis[i]) {
                    dfs(i);
                    dfs2(i, i);
                }
                maxPath = Math.max(maxPath, Math.max(up[i], down[i]));
            }
            long ans = 0;
            for (int i = 1; i <= n; ++i)
                if (Math.max(up[i], down[i]) < maxPath) ans += i;
            return ans;
        }
    }.solve();
}

Другие вопросы по теме

Учитывая список и диапазон, найдите сумму на основе нового списка за меньшее время
Добавление DP в рюкзак 0/1
Написал программу для печати простых чисел. Как мне ее улучшить?
Нахождение максимального произведения элемента массива и расстояния
Эффективное интервальное хранение с использованием стандартной библиотеки C++
Самая длинная полная подпоследовательность упорядоченных гласных
Есть ли способ получить проекцию на заархивированные векторы std::tuple без лямбды?
Как оптимально разместить две точки на числовой прямой, чтобы минимизировать общее расстояние до набора заданных точек
Есть ли в этом алгоритме преобразования двоичного дерева поиска в отсортированный связанный список в Википедии ошибка?
Почему на этапе разделения быстрой сортировки используется одно условие остановки с двумя указателями L и R, а (L <= R)? Может ли это быть пока (L < R)?