Skip to content
DSA interview 12 min read

Top Tree & Graph Interview Questions with Solutions (C++, Java, JS, Python)

This page covers the tree and graph interview questions that interviewers love because they test recursion, BFS/DFS, and traversal order all at once. Each entry gives the problem, the approach, the complexity, and a full C++/Java/JavaScript/Python solution via the switcher. For the underlying concepts, see trees introduction and graphs introduction.

Mental model: Almost every tree problem is a recursive DFS that combines results from the left and right child. Almost every graph problem is a BFS/DFS over an adjacency structure, with a visited set to avoid cycles. Master those two shapes and these questions become variations on a theme.

The tree solutions assume a node with a val and left/right children, shown in each snippet.

1. Maximum Depth of a Binary Tree

Question. Return the number of nodes along the longest path from the root down to a leaf.

Approach. Post-order recursion: the depth of a node is 1 plus the larger of its children’s depths. An empty subtree has depth 0.

Complexity. O(n) time, O(h) space for the call stack (h = height).

#include <algorithm>
struct TreeNode { int val; TreeNode* left; TreeNode* right; };

int maxDepth(TreeNode* root) {
    if (!root) return 0;
    return 1 + std::max(maxDepth(root->left), maxDepth(root->right));
}

2. Invert a Binary Tree

Question. Mirror a binary tree so every node’s left and right children are swapped.

Approach. Swap children at each node, then recurse into both subtrees.

Complexity. O(n) time, O(h) space.

#include <algorithm>

TreeNode* invertTree(TreeNode* root) {
    if (!root) return nullptr;
    std::swap(root->left, root->right);
    invertTree(root->left);
    invertTree(root->right);
    return root;
}

3. Validate a Binary Search Tree

Question. Decide whether a binary tree is a valid BST: every left value is strictly less, every right value strictly greater, recursively.

Approach. Carry a valid (low, high) range down the tree. Each node must lie strictly inside its range; the left child tightens the upper bound, the right child tightens the lower bound. (Checking only parent vs child is the classic wrong answer.)

Complexity. O(n) time, O(h) space.

#include <climits>

bool validate(TreeNode* node, long lo, long hi) {
    if (!node) return true;
    if (node->val <= lo || node->val >= hi) return false;
    return validate(node->left, lo, node->val) &&
           validate(node->right, node->val, hi);
}
bool isValidBST(TreeNode* root) {
    return validate(root, LONG_MIN, LONG_MAX);
}

4. Binary Tree Level-Order Traversal

Question. Return the node values level by level, top to bottom (a list of lists).

Approach. BFS with a queue. Process the queue one full level at a time by recording its size before the inner loop.

Complexity. O(n) time, O(n) space.

#include <vector>
#include <queue>

std::vector<std::vector<int>> levelOrder(TreeNode* root) {
    std::vector<std::vector<int>> res;
    if (!root) return res;
    std::queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
        int sz = q.size();
        std::vector<int> level;
        for (int i = 0; i < sz; i++) {
            TreeNode* node = q.front(); q.pop();
            level.push_back(node->val);
            if (node->left) q.push(node->left);
            if (node->right) q.push(node->right);
        }
        res.push_back(level);
    }
    return res;
}

See level-order traversal for more BFS patterns.

5. Lowest Common Ancestor of a Binary Tree

Question. Given two nodes p and q, return their lowest common ancestor — the deepest node that has both in its subtree.

Approach. Recurse. If the current node is null, p, or q, return it. If p and q are found in different subtrees, the current node is the LCA; otherwise return whichever side found something.

Complexity. O(n) time, O(h) space.

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    if (!root || root == p || root == q) return root;
    TreeNode* left = lowestCommonAncestor(root->left, p, q);
    TreeNode* right = lowestCommonAncestor(root->right, p, q);
    if (left && right) return root;
    return left ? left : right;
}

Deeper coverage on the lowest common ancestor page.

6. Number of Islands

Question. Given a grid of '1' (land) and '0' (water), count the islands. Land connects horizontally and vertically.

Approach. Scan the grid; on each unvisited land cell, flood-fill with DFS, sinking the whole island to '0', and increment the count. Each cell is visited once.

Complexity. O(rows · cols) time, O(rows · cols) space (recursion in the worst case).

#include <vector>

void sink(std::vector<std::vector<char>>& g, int r, int c) {
    if (r < 0 || c < 0 || r >= (int)g.size() || c >= (int)g[0].size() || g[r][c] != '1')
        return;
    g[r][c] = '0';
    sink(g, r + 1, c); sink(g, r - 1, c);
    sink(g, r, c + 1); sink(g, r, c - 1);
}
int numIslands(std::vector<std::vector<char>>& g) {
    int count = 0;
    for (int r = 0; r < (int)g.size(); r++)
        for (int c = 0; c < (int)g[0].size(); c++)
            if (g[r][c] == '1') { count++; sink(g, r, c); }
    return count;
}

7. Course Schedule (Cycle Detection)

Question. Given numCourses and prerequisite pairs [a, b] (take b before a), can you finish all courses? This is asking whether the directed graph is acyclic.

Approach. Topological sort via Kahn’s algorithm. Build an adjacency list and in-degree counts; repeatedly remove nodes with in-degree 0. If you process every course, there is no cycle.

Complexity. O(V + E) time, O(V + E) space.

#include <vector>
#include <queue>

bool canFinish(int numCourses, std::vector<std::vector<int>>& prerequisites) {
    std::vector<std::vector<int>> adj(numCourses);
    std::vector<int> indeg(numCourses, 0);
    for (auto& p : prerequisites) { adj[p[1]].push_back(p[0]); indeg[p[0]]++; }
    std::queue<int> q;
    for (int i = 0; i < numCourses; i++) if (indeg[i] == 0) q.push(i);
    int done = 0;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        done++;
        for (int v : adj[u]) if (--indeg[v] == 0) q.push(v);
    }
    return done == numCourses;
}

Pitfall: On deep trees or large grids, recursive DFS can blow the call stack. Mention this trade-off in interviews and offer an explicit stack or BFS as the iterative alternative.

Keep practicing

These seven span the essential tree/graph toolkit: recursive DFS that combines child results, range-passing validation, BFS by level, flood fill, and topological sort. Drill more on the tree exercises and graph exercises, then finish with dynamic programming interview questions.

Last updated June 25, 2026
Was this helpful?