# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def findClosestLeaf(self, root: TreeNode, k: int) -> int:
    """
    for Binary tree, if we need to check neighbour node which is not parent- children relationship, we probably need to convert binary tree to undirected graph. 
    In order to ensure leaf node is closest to target node k, use BFS  
    """
        # time O(N) space O(N)
        # Build graph, annotate leaves
        leaves = set()
        graph = collections.defaultdict(list)
        def preorder(node):
            if not node:
                return 
            # mark leaf node
            if not node.left and not node.right:
                leaves.add(node.val)
            if node.left:
                # undirected graph 
                graph[node.val].append(node.left.val)
                graph[node.left.val].append(node.val)
                preorder(node.left)
            if node.right: 
                graph[node.val].append(node.right.val)
                graph[node.right.val].append(node.val)
                preorder(node.right)
        preorder(root)
        # Graph traverse -BFS
        q = [k]
        while q:
            next_q = [] 
            # loop current q, update it's neighbour nodes.
            for node in q:
                # exit condition: find a leaf node
                if node in leaves:
                    return node
                next_q += graph.pop(node,[])# be careful to cases dictionary.pop(key) returns None
            q = next_q 

Note, here is using concept of queue by creating a list and loop through current list and keep updating list after a loop finished. But deque() is not used. If deque is applied, runtime will be much slower and also need to notice None type is not iterable, so we need to use [] to replace None node.