Talentd
FleetCode
Make and follow sheets.Sign in

Find the Last Marked Nodes in Tree solution and explanation

HardDepth-First SearchTree6 min read57.1% acceptanceUpdated 24 Mar 2026Premium elsewhere, free here
Find a Corresponding Node of a Binary Tree in a Clone of That Tree | Live Coding | Leetcode - 1379Algorithms Made Easy · 5:42 · 5K viewsWatch on YouTube
3 more videos on this problem
  1. Find a Corresponding Node of a Binary Tree in a Clone of That Tree | Live Coding | Leetcode - 1379Algorithms Made Easy · 5:42 · 5K views
  2. Leetcode - Find a Corresponding Node of a Binary Tree in a Clone of That Tree (Python)Timothy H Chang · 5:02 · 1K views
  3. FIND NUMBER OF COINS TO PLACE IN BINARY TREE NODES | LEETCODE 2973 | PYTHON DFS + HEAP SOLUTIONCracking FAANG · 24:59 · 1K views
  4. LeetCode 1379 Find a Corresponding Node of a Binary Tree in a Clone of That TreePuzzlingClarity · 3:07 · 297 views

The problem

There exists an undirected tree with n nodes numbered 0 to n - 1. You are given a 2D integer array edges of length n - 1, where edges[i] = [ui, vi] indicates that there is an edge between nodes ui and vi in the tree.

Initially, all nodes are unmarked. After every second, you mark all unmarked nodes which have at least one marked node adjacent to them.

Return an array nodes where nodes[i] is the last node to get marked in the tree, if you mark node i at time t = 0. If nodes[i] has multiple answers for any node i, you can choose any one answer.

Example 1:

Input: edges = [[0,1],[0,2]]

Output: [2,2,1]

Explanation:

  • For i = 0, the nodes are marked in the sequence: [0] -> [0,1,2]. Either 1 or 2 can be the answer.
  • For i = 1, the nodes are marked in the sequence: [1] -> [0,1] -> [0,1,2]. Node 2 is marked last.
  • For i = 2, the nodes are marked in the sequence: [2] -> [0,2] -> [0,1,2]. Node 1 is marked last.

Example 2:

Input: edges = [[0,1]]

Output: [1,0]

Explanation:

  • For i = 0, the nodes are marked in the sequence: [0] -> [0,1].
  • For i = 1, the nodes are marked in the sequence: [1] -> [0,1].

Example 3:

Input: edges = [[0,1],[0,2],[2,3],[2,4]]

Output: [3,3,1,1,1]

Explanation:

  • For i = 0, the nodes are marked in the sequence: [0] -> [0,1,2] -> [0,1,2,3,4].
  • For i = 1, the nodes are marked in the sequence: [1] -> [0,1] -> [0,1,2] -> [0,1,2,3,4].
  • For i = 2, the nodes are marked in the sequence: [2] -> [0,2,3,4] -> [0,1,2,3,4].
  • For i = 3, the nodes are marked in the sequence: [3] -> [2,3] -> [0,2,3,4] -> [0,1,2,3,4].
  • For i = 4, the nodes are marked in the sequence: [4] -> [2,4] -> [0,2,3,4] -> [0,1,2,3,4].

Constraints:

  • 2 <= n <= 105
  • edges.length == n - 1
  • edges[i].length == 2
  • 0 <= edges[i][0], edges[i][1] <= n - 1
  • The input is generated such that edges represents a valid tree.

Approach overview

Problem Overview: You are given a tree where some nodes are marked. For every node, determine which marked node would be the last one reached if a signal spreads through the tree edges one step at a time. In practice, this means finding the marked node with the maximum distance from each node.

Approach 1: Brute Force DFS from Every Node (O(n^2) time, O(n) space)

