Traverse the binary tree twice. We convert the tree into a graph and then traverse the graph twice. The first traversal creates parent nodes, and then the second BFS goes through K layers to return all neighbors with distance k for each node.

class Solution:
    def distanceK(self, root: TreeNode, target: TreeNode, k: int) -> List[int]:
        def dfs(node, parent):
            if not node:
                return
 
            node.parent = parent
            dfs(node.left, node)
            dfs(node.right, node)
 
        dfs(root, None)
        queue = deque([target])
        seen = {target}
        distance = 0
 
        while queue and distance < k:
            curr_length = len(queue)
            for _ in range(curr_length):
                node = queue.popleft()
                for neighbor in [node.left, node.right, node.parent]:
                    if neighbor and neighbor not in seen:
                        seen.add(neighbor)
                        queue.append(neighbor)
            distance += 1
        
        return [node.val for node in queue]

Without mutation, create a graph structure to store the parents for each node.

class Solution:
    def distanceK(self, root: Optional[TreeNode], target: TreeNode, k: int) -> List[int]:
        # Step 1: Build adjacency list
        graph = defaultdict(list)
 
        def dfs(node, parent):
            if not node:
                return
            if parent:
                graph[node].append(parent)
                graph[parent].append(node)
            dfs(node.left, node)
            dfs(node.right, node)
 
        dfs(root, None)
 
        # Step 2: BFS from target
        queue = deque([target])
        seen = {target}
        distance = 0
 
        while queue and distance < k:
            for _ in range(len(queue)):
                node = queue.popleft()
                for neighbor in graph[node]:
                    if neighbor not in seen:
                        seen.add(neighbor)
                        queue.append(neighbor)
            distance += 1
 
        return [node.val for node in queue]