ভূমিকা

BFS (Breadth-First Search) এবং DFS (Depth-First Search) হলো graph ও tree traversal-এর দুটি মৌলিক অ্যালগরিদম। ইন্টারভিউতে এগুলো সবচেয়ে বেশি জিজ্ঞাসিত বিষয়গুলোর একটি। নিচে ৫টি Easy স্তরের BFS/DFS সমস্যা Java সমাধান ও বাংলা ব্যাখ্যা সহ দেওয়া হলো।

BFS বনাম DFS — দ্রুত পর্যালোচনা

বৈশিষ্ট্য BFS DFS
Data Structure Queue Stack (বা Recursion)
Traversal Level by level গভীরে যায় প্রথমে
Shortest Path হ্যাঁ (unweighted graph-এ) না
Memory বেশি লাগে কম লাগে
ব্যবহার Shortest path, Level order Connected components, Cycle detection

সমস্যা ১: Binary Tree-র Level Order Traversal (BFS)

সমস্যা: একটি Binary Tree-র সমস্ত node-কে level অনুযায়ী (উপর থেকে নিচে, বাম থেকে ডানে) একটি 2D list-এ সাজিয়ে দিন।

উদাহরণ:

Input Tree:
        3
       / \
      9  20
        /  \
       15   7

Output: [[3], [9, 20], [15, 7]]

চিন্তার ধাপ:

  • BFS ব্যবহার করব কারণ এটি level by level traverse করে
  • Queue-তে root রেখে শুরু করব
  • প্রতিটি level-এ Queue-তে কতটি node আছে সেটা গণনা করব
  • সেই সংখ্যা অনুযায়ী node বের করে তাদের children Queue-তে যোগ করব

Java সমাধান:

import java.util.*;

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) return result;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        while (!queue.isEmpty()) {
            int levelSize = queue.size();
            List<Integer> currentLevel = new ArrayList<>();

            for (int i = 0; i < levelSize; i++) {
                TreeNode node = queue.poll();
                currentLevel.add(node.val);
                if (node.left != null)  queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
            result.add(currentLevel);
        }
        return result;
    }
}

জটিলতা: Time O(n), Space O(n)

মূল কৌশল: queue.size() প্রতিটি iteration-এর শুরুতে store করতে হবে — এটি সেই level-এর মোট node সংখ্যা নির্ধারণ করে।


সমস্যা ২: Binary Tree-র Maximum Depth (DFS)

সমস্যা: একটি Binary Tree-র সর্বোচ্চ গভীরতা (maximum depth) বের করুন।

উদাহরণ:

Input:
    3
   / \
  9  20
    /  \
   15   7

Output: 3

চিন্তার ধাপ:

  • DFS ব্যবহার করব — recursively গভীরে যাব
  • প্রতিটি node-এ বাম ও ডান subtree-র depth বের করব
  • সর্বোচ্চটি নিয়ে ১ যোগ করে return করব

Java সমাধান:

class Solution {
    // Recursive DFS
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;

        int leftDepth  = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);

        return Math.max(leftDepth, rightDepth) + 1;
    }

    // Iterative BFS alternative
    public int maxDepthBFS(TreeNode root) {
        if (root == null) return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int depth = 0;

        while (!queue.isEmpty()) {
            depth++;
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                TreeNode node = queue.poll();
                if (node.left  != null) queue.offer(node.left);
                if (node.right != null) queue.offer(node.right);
            }
        }
        return depth;
    }
}

জটিলতা: Time O(n), Space O(h) যেখানে h = tree height

মূল কৌশল: Recursive DFS-এ max(left, right) + 1 pattern — null node-এ 0 return করা।


সমস্যা ৩: Flood Fill অ্যালগরিদম (DFS on Grid)

সমস্যা: একটি 2D image grid-এ starting pixel (sr, sc) থেকে একই রঙের সংলগ্ন সব pixel-কে নতুন color দিয়ে fill করুন।

উদাহরণ:

Input:  [[1,1,1],[1,1,0],[1,0,1]], sr=1, sc=1, color=2
Output: [[2,2,2],[2,2,0],[2,0,1]]

চিন্তার ধাপ:

  • Starting pixel-এর original color মনে রেখে DFS শুরু করব
  • প্রতিটি step-এ 4 দিকে check করব (উপর, নিচ, বাম, ডান)
  • Edge case: শুরুর pixel ইতিমধ্যে নতুন color হলে কিছু করতে হবে না

Java সমাধান:

class Solution {
    private int[][] directions = {{-1,0},{1,0},{0,-1},{0,1}};

    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        int originalColor = image[sr][sc];
        if (originalColor == color) return image;
        dfs(image, sr, sc, originalColor, color);
        return image;
    }

    private void dfs(int[][] image, int row, int col,
                     int originalColor, int newColor) {
        if (row < 0 || row >= image.length ||
            col < 0 || col >= image[0].length ||
            image[row][col] != originalColor) {
            return;
        }

        image[row][col] = newColor; // fill করো

        for (int[] dir : directions) {
            dfs(image, row + dir[0], col + dir[1], originalColor, newColor);
        }
    }
}

জটিলতা: Time O(m×n), Space O(m×n)

