// recursive DFS to detect cycles
    public boolean hasCycle() {
      // firstly check if this node is a visited clean node
      // this pre-check will avoid repeat search and save time
      if (cleanNode) {
        return false;
      }

      // if our curr node is a visited node, then it means we got a back edge in current path, so
      // there is a cycle
      if (visited) {
        return true;
      }

      // mark visited in current search
      visited = true;

      // check its neighbor nodes
      for (Node node : nodes) {
        // recursivly search each next node
        if (node.hasCycle()) {
          return true;
        }
      }

      // reset node as unvisited to avoid the impact on next search
      visited = false;
      // if there is no cycle in this node, we will set it as cleanNode
      cleanNode = true;
      return false;
    }
    private boolean hasCycle(StringBuilder sb) {
      // we need put the root node first. To be convenient we want start DFS from any node, not the
      // node
      // without prerequisites or with most prerequisites. So we will build the string backward.
      // We will start build the string after we reach the bottom (the root node). Therefore, in DFS
      // we should use "depends on" relation, where we put root node to the bottom

      // cleanNode check first, so we can set and reset "visited" freely
      if (cleanNode) return false;
      if (visited) return true;
      visited = true;

      for (Node node : nodes) {
        if (node.hasCycle(sb)) return true;
      }

      cleanNode = true;
      visited = false;
      sb.append(c);
      return false;
    }
  public boolean canFinish(int numCourses, int[][] prerequisites) {
    // create a node list, so we can visit node easier
    Node[] nodes = new Node[numCourses];

    // initialize each node
    for (int i = 0; i < nodes.length; i++) {
      nodes[i] = new Node();
    }

    // update neighbors of each node
    for (int[] pair : prerequisites) {
      nodes[pair[0]].addNode(nodes[pair[1]]);
    }

    // scan array to check if there is any cycle
    for (Node node : nodes) {
      if (node.hasCycle()) return false;
    }

    return true;
  }