Open In App

Optimal Tree Connectivity

Last Updated : 15 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given an undirected tree consisting of n vertices and n-1 edges. The task is to add the minimum number of edges in such a way that the length of the shortest path from vertex 1 to any other vertex is at most 2. The edges should be added in a way that the graph does not contain any loops.

Example:

Input: n = 7, edges = {{1, 2}, {2, 3}, {2, 4}, {4, 5}, {4, 6}, {5, 7}}
Output: 2

Input: n = 7, edges = {{1, 2}, {1, 3}, {2, 4}, {2, 5}, {3, 6}, {1, 7}}
Output: 0

Approach:

The idea is to perform a Depth-First Search (DFS) on an undirected tree to calculate the distance of each vertex from the root. It then identifies vertices whose distance from the root exceeds 2 and removes them along with their neighbors iteratively until the resulting graph satisfies the condition of having a minimum shortest path of at most 2 from vertex 1 to any other vertex. The count of removed vertices and their neighbors represents the minimum number of edges to be added to meet the given criteria.

Step-by-step approach:

  • DFS to Calculate Distances:
    • Perform a Depth-First Search (DFS) on the tree to calculate the distance of each vertex from the root (vertex 1).
    • Store the parent of each vertex and the distance from the root.
  • Identify Vertices to Remove:
    • Identify vertices whose distance from the root is greater than 2.
    • Use a set to store these vertices along with their distances.
  • Remove Vertices Iteratively:
    • While there are vertices to remove:
      • Remove a vertex and its corresponding distance from the set.
      • Remove the same vertex and its neighbors from the set.
      • Increment the answer count.
  • Print the count of removed vertices, which represents the minimum number of edges to add.

Below is the implementation of the above approach:

C++




#include <bits/stdc++.h>
 
using namespace std;
 
const int MAX_VERTICES = 200 * 1000 + 11;
 
int parent[MAX_VERTICES]; // Array to store parent of each
                        // vertex in the DFS traversal
int distanceFromRoot[MAX_VERTICES]; // Array to store
                                    // distance of each
                                    // vertex from the root
vector<int>
    tree[MAX_VERTICES]; // Adjacency list representation of
                        // the tree
 
// Depth-first search to compute distances from the root
void computeDistances(int currentVertex,
                    int parentVertex = -1,
                    int currentDistance = 0)
{
    distanceFromRoot[currentVertex] = currentDistance;
    parent[currentVertex] = parentVertex;
 
    for (auto adjacentVertex : tree[currentVertex]) {
        if (adjacentVertex != parentVertex) {
            computeDistances(adjacentVertex, currentVertex,
                            currentDistance + 1);
        }
    }
}
 
int main()
{
    // input
    int numberOfVertices = 7;
    vector<pair<int, int> > edges
        = { { 1, 2 }, { 2, 3 }, { 2, 4 },
            { 4, 5 }, { 4, 6 }, { 5, 7 } };
 
    // Building the tree
    for (const auto& edge : edges) {
        int vertex1 = edge.first - 1;
        int vertex2 = edge.second - 1;
        tree[vertex1].push_back(vertex2);
        tree[vertex2].push_back(vertex1);
    }
 
    // Compute distances from the root (vertex 0)
    computeDistances(0);
 
    set<pair<int, int> > verticesToRemove;
 
    // Collect vertices with distances greater than 2
    for (int i = 0; i < numberOfVertices; ++i) {
        if (distanceFromRoot[i] > 2) {
            verticesToRemove.insert(
                make_pair(-distanceFromRoot[i], i));
        }
    }
 
    int additionalEdges = 0;
 
    // Remove vertices and their neighbors until the tree
    // satisfies the condition
    while (!verticesToRemove.empty()) {
        int currentVertex
            = verticesToRemove.begin()->second;
        currentVertex = parent[currentVertex];
        ++additionalEdges;
        auto it = verticesToRemove.find(
            make_pair(-distanceFromRoot[currentVertex],
                    currentVertex));
        if (it != verticesToRemove.end()) {
            verticesToRemove.erase(it);
        }
        for (auto adjacentVertex : tree[currentVertex]) {
            auto it = verticesToRemove.find(
                make_pair(-distanceFromRoot[adjacentVertex],
                        adjacentVertex));
            if (it != verticesToRemove.end()) {
                verticesToRemove.erase(it);
            }
        }
    }
 
    printf("%d\n", additionalEdges);
 
    return 0;
}


