private V findPath(V root) {
    Set<V> used = new HashSet<>();
    Queue<V> q = new ArrayDeque<>();

    // Expand graph back from its contracted state
    path.clear();
    contracted.clear();

    graph.vertexSet().forEach(vertex -> contracted.put(vertex, vertex));

    used.add(root);
    q.add(root);

    while (!q.isEmpty()) {
      V v = q.remove();

      for (E e : graph.edgesOf(v)) {
        V to = graph.getEdgeSource(e);

        if (to.equals(v)) {
          to = graph.getEdgeTarget(e);
        }

        if ((contracted.get(v).equals(contracted.get(to))) || to.equals(match.get(v))) {
          continue;
        }

        // Check whether we've hit a 'blossom'
        if ((to.equals(root)) || ((match.containsKey(to)) && (path.containsKey(match.get(to))))) {
          V stem = lowestCommonAncestor(v, to);

          Set<V> blossom = new HashSet<>();

          markPath(v, to, stem, blossom);
          markPath(to, v, stem, blossom);

          graph
              .vertexSet()
              .stream()
              .filter(i -> contracted.containsKey(i) && blossom.contains(contracted.get(i)))
              .forEach(
                  i -> {
                    contracted.put(i, stem);
                    if (!used.contains(i)) {
                      used.add(i);
                      q.add(i);
                    }
                  });

          // Check whether we've had hit a loop (of even length (!) presumably)
        } else if (!path.containsKey(to)) {
          path.put(to, v);

          if (!match.containsKey(to)) {
            return to;
          }

          to = match.get(to);

          used.add(to);
          q.add(to);
        }
      }
    }
    return null;
  }