Welcome to Subscribe On Youtube

1245. Tree Diameter

Description

The diameter of a tree is the number of edges in the longest path in that tree.

There is an undirected tree of n nodes labeled from 0 to n - 1. You are given a 2D array edges where edges.length == n - 1 and edges[i] = [ai, bi] indicates that there is an undirected edge between nodes ai and bi in the tree.

Return the diameter of the tree.

 

Example 1:

Input: edges = [[0,1],[0,2]]
Output: 2
Explanation: The longest path of the tree is the path 1 - 0 - 2.

Example 2:

Input: edges = [[0,1],[1,2],[2,3],[1,4],[4,5]]
Output: 4
Explanation: The longest path of the tree is the path 3 - 2 - 1 - 4 - 5.

 

Constraints:

  • n == edges.length + 1
  • 1 <= n <= 104
  • 0 <= ai, bi < n
  • ai != bi

Solutions

Solution 1: Two DFS

First, perform DFS on any node to find the furthest node, then perform DFS again from this node to reach another furthest node. The node reached by the first DFS can be proven to be one end of the diameter of this graph, and the second DFS will reach the other end. Let’s prove this theorem.

Theorem: In a connected undirected acyclic graph, the furthest node that can be reached from any node must be one end of the diameter of the graph.

Proof: Suppose this diameter is $\delta(s, t)$. There are two cases:

  1. When the starting node $y$ is on $\delta(s, t)$, suppose the furthest node $z$ reached is not any of $s, t$. At this time, connect $\delta(y, z)$ with $\delta(y, s)$ that does not overlap with it (you can also assume that the other direction of the diameter does not overlap), you can get a longer diameter, which contradicts the premise.
  2. When the starting node $y$ is not on $\delta(s, t)$, there are two cases:

    • When the furthest node $z$ reached by $y$ crosses $\delta(s, t)$, mark the intersecting node as $x$. At this time, $\delta(y, z) = \delta(y, x) + \delta(x, z)$. And at this time $\delta(y, z) > \delta(y, t)$, so $\delta(x, z) > \delta(x, t)$. According to the conclusion of 1, this assumption is not established.
    • When the furthest node $z$ reached by $y$ does not intersect with $\delta(s, t)$, define the first node on the simple path from $y$ to $t$ that also exists on the simple path $\delta(s, t)$ as $x$, and the last node on the simple path $\delta(y, z)$ as $x’$. As shown in the figure below. Then according to the assumption, $\delta(y, z) \geq \delta(y, t) \Rightarrow \delta(x’, z) \geq \delta(x’, x) + \delta(x, t)$. In this case, $\delta(x, z) \geq \delta(x, t)$, which does not match the premise that $\delta(s, t)$ corresponds to the diameter, so the furthest node $z$ of $y$ cannot be outside the path corresponding to the diameter from $s$ to $t$.

Therefore, the theorem holds.

Solution 2: Two Explicit Stack Passes

Thinking

The diameter is the number of edges on a longest simple path. With $n \le 10^4$, trying every pair of endpoints is too slow. The farthest vertex from any start is an endpoint of some diameter, and a second walk from that endpoint yields the length. Recursing along a chain uses a call depth equal to the node count and overflows Python once the chain reaches length $1000$. Each search therefore stores $(node, parent, distance)$ on a stack: a larger distance on pop records the current node, then the other neighbors are pushed at distance one greater. The first search from $0$ finds endpoint $a$, and the farthest distance from $a$ is the diameter.

We start at node $0$ and use an explicit stack to find the farthest node $a$, then repeat the search from $a$. The farthest distance in the second search is the diameter of the tree.

Each stack frame is $(i, fa, t)$: the walk has reached node $i$ from parent $fa$ after $t$ edges. When the frame is popped, a larger $t$ replaces the farthest node and the answer, and every neighbor other than the parent is pushed at distance $t + 1$. The tree has no cycle, so each node is pushed once. Which tied endpoint is recorded does not change the length found by the second search.

Time complexity is $O(n)$, and space complexity is $O(n)$, where $n$ is the number of nodes.

