Python 0590.py
class Solution:
def postorder(self, root: 'Node') -> List[int]:
# see 589
def deeper(node):
if node:
for i in node.children:
yield from deeper(i)
yield node.val
return deeper(root)
Difficulty
and has an acceptance rate of about 81% (500k+ submissions).
Typical patterns to solve this problem:
- Depth First Search
- Stack
- Tree
AI Disclaimer
However, two years later I set up this website and used various AI tools to generate most of the walkthrough / explanations below.
See this dedicated page for more details.
Code Walkthrough
Overview
The provided implementation defines a Solution class with a single public method postorder. This method performs a post-order traversal of an N-ary tree, returning the node values in the order in which each node is visited after all of its children. The traversal logic is encapsulated in a nested generator function named deeper. By yielding values directly from the generator, the implementation produces a lazy sequence rather than constructing an intermediate list.
Key Steps
The core of the algorithm resides in the nested deeper function:
def deeper(node):
if node:
for i in node.children:
yield from deeper(i)
yield node.val
The outer method simply invokes this generator with the root node:
return deeper(root)
The first step checks whether the current node reference is truthy. If the node is None, the function returns without yielding any value, effectively terminating that branch of recursion.
The second step iterates over the children collection of the current node. For each child i, the statement yield from deeper(i) delegates the iteration to the recursive call, streaming all values produced by the child’s subtree directly to the caller. This delegation eliminates the need for an explicit loop that would collect child results before yielding them.
The final step, yield node.val, emits the value of the current node after all of its descendants have been processed, satisfying the post-order requirement.
Edge Cases Handled
The recursive generator naturally accommodates several edge conditions:
-
Empty tree - When
rootisNone, the initial call todeeperreceives a falsy argument. The guardif node:prevents any further execution, and the generator yields no elements. Consumers of the result simply receive an empty iterator. -
Leaf node - A node without children has an empty
childreniterable. Theforloop executes zero times, and the generator proceeds directly toyield node.val, correctly emitting the leaf’s value as the sole element of its subtree. -
Highly unbalanced tree - The recursion depth follows the longest root-to-leaf path. Because the generator yields values incrementally, the call stack is the only additional memory consumption, and the algorithm remains correct regardless of how skewed the tree structure is.
-
Nodes with many children - The
forloop iterates over each child in order, andyield fromstreams each child’s subtree before moving to the next sibling. This preserves the left-to-right ordering expected in a typical post-order traversal of an N-ary tree.
Performance Optimizations
The implementation leverages Python’s generator protocol to achieve several performance benefits:
-
Lazy evaluation - By yielding values as they are discovered, the algorithm avoids allocating a full list of node values. This reduces peak memory usage, especially for large trees where the number of nodes may be substantial.
-
Direct delegation with
yield from- The use ofyield frompasses control to the recursive generator without the overhead of manually iterating over its output and re-yielding each element. This results in a tighter execution path and fewer Python bytecode instructions per yielded value. -
Minimal data copying - Since each node’s value is emitted exactly once and never stored in an intermediate container, the code eliminates unnecessary copying of integers or references, contributing to faster overall traversal when the consumer processes values on the fly.
Collectively, these choices make the traversal both memory-efficient and streamlined in terms of execution flow, while preserving the clarity of a straightforward recursive definition.
ELI5 - Simplified Explanation
Imagine you are holding a big, beautiful tree made of wooden blocks. This specific tree has a special rule for playing. You have a little helper character that starts at the very top of the tree, which is called the root.
The helper looks at where it is standing and notices that each block can have several smaller blocks hanging underneath it like branches. The helper goes down into those branches one by one to see what is hidden deep inside. It keeps going down as far as it can until it reaches the very last block at the bottom of a branch.
Once the helper reaches a block that has no more branches underneath it, it says the number written on that block out loud. After saying the number, the helper climbs back up to the block right above it to check if there are any other branches it missed. It repeats this over and over again, visiting every single branch and every single block. The helper always makes sure to look at all the little branches first before it ever says the number written on the big block it is currently holding onto. By the time the helper finishes visiting every spot, all the numbers have been called out in a special order starting from the bottom of the tree and working all the way back up to the top.
Code Quality
Complexity Analysis
As a rule of thumb, the maximum input size for one second execution time is
- factorial: up to 10 elements
- exponential: up to 20 elements
- polynomial: a few thousand elements
- loglinear: up to 100,000 elements
- linear: a few million elements
- logarithmic: a billion elements
- constant: practically unlimited
Other Solutions
Obviously, I did that after I solved the problem myself.
In general, AlgoMonster and WalkCCC solutions are high-quality and always worth a look.
I haven't checked most of the other solutions, but they might be helpful as well.
And of course, the LeetCode forum is always a good place (or even the best) to look for alternative approaches.
(click on a column header to sort the table)
| author | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| author | solutions | ||||||||||
| www | AlgoMonster | C++ | Python | Java | TS | ||||||
| WalkCCC | C++ | Java | |||||||||
| leetcode.ca | C++ | Python | Java | Go | TS | ||||||
| LeetcodeHelp | C++ | Python | Java | ||||||||
| Progiez | C++ | Python | Java | ||||||||
| github | BigEggStudy | C# | |||||||||
| black-shadows | C++ | ||||||||||
| charles-wangkai | Java | ||||||||||
| DimitrisJim | C | ||||||||||
| everthis | JS | ||||||||||
| fartem | Ruby | ||||||||||
| JoshCrozier | JS | ||||||||||
| keineahnung2345 | C++ | ||||||||||
| Krazune | C++ | ||||||||||
| lkwq007 | Python | ||||||||||
| Shamik07 | Dart | ||||||||||
| sonugiri1043 | C++ | ||||||||||
| suxrobGM | C# | ||||||||||
| varunu28 | Java | ||||||||||
| YuriSpiridonov | Python | ||||||||||
| AlgoMonster | C++, Python, Java, TypeScript | ||||||||||
| WalkCCC | C++, Java | ||||||||||
| leetcode.ca | C++, Go, Java, Python, TypeScript | ||||||||||
| LeetcodeHelp | C++, Java, Python | ||||||||||
| Progiez | C++, Java, Python | ||||||||||
| BigEggStudy | C# | ||||||||||
| black-shadows | C++, C++ | ||||||||||
| charles-wangkai | Java | ||||||||||
| DimitrisJim | C | ||||||||||
| everthis | JavaScript | ||||||||||
| fartem | Ruby | ||||||||||
| JoshCrozier | JavaScript | ||||||||||
| keineahnung2345 | C++ | ||||||||||
| Krazune | C++ | ||||||||||
| lkwq007 | Python | ||||||||||
| Shamik07 | Dart | ||||||||||
| sonugiri1043 | C++ | ||||||||||
| suxrobGM | C# | ||||||||||
| varunu28 | Java | ||||||||||
| YuriSpiridonov | Python |
Videos
Similar Problems
- problem 589: N-ary Tree Preorder Traversal
Solved On The Same Day
- problem 144: Binary Tree Preorder Traversal
- problem 145: Binary Tree Postorder Traversal
- problem 222: Count Complete Tree Nodes
- problem 404: Sum of Left Leaves
- problem 501: Find Mode in Binary Search Tree
- problem 543: Diameter of Binary Tree
- problem 559: Maximum Depth of N-ary Tree
- problem 589: N-ary Tree Preorder Traversal
- problem 637: Average of Levels in Binary Tree
- problem 700: Search in a Binary Search Tree
- problem 703: Kth Largest Element in a Stream
- problem 79: Word Search
- problem 81: Search in Rotated Sorted Array II
- problem 97: Interleaving String
- problem 99: Recover Binary Search Tree
- problem 173: Binary Search Tree Iterator
- problem 199: Binary Tree Right Side View
- problem 230: Kth Smallest Element in a BST
- problem 235: Lowest Common Ancestor of a Binary Search Tree
- problem 341: Flatten Nested List Iterator
- problem 429: N-ary Tree Level Order Traversal
- problem 508: Most Frequent Subtree Sum
- problem 513: Find Bottom Left Tree Value
- problem 538: Convert BST to Greater Tree
- problem 65: Valid Number
- problem 85: Maximal Rectangle
- problem 87: Scramble String
- problem 126: Word Ladder II
- problem 127: Word Ladder
Extra
- Tech Stack - my development environment and tools
- AI - how AI helped me create this website (NOT solving the problems)
- Progress - progression over time
- Quality - code quality