Treat each node as a starting point. Run a Depth-First Search or BFS to compute the distance to all other nodes, then pick the farthest node that is marked. Repeat this process for every node. The idea is straightforward: explore the entire tree and track the maximum distance to a marked node. However, because each traversal touches all n nodes, the total cost becomes O(n^2). This approach works for small trees but quickly becomes too slow for large inputs.

Approach 2: Multi-Source BFS from Marked Nodes (O(n) time, O(n) space)

Start a BFS simultaneously from all marked nodes using a queue. This computes the closest marked node for every node in the tree. While useful for proximity problems, it does not directly solve the "last marked" requirement because the farthest node matters, not the nearest one. You would need additional distance tracking or reverse reasoning to determine the farthest marked node. This method is helpful for understanding the structure of distances but is usually not the final optimized solution.

Approach 3: Tree Diameter of Marked Nodes + DFS (O(n) time, O(n) space)

The key observation: in a tree, the farthest node from any node must lie on the tree's diameter. If you restrict the diameter to marked nodes, the answer for any node must be one of the two endpoints of that diameter. First, run a DFS to find the farthest marked node from any marked node. Then run another DFS from that node to find the opposite endpoint of the marked diameter. Finally, compute distances from both endpoints using DFS. For each node, compare the distance to both endpoints and choose the farther one. This works because the longest path between marked nodes captures the extreme candidates.

This technique relies heavily on properties of trees and repeated DFS traversals. Each traversal is linear, and only a few passes over the adjacency list are needed.

Recommended for interviews: The diameter + DFS approach is what interviewers typically expect. Starting with the brute force method shows you understand the problem definition. Transitioning to the diameter insight demonstrates strong tree intuition and algorithmic optimization, reducing the complexity from O(n^2) to O(n).

Solution

According to the problem description, the last marked node must be one endpoint of the tree's diameter, because the distance from any node on the diameter to any other node on the diameter is the greatest.

We can start a depth-first search (DFS) from any node to find the farthest node a, which is one endpoint of the tree's diameter.

Then, starting from node a, we perform another depth-first search to find the farthest node b, which is the other endpoint of the tree's diameter. During this process, we calculate the distance from each node to node a, denoted as dist2.

Next, we perform a depth-first search starting from node b to calculate the distance from each node to node b, denoted as dist3.

For each node i, if dist2[i] > dist3[i], then the distance from node a to node i is greater, so node a is the last marked node; otherwise, node b is the last marked node.

The time complexity is O(n), and the space complexity is O(n). Here, n$ is the number of nodes.

class Solution:
    def lastMarkedNodes(self, edges: List[List[int]]) -> List[int]:
        def dfs(i: int, fa: int, dist: List[int]):
            for j in g[i]:
                if j != fa:
                    dist[j] = dist[i] + 1
                    dfs(j, i, dist)

        n = len(edges) + 1
        g = [[] for _ in range(n)]
        for u, v in edges:
            g[u].append(v)
            g[v].append(u)

        dist1 = [-1] * n
        dist1[0] = 0
        dfs(0, -1, dist1)
        a = dist1.index(max(dist1))

        dist2 = [-1] * n
        dist2[a] = 0
        dfs(a, -1, dist2)
        b = dist2.index(max(dist2))

        dist3 = [-1] * n
        dist3[b] = 0
        dfs(b, -1, dist3)

        return [a if x > y else b for x, y in zip(dist2, dist3)]
class Solution {
    private List<Integer>[] g;

    public int[] lastMarkedNodes(int[][] edges) {
        int n = edges.length + 1;
        g = new List[n];
        Arrays.setAll(g, k -> new ArrayList<>());
        for (var e : edges) {
            int u = e[0], v = e[1];
            g[u].add(v);
            g[v].add(u);
        }
        int[] dist1 = new int[n];
        dist1[0] = 0;
        dfs(0, -1, dist1);
        int a = maxNode(dist1);

        int[] dist2 = new int[n];
        dist2[a] = 0;
        dfs(a, -1, dist2);
        int b = maxNode(dist2);

        int[] dist3 = new int[n];
        dist3[b] = 0;
        dfs(b, -1, dist3);

        int[] ans = new int[n];
        for (int i = 0; i < n; ++i) {
            ans[i] = dist2[i] > dist3[i] ? a : b;
        }
        return ans;
    }