Java




import java.util.*;
 
// Custom Pair class
class Pair<X, Y> {
    public final X first;
    public final Y second;
 
    public Pair(X first, Y second) {
        this.first = first;
        this.second = second;
    }
}
 
public class Main {
    static final int MAX_VERTICES = 200000 + 11;
 
    // Array to store parent of each vertex in the DFS traversal
    static int[] parent = new int[MAX_VERTICES];
 
    // Array to store distance of each vertex from the root
    static int[] distanceFromRoot = new int[MAX_VERTICES];
 
    // Adjacency list representation of the tree
    static List<Integer>[] tree = new ArrayList[MAX_VERTICES];
 
    // Depth-first search to compute distances from the root
    static void computeDistances(int currentVertex,
                                 int parentVertex,
                                 int currentDistance) {
        distanceFromRoot[currentVertex] = currentDistance;
        parent[currentVertex] = parentVertex;
 
        for (int adjacentVertex : tree[currentVertex]) {
            if (adjacentVertex != parentVertex) {
                computeDistances(adjacentVertex, currentVertex, currentDistance + 1);
            }
        }
    }
 
    public static void main(String[] args) {
        // input
        int numberOfVertices = 7;
        List<int[]> edges = Arrays.asList(
            new int[]{1, 2}, new int[]{2, 3}, new int[]{2, 4},
            new int[]{4, 5}, new int[]{4, 6}, new int[]{5, 7}
        );
 
        // Initializing the tree
        for (int i = 0; i < MAX_VERTICES; i++) {
            tree[i] = new ArrayList<>();
        }
 
        // Building the tree
        for (int[] edge : edges) {
            int vertex1 = edge[0] - 1;
            int vertex2 = edge[1] - 1;
            tree[vertex1].add(vertex2);
            tree[vertex2].add(vertex1);
        }
 
        // Compute distances from the root (vertex 0)
        computeDistances(0, -1, 0);
 
        TreeSet<Pair<Integer, Integer>> verticesToRemove = new TreeSet<>(
                Comparator.comparing((Pair<Integer, Integer> p) -> p.first)
                        .thenComparing(p -> p.second)
        );
 
        // Collect vertices with distances greater than 2
        for (int i = 0; i < numberOfVertices; ++i) {
            if (distanceFromRoot[i] > 2) {
                verticesToRemove.add(new Pair<>(-distanceFromRoot[i], i));
            }
        }
 
        int additionalEdges = 0;
 
        // Remove vertices and their neighbors until the tree satisfies the condition
        while (!verticesToRemove.isEmpty()) {
            int currentVertex = verticesToRemove.first().second;
            currentVertex = parent[currentVertex];
            ++additionalEdges;
 
            Iterator<Pair<Integer, Integer>> iterator = verticesToRemove.iterator();
            while (iterator.hasNext()) {
                Pair<Integer, Integer> entry = iterator.next();
                if (entry.second == currentVertex) {
                    iterator.remove();
                }
            }
 
            for (int adjacentVertex : tree[currentVertex]) {
                iterator = verticesToRemove.iterator();
                while (iterator.hasNext()) {
                    Pair<Integer, Integer> entry = iterator.next();
                    if (entry.second == adjacentVertex) {
                        iterator.remove();
                    }
                }
            }
        }
 
        System.out.printf("%d\n", additionalEdges);
    }
}
 
// This code is contributed by akshitaguprzj3


Python3




from collections import defaultdict
 
MAX_VERTICES = 200 * 1000 + 11
 
