input: Write a python function to find the shortest path in a graph using breadth-first search.
  assert shortest_path({'A': ['B', 'C', 'E'], 'B': ['A', 'D', 'E'], 'C': ['A', 'F', 'G'], 'D': ['B'], 'E': ['A', 'B', 'D'], 'F': ['C'], 'G': ['C']}, 'A', 'D') == ['A', 'B', 'D']
  assert shortest_path({'A': ['B', 'C'], 'B': ['A', 'D'], 'C': ['A', 'D'], 'D': ['B', 'C', 'E'], 'E': ['D']}, 'A', 'E') == ['A', 'B', 'D', 'E']
  assert shortest_path({'A': ['B'], 'B': ['A', 'C'], 'C': ['B']}, 'A', 'C') == ['A', 'B', 'C']
output: def shortest_path(graph, start, goal):
      from collections import deque
      
      queue = deque([[start]])
      visited = set()
      
      while queue:
          path = queue.popleft()
          node = path[-1]
          
          if node in visited:
              continue
          
          for neighbor in graph.get(node, []):
              new_path = list(path)
              new_path.append(neighbor)
              queue.append(new_path)
              
              if neighbor == goal:
                  return new_path
          
          visited.add(node)
      
      return None