মূল কৌশল: directions array দিয়ে 4 দিক handle করুন। visited tracking-এর জন্য color পরিবর্তনই যথেষ্ট — আলাদা visited array দরকার নেই।


সমস্যা ৪: Graph-এ Connected Components গোনা (DFS)

সমস্যা: n টি node এবং edges array দেওয়া আছে। Undirected graph-এ মোট কতটি connected component আছে বের করুন।

উদাহরণ:

Input: n = 5, edges = [[0,1],[1,2],[3,4]]
Output: 2
(Component 1: 0-1-2,  Component 2: 3-4)

চিন্তার ধাপ:

  • Adjacency List তৈরি করব
  • প্রতিটি unvisited node থেকে DFS শুরু করব
  • প্রতিটি নতুন DFS শুরু = একটি নতুন component

Java সমাধান:

import java.util.*;

class Solution {
    public int countComponents(int n, int[][] edges) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < n; i++) graph.add(new ArrayList<>());

        for (int[] edge : edges) {
            graph.get(edge[0]).add(edge[1]);
            graph.get(edge[1]).add(edge[0]);
        }

        boolean[] visited = new boolean[n];
        int components = 0;

        for (int i = 0; i < n; i++) {
            if (!visited[i]) {
                components++;
                dfs(graph, visited, i);
            }
        }
        return components;
    }

    private void dfs(List<List<Integer>> graph,
                     boolean[] visited, int node) {
        visited[node] = true;
        for (int neighbor : graph.get(node)) {
            if (!visited[neighbor]) dfs(graph, visited, neighbor);
        }
    }
}

জটিলতা: Time O(V+E), Space O(V+E)

মূল কৌশল: visited[] array দিয়ে track করা। প্রতিটি unvisited node-এ নতুন DFS শুরু = একটি নতুন component আবিষ্কার।


সমস্যা ৫: Binary Tree Symmetric কিনা যাচাই (DFS)

সমস্যা: একটি Binary Tree তার নিজের mirror image কিনা নির্ধারণ করুন।

উদাহরণ:

Symmetric → true:       Not Symmetric → false:
        1                       1
       / \                     / \
      2   2                   2   2
     / \ / \                   \   \
    3  4 4  3                   3   3

চিন্তার ধাপ:

  • Root-এর বাম ও ডান subtree mirror কিনা check করব
  • Mirror মানে: বাম-এর বাম = ডান-এর ডান, বাম-এর ডান = ডান-এর বাম

Java সমাধান:

import java.util.*;

class Solution {
    // Recursive DFS
    public boolean isSymmetric(TreeNode root) {
        if (root == null) return true;
        return isMirror(root.left, root.right);
    }

    private boolean isMirror(TreeNode left, TreeNode right) {
        if (left == null && right == null) return true;
        if (left == null || right == null) return false;

        return (left.val == right.val)
            && isMirror(left.left,  right.right)
            && isMirror(left.right, right.left);
    }

    // Iterative BFS approach
    public boolean isSymmetricBFS(TreeNode root) {
        if (root == null) return true;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root.left);
        queue.offer(root.right);

        while (!queue.isEmpty()) {
            TreeNode left  = queue.poll();
            TreeNode right = queue.poll();

            if (left == null && right == null) continue;
            if (left == null || right == null) return false;
            if (left.val != right.val) return false;

            // Mirror জোড়া যোগ করো
            queue.offer(left.left);  queue.offer(right.right);
            queue.offer(left.right); queue.offer(right.left);
        }
        return true;
    }
}

জটিলতা: Time O(n), Space O(n)

মূল কৌশল: জোড়ায় জোড়ায় compare — (left.left, right.right) এবং (left.right, right.left) এই cross-compare pattern মনে রাখুন।


সব সমস্যার সারসংক্ষেপ

সমস্যা অ্যালগরিদম Time Space মূল কৌশল
Level Order Traversal BFS O(n) O(n) Queue + level size store করা
Maximum Depth DFS Recursive O(n) O(h) max(left, right) + 1
Flood Fill DFS on Grid O(m×n) O(m×n) directions array, color = visited
Connected Components DFS + visited[] O(V+E) O(V+E) unvisited = new component
Symmetric Tree DFS Mirror check O(n) O(n) cross-compare জোড়ায়

ইন্টারভিউ টিপস

BFS কখন ব্যবহার করবেন: Shortest path, Level order processing, Minimum steps/distance।

DFS কখন ব্যবহার করবেন: Backtracking, Path finding, Cycle detection, Tree height/depth।

সাধারণ Template:

// DFS Template (Graph)
void dfs(int node, boolean[] visited, List<List<Integer>> graph) {
    visited[node] = true;
    for (int neighbor : graph.get(node)) {
        if (!visited[neighbor]) dfs(neighbor, visited, graph);
    }
}

// BFS Template (Graph)
void bfs(int start, List<List<Integer>> graph) {
    Queue<Integer> queue = new LinkedList<>();
    boolean[] visited = new boolean[graph.size()];
    queue.offer(start);
    visited[start] = true;
    while (!queue.isEmpty()) {
        int node = queue.poll();
        for (int neighbor : graph.get(node)) {
            if (!visited[neighbor]) {
                visited[neighbor] = true;
                queue.offer(neighbor);
            }
        }
    }
}
Share