# Array to store parent of each
# vertex in the DFS traversal
parent = [0] * MAX_VERTICES
 
# Array to store
# distance of each
# vertex from the root
distance_from_root = [0] * MAX_VERTICES
 
tree = defaultdict(list# Adjacency list representation of
# the tree
 
# Depth-first search to compute distances from the root
 
 
def compute_distances(current_vertex, parent_vertex=-1, current_distance=0):
    distance_from_root[current_vertex] = current_distance
    parent[current_vertex] = parent_vertex
 
    for adjacent_vertex in tree[current_vertex]:
        if adjacent_vertex != parent_vertex:
            compute_distances(adjacent_vertex, current_vertex,
                              current_distance + 1)
 
 
def main():
    # input
    number_of_vertices = 7
    edges = [(1, 2), (2, 3), (2, 4), (4, 5), (4, 6), (5, 7)]
 
    # Building the tree
    for edge in edges:
        vertex1, vertex2 = edge
        vertex1 -= 1
        vertex2 -= 1
        tree[vertex1].append(vertex2)
        tree[vertex2].append(vertex1)
 
    # Compute distances from the root (vertex 0)
    compute_distances(0)
 
    vertices_to_remove = set()
 
    # Collect vertices with distances greater than 2
    for i in range(number_of_vertices):
        if distance_from_root[i] > 2:
            vertices_to_remove.add((-distance_from_root[i], i))
 
    additional_edges = 0
 
    # Remove vertices and their neighbors until the tree
    # satisfies the condition
    while vertices_to_remove:
        current_vertex = vertices_to_remove.pop()[1]
        current_vertex = parent[current_vertex]
        additional_edges += 1
        if (-distance_from_root[current_vertex], current_vertex) in vertices_to_remove:
            vertices_to_remove.remove(
                (-distance_from_root[current_vertex], current_vertex))
        for adjacent_vertex in tree[current_vertex]:
            if (-distance_from_root[adjacent_vertex], adjacent_vertex) in vertices_to_remove:
                vertices_to_remove.remove(
                    (-distance_from_root[adjacent_vertex], adjacent_vertex))
 
    print(additional_edges)
 
 
if __name__ == "__main__":
    main()
 
# This code is contributed by ragul21


C#




using System;
using System.Collections.Generic;
using System.Linq;
class Graph
{
    const int MAX_VERTICES = 200000 + 11;
 
    // Arrays to store parent and distance information for each vertex
    static int[] parent = new int[MAX_VERTICES];
    static int[] distanceFromRoot = new int[MAX_VERTICES];
 
    // Dictionary to represent the tree structure
    static Dictionary<int, List<int>> tree = new Dictionary<int, List<int>>();
 
    // Recursive function to compute distances from the root vertex
    static void ComputeDistances(int currentVertex, int parentVertex = -1, int currentDistance = 0)
    {
        distanceFromRoot[currentVertex] = currentDistance;
        parent[currentVertex] = parentVertex;
 
        // Traverse each adjacent vertex and recursively compute distances
        foreach (var adjacentVertex in tree[currentVertex])
        {
            if (adjacentVertex != parentVertex)
            {
                ComputeDistances(adjacentVertex, currentVertex, currentDistance + 1);
            }
        }
    }
 
    static void Main()
    {
        // Number of vertices in the graph
        int numberOfVertices = 7;
 
        // List of edges as tuples
        List<Tuple<int, int>> edges = new List<Tuple<int, int>>
        {
            Tuple.Create(1, 2),
            Tuple.Create(2, 3),
            Tuple.Create(2, 4),
            Tuple.Create(4, 5),
            Tuple.Create(4, 6),
            Tuple.Create(5, 7)
        };
 
        // Populate the tree structure based on the edges
        foreach (var edge in edges)
        {
            int vertex1 = edge.Item1 - 1;
            int vertex2 = edge.Item2 - 1;
 
            if (!tree.ContainsKey(vertex1))
                tree[vertex1] = new List<int>();
            if (!tree.ContainsKey(vertex2))
                tree[vertex2] = new List<int>();
 
            tree[vertex1].Add(vertex2);
            tree[vertex2].Add(vertex1);
        }
 
        // Compute distances from the root (vertex 0)
        ComputeDistances(0);
 
        // Set to store vertices that need to be removed
        HashSet<Tuple<int, int>> verticesToRemove = new HashSet<Tuple<int, int>>();
 
        // Identify vertices with distances greater than 2 and add them to the set
        for (int i = 0; i < numberOfVertices; i++)
        {
            if (distanceFromRoot[i] > 2)
            {
                verticesToRemove.Add(Tuple.Create(-distanceFromRoot[i], i));
            }
        }
 
        // Counter for additional edges to be added
        int additionalEdges = 0;
 
        // Process vertices to be removed and update additional edges
        while (verticesToRemove.Count > 0)
        {
            var currentVertex = verticesToRemove.First(); // Use First to get the first element
            verticesToRemove.Remove(currentVertex);
            int vertex = currentVertex.Item2;
            vertex = parent[vertex];
            additionalEdges++;
 
            // Remove the corresponding vertex from the set
            if (verticesToRemove.Contains(Tuple.Create(-distanceFromRoot[vertex], vertex)))
            {
                verticesToRemove.Remove(Tuple.Create(-distanceFromRoot[vertex], vertex));
            }
 
            // Remove adjacent vertices from the set
            foreach (var adjacentVertex in tree[vertex])
            {
                if (verticesToRemove.Contains(Tuple.Create(-distanceFromRoot[adjacentVertex], adjacentVertex)))
                {
                    verticesToRemove.Remove(Tuple.Create(-distanceFromRoot[adjacentVertex], adjacentVertex));
                }
            }
        }
 
        // Print the result (number of additional edges)
        Console.WriteLine(additionalEdges);
    }
}


Javascript




const MAX_VERTICES = 200000 + 11;
 
let parent = new Array(MAX_VERTICES); // Array to store parent of each vertex in the DFS traversal
let distanceFromRoot = new Array(MAX_VERTICES); // Array to store distance of each vertex from the root
let tree = new Array(MAX_VERTICES).fill(null).map(() => []); // Adjacency list representation of the tree
 
// Depth-first search to compute distances from the root
function computeDistances(currentVertex, parentVertex = -1, currentDistance = 0) {
    distanceFromRoot[currentVertex] = currentDistance;
    parent[currentVertex] = parentVertex;
 
    for (const adjacentVertex of tree[currentVertex]) {
        if (adjacentVertex !== parentVertex) {
            computeDistances(adjacentVertex, currentVertex, currentDistance + 1);
        }
    }
}
 
// input
const numberOfVertices = 7;
const edges = [[1, 2], [2, 3], [2, 4], [4, 5], [4, 6], [5, 7]];
 
// Building the tree
for (const edge of edges) {
    const vertex1 = edge[0] - 1;
    const vertex2 = edge[1] - 1;
    tree[vertex1].push(vertex2);
    tree[vertex2].push(vertex1);
}
 
// Compute distances from the root (vertex 0)
computeDistances(0);
 
let verticesToRemove = new Set();
 
// Collect vertices with distances greater than 2
for (let i = 0; i < numberOfVertices; ++i) {
    if (distanceFromRoot[i] > 2) {
        verticesToRemove.add(i);
    }
}
 
let additionalEdges = 0;
 
// Remove vertices and their neighbors until the tree satisfies the condition
while (verticesToRemove.size > 0) {
    let currentVertex = verticesToRemove.values().next().value;
    verticesToRemove.delete(currentVertex);
    for (const adjacentVertex of tree[currentVertex]) {
        verticesToRemove.delete(adjacentVertex);
    }
    ++additionalEdges;
}
 
console.log(additionalEdges);


Output

2

Time Complexity: O(n log n)
Auxiliary Space: O(n).



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads