# Check if an Undirected Graph is a Tree or Not Using DFS

In this article, we will check if an undirected graph is a tree or not. A graph is an abstract Data Structure that consists of a set of vertices (nodes) and a set of pairs containing these vertices. A pair represents an edge (line/ connection) between two vertices. If the graph is undirected, the pairs are unordered, which means that both vertices are connected. In a directed graph, pairs are ordered; the edge (v1, v2) indicates a connection between v1 and v2, but not the other way around.

A tree, on the other hand, is a type of undirected graph that has the following two properties:

1. There are no cycles; every node has only one parent.
2. The graph is connected, i.e., we can go from every vertex to any other vertex in the graph.

Simply put, a tree is a connected acyclic undirected graph.

## How to determine if an Undirected Graph is a Tree or Not Using DFS

So, to determine if an undirected graph is a tree or not, we have to check for the above two properties.  We will use the Depth First Search (DFS) approach. The steps of the solution are as follows:

Initially, in our main function, we create an array (a dictionary or hash table) to keep track of the visited nodes. In the beginning, it is false for every node.

Then, we call a recursive function to detect whether there is a cycle in the graph or not. Initially, we pass a node of the graph as the current vertex (`curr_vertex`), visited variable, `NULL` as the parent since this is the first node to be passed.
[adinserter block=”2″] Now, the recursive function marks the `curr_vertex` as visited. Then, it iterates over the adjacent nodes of the current vertex.

• If the adjacent node is unvisited, then it calls itself with the adjacent vertex as the `curr_vertex`, visited variable, and the current vertex as the parent. If this call returns false (indicating no cycle), then the function continues. Otherwise, the function returns true (indicating a loop) as well.
• However, if the adjacent node is visited but similar to the parent, nothing happens. The function moves to the next neighboring vertex if there is any. In the above figure, first, node 5 is visited, and then the recursive function gets called with node 1 as the current vertex and 5 as the parent. Here, 5 is adjacent to 1, and it is already visited. However, it does not imply a cycle as it is the parent.

• Otherwise, there is a cycle in the graph, and the function returns true. First, node 2 is visited, then node 5, and finally, the recursive function gets called with node 1 as the current vertex and 5 as the parent. Here, 2 is already visited and is not a parent of 1 specifying a cycle in the graph.

• Finally, if we successfully iterate over all adjacent nodes, we return false as no cycle got detected.

If a cycle gets detected in a graph, we return false from the main function as there is no need to check further about the graph’s connectivity. However, if there is no cycle, we call another method with the visited variable (it has now updated data from the recursive function) to see if the graph is connected. If there are any unvisited vertices, then the graph is not connected, and thus, not a tree. However, if all the vertices are visited, implying a connected graph, we will return true as there isn’t a cycle and the graph is connected.

## Implementation

The implementation of the above solution is given below:

```class Graph:
def __init__(self):
self.vertices = []
self.graph = {}

#add an isolated vertex in the graph
if vertex in self.vertices:
raise KeyError(f"Vertex {vertex} already exists in the graph")
else:
self.vertices.append(vertex)
self.graph[vertex] = [] #add isolated vertex in th graph

#add an edge to the graph
def add_edge(self, edge): #edge is passed as an tuple (v1, v2)
v1 = edge
v2 = edge

if self.graph.get(v1, None): #v1 already exists as a vertex in the graph
self.graph[v1].append(v2)
else:
self.graph[v1] = [v2]
self.vertices.append(v1)

if self.graph.get(v2, None): #v2 already exists as a vertex in the graph
self.graph[v2].append(v1)
else:
self.graph[v2] = [v1]
self.vertices.append(v2)

#display all the edges in the graph
def display_edges(self):
print("---------Graph---------")
for vertex1 in self.graph.keys():
if len(self.graph[vertex1]) == 0:
print("Isolated vertex", vertex1)
for vertex2 in self.graph[vertex1]:
print(f"edge {vertex1}-->{vertex2}")

def is_cycle(self, curr_vertex, visited, parent):
#mark the current node as visited
visited[curr_vertex] = True

return True
continue
else: #adjacent node is visited and not a parent, indicating a cycle
return True

return False

#check if the graph is connected or not using the visited nodes info
def is_connected(self, visited):
for vertex in self.vertices:
if not visited[vertex]:
return False
return True

#check if the graph is a tree or not
def is_tree(self):
#create a dictionary to store information of visited node
visited ={}
for node in self.vertices:
visited[node] = False

#check for a cycle by passing a vertex from the graph
cycle = self.is_cycle(self.vertices, visited, None)
if cycle:
return False

#check if the graph is connected
return self.is_connected(visited)
```

Now call the function:

```graph_1 = Graph()
tree = graph_1.is_tree()
if tree:
print("The given graph is a tree")
else:
print("The given graph is not a tree")
``` Output

`The given graph is a tree`

Call the function using different parameters:

```graph_2 = Graph()
tree = graph_2.is_tree()
if tree:
print("The given graph is a tree")
else:
print("The given graph is not a tree")
``` Output

`The given graph is not a tree`
```graph_3 = Graph() `The given graph is not a tree`