The depth of the n-ary tree is less than or equal to 1000.
The total number of nodes is between [1, 104].
Solutions
Solution 1: DFS (Post-order Diameter)
Thinking
The diameter of an \(N\)-ary tree is the longest distance between two nodes. The tree can have thousands of nodes, so careless double walks waste work. The diameter through a node is the sum of its two deepest child heights.
A post-order DFS gathers child heights, updates a global answer with the sum of the two largest, and returns the node's own height. One traversal examines every candidate without building another graph.
Traverse each node in post-order, record the two deepest child heights \(m_1\) and \(m_2\), and update the diameter with \(m_1+m_2\). The height of a subtree is one plus the deepest child height.
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the number of nodes.
"""# Definition for a Node.class Node: def __init__(self, val=None, children=None): self.val = val self.children = children if children is not None else []"""classSolution:defdiameter(self,root:'Node')->int:""" :type root: 'Node' :rtype: int """defdfs(root):ifrootisNone:return0nonlocalansm1=m2=0forchildinroot.children:t=dfs(child)ift>m1:m2,m1=m1,telift>m2:m2=tans=max(ans,m1+m2)return1+m1ans=0dfs(root)returnans
/*// Definition for a Node.class Node { public int val; public List<Node> children; public Node() { children = new ArrayList<Node>(); } public Node(int _val) { val = _val; children = new ArrayList<Node>(); } public Node(int _val,ArrayList<Node> _children) { val = _val; children = _children; }};*/classSolution{privateintans;publicintdiameter(Noderoot){ans=0;dfs(root);returnans;}privateintdfs(Noderoot){if(root==null){return0;}intm1=0,m2=0;for(Nodechild:root.children){intt=dfs(child);if(t>m1){m2=m1;m1=t;}elseif(t>m2){m2=t;}}ans=Math.max(ans,m1+m2);return1+m1;}}
/** * Definition for a Node. * type Node struct { * Val int * Children []*Node * } */funcdiameter(root*Node)int{ans:=0vardfsfunc(root*Node)intdfs=func(root*Node)int{ifroot==nil{return0}m1,m2:=0,0for_,child:=rangeroot.Children{t:=dfs(child)ift>m1{m2,m1=m1,t}elseift>m2{m2=t}}ans=max(ans,m1+m2)return1+m1}dfs(root)returnans}
Solution 2: Build Graph + Two DFS
Thinking
Solution 1 folds the diameter into a tree DP. If we only have adjacency, or want the classic graph algorithm, that coupling is inconvenient. Build an undirected graph, walk from an arbitrary node to a farthest node, then walk again from there; the second distance is the diameter. The two DFS passes match the tree DP asymptotically and differ only in implementation.
Convert the \(N\)-ary tree into an undirected graph. DFS from an arbitrary node to find the farthest node, then DFS again from that node. The farthest distance of the second search is the diameter of the tree.
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the number of nodes.
"""# Definition for a Node.class Node: def __init__(self, val=None, children=None): self.val = val self.children = children if children is not None else []"""classSolution:defdiameter(self,root:'Node')->int:""" :type root: 'Node' :rtype: int """defbuild(root):nonlocaldifrootisNone:returnforchildinroot.children:d[root].add(child)d[child].add(root)build(child)defdfs(u,t):nonlocalans,vis,d,nextifuinvis:returnvis.add(u)forvind[u]:dfs(v,t+1)ifans<t:ans=tnext=ud=defaultdict(set)vis=set()build(root)ans=0next=Nonedfs(root,0)vis.clear()dfs(next,0)returnans
/*// Definition for a Node.class Node { public int val; public List<Node> children; public Node() { children = new ArrayList<Node>(); } public Node(int _val) { val = _val; children = new ArrayList<Node>(); } public Node(int _val,ArrayList<Node> _children) { val = _val; children = _children; }};*/classSolution{privateMap<Node,Set<Node>>g;privateSet<Node>vis;privateNodenext;privateintans;publicintdiameter(Noderoot){g=newHashMap<>();build(root);vis=newHashSet<>();next=root;ans=0;dfs(next,0);vis.clear();dfs(next,0);returnans;}privatevoiddfs(Nodeu,intt){if(vis.contains(u)){return;}vis.add(u);if(t>ans){ans=t;next=u;}if(g.containsKey(u)){for(Nodev:g.get(u)){dfs(v,t+1);}}}privatevoidbuild(Noderoot){if(root==null){return;}for(Nodechild:root.children){g.computeIfAbsent(root,k->newHashSet<>()).add(child);g.computeIfAbsent(child,k->newHashSet<>()).add(root);build(child);}}}
/** * Definition for a Node. * type Node struct { * Val int * Children []*Node * } */funcdiameter(root*Node)int{g:=make(map[*Node][]*Node)vis:=make(map[*Node]bool)next:=rootans:=0varbuildfunc(root*Node)build=func(root*Node){ifroot==nil{return}for_,child:=rangeroot.Children{g[root]=append(g[root],child)g[child]=append(g[child],root)build(child)}}build(root)vardfsfunc(u*Node,tint)dfs=func(u*Node,tint){ifvis[u]{return}vis[u]=trueift>ans{ans=tnext=u}ifvs,ok:=g[u];ok{for_,v:=rangevs{dfs(v,t+1)}}}dfs(next,0)vis=make(map[*Node]bool)dfs(next,0)returnans}
Solution 3: Explicit Stack (Post-order Diameter)
Thinking
The diameter of an \(N\)-ary tree is the longest path between any two nodes. The depth can reach \(1000\), so a post-order recursion along a single-child chain uses a call depth equal to the node count and overflows Python once the chain reaches length \(1000\). Each node only needs the finished heights of its children: the sum of the two largest updates the diameter, and the node's own height is one plus the deepest child. A stack therefore stores a node together with a state. State \(0\) pushes the exit frame and then the children, so the children finish first. State \(1\) reads those heights, updates the diameter, and records the current height.
Start at the root and walk in post-order. The stack holds a node and a state. In state \(0\), push the same node with state \(1\), then push every non-null child. In state \(1\), every child height is already stored. Take the two largest heights \(m_1\) and \(m_2\), update the diameter with \(m_1+m_2\), and record the current height as \(m_1+1\). An empty root has diameter \(0\).
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the number of nodes.
"""# Definition for a Node.class Node: def __init__(self, val=None, children=None): self.val = val self.children = children if children is not None else []"""classSolution:defdiameter(self,root:'Node')->int:""" :type root: 'Node' :rtype: int """ifrootisNone:return0ans=0height={}stk=[(root,0)]whilestk:node,state=stk.pop()ifstate==0:stk.append((node,1))forchildinreversed(node.children):ifchildisnotNone:stk.append((child,0))else:m1=m2=0forchildinnode.children:t=height.get(child,0)ift>m1:m2,m1=m1,telift>m2:m2=tans=max(ans,m1+m2)height[node]=m1+1returnans
/*// Definition for a Node.class Node { public int val; public List<Node> children; public Node() { children = new ArrayList<Node>(); } public Node(int _val) { val = _val; children = new ArrayList<Node>(); } public Node(int _val,ArrayList<Node> _children) { val = _val; children = _children; }};*/classSolution{publicintdiameter(Noderoot){if(root==null){return0;}intans=0;Map<Node,Integer>height=newHashMap<>();Deque<Node>nodes=newArrayDeque<>();Deque<Integer>states=newArrayDeque<>();nodes.push(root);states.push(0);while(!nodes.isEmpty()){Nodenode=nodes.pop();intstate=states.pop();if(state==0){nodes.push(node);states.push(1);List<Node>children=node.children;for(inti=children.size()-1;i>=0;--i){Nodechild=children.get(i);if(child!=null){nodes.push(child);states.push(0);}}}else{intm1=0,m2=0;for(Nodechild:node.children){intt=height.getOrDefault(child,0);if(t>m1){m2=m1;m1=t;}elseif(t>m2){m2=t;}}ans=Math.max(ans,m1+m2);height.put(node,m1+1);}}returnans;}}
/** * Definition for a Node. * type Node struct { * Val int * Children []*Node * } */funcdiameter(root*Node)int{ifroot==nil{return0}typeframestruct{node*Nodestateint}ans:=0height:=map[*Node]int{}stk:=[]frame{{root,0}}forlen(stk)>0{f:=stk[len(stk)-1]stk=stk[:len(stk)-1]iff.state==0{stk=append(stk,frame{f.node,1})children:=f.node.Childrenfori:=len(children)-1;i>=0;i--{ifchildren[i]!=nil{stk=append(stk,frame{children[i],0})}}}else{m1,m2:=0,0for_,child:=rangef.node.Children{t:=height[child]ift>m1{m2,m1=m1,t}elseift>m2{m2=t}}ans=max(ans,m1+m2)height[f.node]=m1+1}}returnans}
Solution 4: Build Graph + Two Explicit Stacks
Thinking
Solution 1 already obtains the diameter from child heights. The classic tree algorithm instead expands the \(N\)-ary tree into an undirected graph and searches twice for a farthest node. Building that graph and walking a chain by recursion also reach depth \(1000\). The build uses a stack and writes each parent-child edge in both directions. Each search marks a node when it is pushed, so the undirected edge back to the parent is not expanded. The first search starts at the root, and the second starts at the node found by the first. The second distance is the diameter.
Walk the tree with a stack and turn each parent-child edge into two undirected edges. Then search twice with an explicit stack. From the start node, a popped node updates the farthest node with its distance, and every unvisited neighbor is pushed with that distance plus one. The first start is the root. The second start is the farthest node from the first search. The farthest distance of the second search is the diameter.
The time complexity is \(O(n)\), and the space complexity is \(O(n)\), where \(n\) is the number of nodes.
"""# Definition for a Node.class Node: def __init__(self, val=None, children=None): self.val = val self.children = children if children is not None else []"""classSolution:defdiameter(self,root:'Node')->int:""" :type root: 'Node' :rtype: int """ifrootisNone:return0g=defaultdict(list)seen={root}stk=[root]whilestk:u=stk.pop()forchildinu.children:ifchildisNoneorchildinseen:continueseen.add(child)g[u].append(child)g[child].append(u)stk.append(child)deffarthest(start):vis={start}walk=[(start,0)]best,node=0,startwhilewalk:u,t=walk.pop()ift>best:best,node=t,uforving[u]:ifvnotinvis:vis.add(v)walk.append((v,t+1))returnbest,node_,nxt=farthest(root)ans,_=farthest(nxt)returnans
/*// Definition for a Node.class Node { public int val; public List<Node> children; public Node() { children = new ArrayList<Node>(); } public Node(int _val) { val = _val; children = new ArrayList<Node>(); } public Node(int _val,ArrayList<Node> _children) { val = _val; children = _children; }};*/classSolution{publicintdiameter(Noderoot){if(root==null){return0;}Map<Node,List<Node>>g=newHashMap<>();Set<Node>seen=newHashSet<>();Deque<Node>stk=newArrayDeque<>();stk.push(root);seen.add(root);while(!stk.isEmpty()){Nodeu=stk.pop();for(Nodechild:u.children){if(child==null||!seen.add(child)){continue;}g.computeIfAbsent(u,k->newArrayList<>()).add(child);g.computeIfAbsent(child,k->newArrayList<>()).add(u);stk.push(child);}}Node[]nxt=newNode[]{root};farthest(g,root,nxt);returnfarthest(g,nxt[0],nxt);}privateintfarthest(Map<Node,List<Node>>g,Nodestart,Node[]nxt){Set<Node>vis=newHashSet<>();Deque<Node>nodes=newArrayDeque<>();Deque<Integer>dist=newArrayDeque<>();nodes.push(start);dist.push(0);vis.add(start);intbest=0;nxt[0]=start;while(!nodes.isEmpty()){Nodeu=nodes.pop();intt=dist.pop();if(t>best){best=t;nxt[0]=u;}List<Node>vs=g.get(u);if(vs==null){continue;}for(Nodev:vs){if(vis.add(v)){nodes.push(v);dist.push(t+1);}}}returnbest;}}
/** * Definition for a Node. * type Node struct { * Val int * Children []*Node * } */funcdiameter(root*Node)int{ifroot==nil{return0}g:=map[*Node][]*Node{}seen:=map[*Node]bool{root:true}stk:=[]*Node{root}forlen(stk)>0{u:=stk[len(stk)-1]stk=stk[:len(stk)-1]for_,child:=rangeu.Children{ifchild==nil||seen[child]{continue}seen[child]=trueg[u]=append(g[u],child)g[child]=append(g[child],u)stk=append(stk,child)}}farthest:=func(start*Node)(int,*Node){typeframestruct{node*Nodedistint}vis:=map[*Node]bool{start:true}walk:=[]frame{{start,0}}best,node:=0,startforlen(walk)>0{f:=walk[len(walk)-1]walk=walk[:len(walk)-1]iff.dist>best{best,node=f.dist,f.node}for_,v:=rangeg[f.node]{if!vis[v]{vis[v]=truewalk=append(walk,frame{v,f.dist+1})}}}returnbest,node}_,nxt:=farthest(root)ans,_:=farthest(nxt)returnans}