Jc-alt logo
jc
data structures and algorithms

LeetCode: Trees I BFS

LeetCode: Trees I BFS
11 min read
#data structures and algorithms

Tree Intro

What is a Tree

Trees are hierarchical data structures representing relationships between entities, often in a parent-child format.

102. Binary Tree Level Order Traversal ::2:: - Medium

Topics: Tree, Breadth First Search, Binary Tree

Intro

Given the root of a binary tree, return the level order traversal of its nodes' values. (i.e., from left to right, level by level).

Example InputOutput
root = [3,9,20,null,null,15,7][[3],[9,20],[15,7]]
root = [1][[1]]
root = [][]

Constraints:

The number of nodes in the root tree is in the range [1, 2000].

-1000 ≤ Node.val ≤ 1000

Abstraction

Traverse a tree and return list of nodes grouped by level.

Space & Time Complexity

SolutionTime ComplexitySpace ComplexityTime RemarkSpace Remark
BugError

Brute Force: iterative

AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

Find the Bug:

Solution 1: BFS Iterative - Tree/DFS Pre order Traversal

    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        
        # Empty check
        if not root:
            return []
        
        groups = []
        queue = deque([root])  # start with root
        
        while queue:
            # grab size of current level
            size = len(queue)
            level = []
            
            # process each node in this level
            for _ in range(size):
                node = queue.popleft()
                level.append(node.val)
                
                # enqueue children for next level
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
            
            groups.append(level)
        
        return groups
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

Solution 2: DFS Pre Order Recursive - Tree/DFS Pre order Traversal

    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        
        # Note:
        # DFS pre order: root -> left -> right
        # 1. Process root -> :
        #    append root to corresponding depth group
        # 2. Process -> left -> right :
        # Results: nodes grouped by depth level
        
        # depth level groups
        groups = []
        
        def dfs(node, depth):

            # Empty check
            if not node:
                return
            
            # Process root -> :
            # add group if new depth reached
            if len(groups) == depth:
                groups.append([])
            
            # Process root -> : 
            # add node to corresponding depth group
            groups[depth].append(node.val)
            
            # Process -> left -> right : update depth
            dfs(node.left, depth + 1)
            dfs(node.right, depth + 1)
        
        dfs(root, 0)

        # overall: time complexity
        # overall: space complexity
        return groups
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

199. Binary Tree Right Side View ::2:: - Medium

Topics: Tree, Breadth First Search, Binary Tree

Intro

Given the root of a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom.

Example InputOutput
root = [1,2,3,null,5,null,4][1,3,4]
root = [1,2,3,4,null,null,null,5][1,3,4,5]
root = [1,null,3][1,3]
root = [][]

Constraints:

The number of nodes in the root tree is in the range [1, 100].

-100 ≤ Node.val ≤ 100

Abstraction

Given a tree, if you were to stand on the right side, return all nodes that you would have a direct line of sight to (not hidden by right-er nodes)

Space & Time Complexity

SolutionTime ComplexitySpace ComplexityTime RemarkSpace Remark
BugError

Brute Force: iterative

AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

Find the Bug:

Solution 1: BFS Pre Order Iterative Grab Last Element Per Level - Tree/DFS Pre order Traversal

    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        
        # Note:
        # BFS pre order: process level : root -> left -> right
        # 1. For each level
        # 2. Process root -> :
        #    grab length of level, if root is last element in group, add to res
        # 3. Process -> left -> right :
        # Result: right most element of each level added
        
        # Empty check
        if not root:
            return []
        
        # right most elements of each level
        result = []
        
        # iterative queue
        queue = deque([root])
        
        while queue:

            # process entire level
            level_size = len(queue)
            for i in range(level_size):

                # Process root -> : 
                node = queue.popleft()

                # Process root -> : 
                # if this is last element in level, add to res
                if i == level_size - 1:
                    result.append(node.val)

                # Process -> left -> right : 
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)
        
        # overall: time complexity
        # overall: space complexity
        return result
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

Solution 2: DFS Pre Order Recursive Right Subtree Search With New Depth Trigger - Tree/DFS Pre order Traversal

    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        
        # Note:
        # DFS pre order: root -> right -> left
        # 1. Process root -> : 
        # 2. Process -> right -> left
        #    we are exploring right subtree first,
        #    first time we hit a new depth, we must be at right most node,
        #    add node to res
        # Result: right most element of each level added

        # right most elements of each level
        result = []
        
        def dfs(node, depth):

            # Empty check
            if not node:
                return

            # Process root -> :
            # if hit new depth, must be at right most node
            if depth == len(result):
                result.append(node.val)

            # Process -> right -> left
            dfs(node.right, depth + 1)
            dfs(node.left, depth + 1)
        
        # recursive call on root
        dfs(root, 0)

        # overall: time complexity
        # overall: space complexity
        return result
AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

103. Binary Tree Zigzag Level Order Traversal ::1:: - Medium

Topics: Tree, Breadth First Search, Binary Tree

Intro

Given the root of a binary tree, return the zigzag level order traversal of its nodes' values. (i.e., from left to right, then right to left for the next level and alternate between).

Example InputOutput
root = [3,9,20,null,null,15,7][[3],[20,9],[15,7]]
root = [1][[1]]
root = [][]

Constraints:

The number of nodes in the tree is in the range [0, 2000].

-100 ≤ Node.val ≤ 100

Abstraction

something!

Space & Time Complexity

SolutionTime ComplexitySpace ComplexityTime RemarkSpace Remark
BugError

Brute Force:

AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

Find the Bug:

Solution 1: [BFS] BFS And BFS Pruning Optimization - Tree/DFS Post Order Recursive Two Sided Bottom Up

    def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        
        # BFS Level Order + Zigzag
        
        # Idea:
        #   - Standard BFS level traversal.
        #   - Use deque for current level:
        #       left->right  : append()
        #       right->left  : appendleft()
        #   - Toggle direction after each level.
        
        # Complexity:
        #   - Time: O(n)
        #   - Space: O(n)

        if not root:
            return []

        queue = deque([root])
        res = []
        left_to_right = True

        while queue:

            level_size = len(queue)
            level = deque()   # allows O(1) insertion both ends

            for _ in range(level_size):
                node = queue.popleft()

                # Zigzag insertion
                if left_to_right:
                    level.append(node.val)
                else:
                    level.appendleft(node.val)

                # Normal BFS expansion
                if node.left:
                    queue.append(node.left)
                if node.right:
                    queue.append(node.right)

            res.append(list(level))

            # flip direction
            left_to_right = not left_to_right

        return res

1765. Map of Highest Peak ::1:: - Medium

Topics: Array, Breadth First Search, Matrix

Intro

You are given an integer matrix isWater of size m x n that represents a map of land and water cells. If isWater[i][j] == 0, cell (i, j) is a land cell. If isWater[i][j] == 1, cell (i, j) is a water cell. You must assign each cell a height in a way that follows these rules: The height of each cell must be non-negative. If the cell is a water cell, its height must be 0. Any two adjacent cells must have an absolute height difference of at most 1. A cell is adjacent to another cell if the former is directly north, east, south, or west of the latter (i.e., their sides are touching). Find an assignment of heights such that the maximum height in the matrix is maximized. Return an integer matrix height of size m x n where height[i][j] is cell (i, j)'s height. If there are multiple solutions, return any of them.

Example InputOutput
isWater = [[0,1],[0,0]][[1,0],[2,1]]
isWater = [[0,0,1],[1,0,0],[0,0,0]][[1,1,0],[0,1,1],[1,2,2]]

Constraints:

m == isWater.length

n == isWater[i].length

1 ≤ m, n ≤ 1000

isWater[i][j] is 0 or 1

There is at least one water cell

Abstraction

something!

Space & Time Complexity

SolutionTime ComplexitySpace ComplexityTime RemarkSpace Remark
BugError

Brute Force:

AspectTime ComplexitySpace ComplexityTime RemarksSpace Remarks

Find the Bug:

Solution 1: [DFS] DFS And BFS Pruning Optimization - Tree/DFS Post Order Recursive Two Sided Bottom Up

    def highestPeak(self, isWater: List[List[int]]) -> List[List[int]]:
        # Dimensions
        m, n = len(isWater), len(isWater[0])

        # Initialize height grid with -1 (unvisited)
        height = [[-1] * n for _ in range(m)]

        # BFS queue: start with all water cells
        queue = deque()
        for r in range(m):
            for c in range(n):
                if isWater[r][c] == 1:
                    height[r][c] = 0
                    queue.append((r, c))

        # Directions: up, down, left, right
        directions = [(1,0), (-1,0), (0,1), (0,-1)]

        # BFS: propagate heights
        while queue:
            r, c = queue.popleft()

            for dr, dc in directions:
                nr, nc = r + dr, c + dc

                # Early pruning: skip out-of-bounds or already visited
                if 0 <= nr < m and 0 <= nc < n and height[nr][nc] == -1:
                    height[nr][nc] = height[r][c] + 1
                    queue.append((nr, nc))

        return height