Here's the tree DFS template with parent tracking:
function dfs(u, parent):
process(u)
for each neighbor v of u:
if v != parent:
dfs(v, u)
When you call dfs(v, u), you pass the current vertex as the child's parent. The child knows not to recurse back to where it came from. Start with dfs(root, -1) where means no parent.
This single check eliminates the need for a visited array on trees. You save memory and simplify your code. Every tree problem uses this pattern.