Similar problems:

  • class Solution {
        private Map<Integer, Set<Integer>> g;
        private boolean[] vis;
        private int next;
        private int ans;
    
        public int treeDiameter(int[][] edges) {
            int n = edges.length;
            ans = 0;
            g = new HashMap<>();
            for (int[] e : edges) {
                g.computeIfAbsent(e[0], k -> new HashSet<>()).add(e[1]);
                g.computeIfAbsent(e[1], k -> new HashSet<>()).add(e[0]);
            }
            vis = new boolean[n + 1];
            next = edges[0][0];
            dfs(next, 0);
            vis = new boolean[n + 1];
            dfs(next, 0);
            return ans;
        }
    
        private void dfs(int u, int t) {
            if (vis[u]) {
                return;
            }
            vis[u] = true;
            if (ans < t) {
                ans = t;
                next = u;
            }
            for (int v : g.get(u)) {
                dfs(v, t + 1);
            }
        }
    }
    
    
    // Solution 2
    class Solution {
        public int treeDiameter(int[][] edges) {
            int n = edges.length + 1;
            List<Integer>[] g = new List[n];
            Arrays.setAll(g, k -> new ArrayList<>());
            for (var e : edges) {
                int a = e[0], b = e[1];
                g[a].add(b);
                g[b].add(a);
            }
            int node = farthest(g, 0)[0];
            return farthest(g, node)[1];
        }
    
        private int[] farthest(List<Integer>[] g, int start) {
            int ans = 0, node = start;
            Deque<int[]> stk = new ArrayDeque<>();
            stk.push(new int[] {start, -1, 0});
            while (!stk.isEmpty()) {
                int[] cur = stk.pop();
                int i = cur[0], fa = cur[1], t = cur[2];
                if (ans < t) {
                    ans = t;
                    node = i;
                }
                for (int j : g[i]) {
                    if (j != fa) {
                        stk.push(new int[] {j, i, t + 1});
                    }
                }
            }
            return new int[] {node, ans};
        }
    }
    
    
  • class Solution {
    public:
        unordered_map<int, unordered_set<int>> g;
        vector<bool> vis;
        int ans;
        int next;
    
        int treeDiameter(vector<vector<int>>& edges) {
            for (auto& e : edges) {
                g[e[0]].insert(e[1]);
                g[e[1]].insert(e[0]);
            }
            int n = edges.size();
            ans = 0;
            vis.resize(n + 1);
            next = edges[0][0];
            dfs(next, 0);
            vis.assign(vis.size(), false);
            dfs(next, 0);
            return ans;
        }
    
        void dfs(int u, int t) {
            if (vis[u]) return;
            vis[u] = true;
            if (ans < t) {
                ans = t;
                next = u;
            }
            for (int v : g[u]) dfs(v, t + 1);
        }
    };
    
    
    // Solution 2
    class Solution {
    public:
        int treeDiameter(vector<vector<int>>& edges) {
            int n = edges.size() + 1;
            vector<vector<int>> g(n);
            for (auto& e : edges) {
                int a = e[0], b = e[1];
                g[a].push_back(b);
                g[b].push_back(a);
            }
            auto farthest = [&](int start) -> pair<int, int> {
                int ans = 0, node = start;
                vector<array<int, 3>> stk{{start, -1, 0}};
                while (!stk.empty()) {
                    auto [i, fa, t] = stk.back();
                    stk.pop_back();
                    if (ans < t) {
                        ans = t;
                        node = i;
                    }
                    for (int j : g[i]) {
                        if (j != fa) {
                            stk.push_back({j, i, t + 1});
                        }
                    }
                }
                return {node, ans};
            };
            int node = farthest(0).first;
            return farthest(node).second;
        }
    };
    
    
  • class Solution:
        def treeDiameter(self, edges: List[List[int]]) -> int:
            def dfs(u, t):
                nonlocal ans, vis, d, next
                if vis[u]:
                    return
                vis[u] = True
                for v in d[u]:
                    dfs(v, t + 1)
                if ans < t:
                    ans = t
                    next = u
    
            d = defaultdict(set)
            vis = [False] * (len(edges) + 1)
            for u, v in edges:
                d[u].add(v)
                d[v].add(u)
            ans = 0
            next = 0
            dfs(edges[0][0], 0)
            vis = [False] * (len(edges) + 1)
            dfs(next, 0)
            return ans
    
    
    
    # Solution 2
    class Solution:
        def treeDiameter(self, edges: List[List[int]]) -> int:
            n = len(edges) + 1
            g = [[] for _ in range(n)]
            for a, b in edges:
                g[a].append(b)
                g[b].append(a)
    
            def farthest(start: int) -> (int, int):
                ans = 0
                node = start
                stk = [(start, -1, 0)]
                while stk:
                    i, fa, t = stk.pop()
                    if ans < t:
                        ans = t
                        node = i
                    for j in g[i]:
                        if j != fa:
                            stk.append((j, i, t + 1))
                return node, ans
    
            node, _ = farthest(0)
            return farthest(node)[1]
    
    
  • func treeDiameter(edges [][]int) int {
    	n := len(edges)
    	g := make(map[int][]int)
    	for _, e := range edges {
    		g[e[0]] = append(g[e[0]], e[1])
    		g[e[1]] = append(g[e[1]], e[0])
    	}
    	vis := make(map[int]bool, n+1)
    	ans := 0
    	next := edges[0][0]
    	var dfs func(u, t int)
    	dfs = func(u, t int) {
    		if vis[u] {
    			return
    		}
    		vis[u] = true
    		if ans < t {
    			ans = t
    			next = u
    		}
    		if vs, ok := g[u]; ok {
    			for _, v := range vs {
    				dfs(v, t+1)
    			}
    		}
    	}
    	dfs(next, 0)
    	vis = make(map[int]bool, n+1)
    	dfs(next, 0)
    	return ans
    }
    
    
    // Solution 2
    func treeDiameter(edges [][]int) int {
    	n := len(edges) + 1
    	g := make([][]int, n)
    	for _, e := range edges {
    		a, b := e[0], e[1]
    		g[a] = append(g[a], b)
    		g[b] = append(g[b], a)
    	}
    	farthest := func(start int) (int, int) {
    		ans, node := 0, start
    		stk := [][3]int{{start, -1, 0}}
    		for len(stk) > 0 {
    			cur := stk[len(stk)-1]
    			stk = stk[:len(stk)-1]
    			i, fa, t := cur[0], cur[1], cur[2]
    			if ans < t {
    				ans, node = t, i
    			}
    			for _, j := range g[i] {
    				if j != fa {
    					stk = append(stk, [3]int{j, i, t + 1})
    				}
    			}
    		}
    		return node, ans
    	}
    	node, _ := farthest(0)
    	_, ans := farthest(node)
    	return ans
    }
    
    
  • function treeDiameter(edges: number[][]): number {
        const n = edges.length + 1;
        const g: number[][] = Array.from({ length: n }, () => []);
        for (const [a, b] of edges) {
            g[a].push(b);
            g[b].push(a);
        }
        let [ans, a] = [0, 0];
        const dfs = (i: number, fa: number, t: number): void => {
            for (const j of g[i]) {
                if (j !== fa) {
                    dfs(j, i, t + 1);
                }
            }
            if (ans < t) {
                ans = t;
                a = i;
            }
        };
        dfs(0, -1, 0);
        dfs(a, -1, 0);
        return ans;
    }
    
    
    
    // Solution 2
    function treeDiameter(edges: number[][]): number {
        const n = edges.length + 1;
        const g: number[][] = Array.from({ length: n }, () => []);
        for (const [a, b] of edges) {
            g[a].push(b);
            g[b].push(a);
        }
        const farthest = (start: number): [number, number] => {
            let ans = 0;
            let node = start;
            const stk: number[][] = [[start, -1, 0]];
            while (stk.length) {
                const [i, fa, t] = stk.pop()!;
                if (ans < t) {
                    ans = t;
                    node = i;
                }
                for (const j of g[i]) {
                    if (j !== fa) {
                        stk.push([j, i, t + 1]);
                    }
                }
            }
            return [node, ans];
        };
        const [node] = farthest(0);
        return farthest(node)[1];
    }
    
    

All Problems

All Solutions