    private void dfs(int i, int fa, int[] dist) {
        for (int j : g[i]) {
            if (j != fa) {
                dist[j] = dist[i] + 1;
                dfs(j, i, dist);
            }
        }
    }

    private int maxNode(int[] dist) {
        int mx = 0;
        for (int i = 0; i < dist.length; ++i) {
            if (dist[mx] < dist[i]) {
                mx = i;
            }
        }
        return mx;
    }
}
class Solution {
public:
    vector<int> lastMarkedNodes(vector<vector<int>>& edges) {
        int n = edges.size() + 1;
        g.resize(n);
        for (const auto& e : edges) {
            int u = e[0], v = e[1];
            g[u].push_back(v);
            g[v].push_back(u);
        }
        vector<int> dist1(n);
        dfs(0, -1, dist1);
        int a = max_element(dist1.begin(), dist1.end()) - dist1.begin();

        vector<int> dist2(n);
        dfs(a, -1, dist2);
        int b = max_element(dist2.begin(), dist2.end()) - dist2.begin();

        vector<int> dist3(n);
        dfs(b, -1, dist3);

        vector<int> ans;
        for (int i = 0; i < n; ++i) {
            ans.push_back(dist2[i] > dist3[i] ? a : b);
        }
        return ans;
    }

private:
    vector<vector<int>> g;

    void dfs(int i, int fa, vector<int>& dist) {
        for (int j : g[i]) {
            if (j != fa) {
                dist[j] = dist[i] + 1;
                dfs(j, i, dist);
            }
        }
    }
};
/**
 * @param {number[][]} edges
 * @return {number[]}
 */
var lastMarkedNodes = function (edges) {
    const n = edges.length + 1;
    const g = Array.from({ length: n }, () => []);
    for (const [u, v] of edges) {
        g[u].push(v);
        g[v].push(u);
    }
    const dfs = (i, fa, dist) => {
        for (const j of g[i]) {
            if (j !== fa) {
                dist[j] = dist[i] + 1;
                dfs(j, i, dist);
            }
        }
    };

    const dist1 = Array(n).fill(0);
    dfs(0, -1, dist1);
    const a = dist1.indexOf(Math.max(...dist1));

    const dist2 = Array(n).fill(0);
    dfs(a, -1, dist2);
    const b = dist2.indexOf(Math.max(...dist2));

    const dist3 = Array(n).fill(0);
    dfs(b, -1, dist3);

    const ans = [];
    for (let i = 0; i < n; ++i) {
        ans.push(dist2[i] > dist3[i] ? a : b);
    }
    return ans;
};
function lastMarkedNodes(edges: number[][]): number[] {
    const n = edges.length + 1;
    const g: number[][] = Array.from({ length: n }, () => []);
    for (const [u, v] of edges) {
        g[u].push(v);
        g[v].push(u);
    }
    const dfs = (i: number, fa: number, dist: number[]) => {
        for (const j of g[i]) {
            if (j !== fa) {
                dist[j] = dist[i] + 1;
                dfs(j, i, dist);
            }
        }
    };

    const dist1: number[] = Array(n).fill(0);
    dfs(0, -1, dist1);
    const a = dist1.indexOf(Math.max(...dist1));

    const dist2: number[] = Array(n).fill(0);
    dfs(a, -1, dist2);
    const b = dist2.indexOf(Math.max(...dist2));

    const dist3: number[] = Array(n).fill(0);
    dfs(b, -1, dist3);

    const ans: number[] = [];
    for (let i = 0; i < n; ++i) {
        ans.push(dist2[i] > dist3[i] ? a : b);
    }
    return ans;
}
func lastMarkedNodes(edges [][]int) (ans []int) {
	n := len(edges) + 1
	g := make([][]int, n)
	for _, e := range edges {
		u, v := e[0], e[1]
		g[u] = append(g[u], v)
		g[v] = append(g[v], u)
	}
	var dfs func(int, int, []int)
	dfs = func(i, fa int, dist []int) {
		for _, j := range g[i] {
			if j != fa {
				dist[j] = dist[i] + 1
				dfs(j, i, dist)
			}
		}
	}
	maxNode := func(dist []int) int {
		mx := 0
		for i, d := range dist {
			if dist[mx] < d {
				mx = i
			}
		}
		return mx
	}

	dist1 := make([]int, n)
	dfs(0, -1, dist1)
	a := maxNode(dist1)

	dist2 := make([]int, n)
	dfs(a, -1, dist2)
	b := maxNode(dist2)

	dist3 := make([]int, n)
	dfs(b, -1, dist3)

	for i, x := range dist2 {
		if x > dist3[i] {
			ans = append(ans, a)
		} else {
			ans = append(ans, b)
		}
	}
	return
}
Try this approach in the editor

