GRAPH-SEARCH (pg. 83) is an adaptation of TREE-SEARCH to handle cases in which multiple paths can lead to the same state.  Here we create a closed list to explicitly store all the nodes that we have expanded and an open list to store all nodes on the fringe that are currently unexpanded.

When a previously visited state is removed from the fringe it will be tested in the if STATE[node] line and it will not be re-expanded.  Note that this check is done after the state is removed and tested for the goal condition.  It does not prevent repetitive states from being reinserted in the fringe.  Also note that the algorithm will return the first goal solution (even when there are multiple paths to the same goal state of different costs) so can only be optimal if the first path encountered to a goal state is the cheapest.

To think about: the goal-test is performed before checking whether the state has been seen before.  What ramifications does that have in search performance?  Does it have any benefits?