DFS from any root to compute subtree sizes for all nodes.
Store them in an array for quick lookup. This is a standard tree traversal.
Start at the root.
While the current node has a child with size[u] > n/2, move to . When no such child exists, return as the centroid. The first phase takes time for the tree traversal. The second phase visits at most nodes in the worst case (imagine a line graph where you walk the entire length), so total time is .
Space complexity is for the data structures used.