Using a stack-based data structure of your own is always better than using a call stack for recursive traversal:
- You get to store only the state you strictly need and no more. A call stack stores all local variables and parameters for each frame, potentially wasting memory.
- You get to access previous elements. A call stack does not typically allow you to access data in previous stack frames.
- You get to push/pop multiple, variable number of elements per iteration. Call stacks typically only work with a fixed number of local variables/parameters per stack frame. The workarounds are to 1. dynamically allocate memory for each traversal, which causes lots of fragmentation, or 2. use an oversized fixed buffer for each stack frame, which causes memory waste, or 3. use an ‘alloca’-like construct, if your runtime supports it at all.
- You get to explicitly bound its memory usage and use custom handling/error reporting when your desired max depth is exceeded. Doing so with a call stack is typically a lot more clunky and bug-prone, as it requires exception handling and mutating global state.
- You can serialize/deserialize your stack to/from persistent storage however you like if you wish to pause traversal and resume it later. A call stack can only do this if it’s a ‘stackful coroutine’, which even if it is supported in your runtime of choice, gives you little to no control over where or how it is stored.