Complexity analysis

ApproachTimeSpaceWhen to Use
Brute Force DFS from Every NodeO(n^2)O(n)Small trees or when first reasoning about the problem
Multi-Source BFS from Marked NodesO(n)O(n)Useful for analyzing distance structure or nearest-marked queries
Diameter of Marked Nodes + DFSO(n)O(n)Optimal solution for large trees and interview settings

Frequently asked questions

Is Find the Last Marked Nodes in Tree easy or hard?

Find the Last Marked Nodes in Tree is classified as a Hard problem because it requires recognizing a non-obvious tree property. A naive approach is easy to implement but inefficient. The optimized solution requires understanding tree diameter and distance propagation using DFS.

Find the Last Marked Nodes in Tree Python/Java solution

Implement the solution by building the tree with adjacency lists, running DFS to find the diameter endpoints among marked nodes, and computing distances from both endpoints. The same logic works across Python, Java, C++, Go, TypeScript, and JavaScript because the algorithm only relies on DFS and array distance tracking.

How to solve Find the Last Marked Nodes in Tree in O(n)?

First build the adjacency list for the tree. Run DFS from any marked node to find the farthest marked node A. Run another DFS from A to find the opposite marked endpoint B of the diameter. Compute distances from both A and B, and for each node choose whichever endpoint is farther. This completes in linear time.

What is the best approach for Find the Last Marked Nodes in Tree?

The most efficient approach is computing the diameter formed by the marked nodes and then running DFS from the two diameter endpoints. For each node, compare distances to both endpoints and select the farther marked node. This works because the farthest node in a tree must lie on the diameter. The method runs in O(n) time and O(n) space.

Is Find the Last Marked Nodes in Tree asked at Google/Amazon/Meta?

Problems involving tree diameter, farthest node queries, and DFS distance propagation frequently appear in interviews at companies like Google, Amazon, and Meta. Variations of this problem test understanding of tree properties and efficient graph traversal techniques.

What data structure is used in Find the Last Marked Nodes in Tree?

The problem uses an adjacency list to represent the tree along with Depth-First Search for traversal. Arrays or vectors store distances from candidate nodes. The algorithm relies on tree properties and graph traversal rather than complex data structures.

What is the time complexity of Find the Last Marked Nodes in Tree?

The optimal solution runs in O(n) time where n is the number of nodes in the tree. It performs a few DFS traversals to find the marked-node diameter and compute distances. Space complexity is O(n) due to the adjacency list and recursion or stack used in DFS.

Comments

Now write it yourselfClose this page and solve Find the Last Marked Nodes in Tree in the editor, without looking.
Practise Find the Last Marked Nodes in Tree