Let's inspect the programme execution (you can do this easily using a debugger).
Firstly since arr
, n
and r
don't change in the call tree, let's note them down first:
n := 5, r := 3, arr := {1, 2, 3, 4, 5}
For the variable arguments let's build a table and note the first call to combinationUtil
:
Callstack | index | i | data | Cases (printing, returning or recursing)
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 0 | 0 | {., ., .} | recursing (index != r, i != n)
printCombination | | | |
main | | | |
Let's ignore main
and printCombination
in the future, but note that the top of the stack describes the function we've just entered with the given state of index
, i
and data
(and arr, n, r
but those won't change) - specifically we did not yet execute any of the body of the function like assigning data[index] = arr[i]
or similar. Following the execution note how we're neither printing nor breaking out early since index != r and i != n
meaning we'll recurse twice in this function call. Let's step to the first recursive call:
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 1 | 1 | {1, ., .} | recursing
combinationUtil | 0 | 0 | |
When we enter our first recursive call, 1
will have been written to data
so at the beginning of the function we can now see this value in data
. We'll continue twice:
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 2 | {1, 2, .} | recursing
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 3 | 3 | {1, 2, 3} | printing (index == r)
combinationUtil | 2 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
New case: index == r
i.e. we'll loop over data
, print its current content and return, one level higher we'll step into the next recursive call so let's look at the next steps:
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 3 | {1, 2, 3} | recursing
combinationUtil | 2 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 3 | 4 | {1, 2, 4} | printing
combinationUtil | 2 | 3 | |
combinationUtil | 2 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 4 | {1, 2, 4} | recursing
combinationUtil | 2 | 3 | |
combinationUtil | 2 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 3 | 5 | {1, 2, 5} | printing
combinationUtil | 2 | 4 | |
combinationUtil | 2 | 3 | |
combinationUtil | 2 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 5 | {1, 2, 5} | returning (i >= n)
combinationUtil | 2 | 4 | |
combinationUtil | 2 | 3 | |
combinationUtil | 2 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
Can you see how deep the call stack got until we printed 125
? If you've been drawing the call graph along you should have something along the following now:
[] (0, 0)
/
1 (1, 1)
/
12 (2, 2)
/ \
123 12 (2, 3)
/ \
124 12 (2, 4)
/ \
125 ret (2, 5)
We're currently at ret
and I've marked the arguments (index, i)
from our current callstack along the root path (from []
to ret
). We'll be returing in ret
because i == n
, we'll then return from each call up the tree until we reach 1 (index=1, i=1)
where we return from its first recursion (index+1, i+1)
so its next is (index, i+1)
and the following next 6 steps together look like:
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 1 | 2 | {1, 2, 5} | recursing
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 3 | {1, 3, 5} | recursing
combinationUtil | 1 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 3 | 4 | {1, 3, 4} | printing
combinationUtil | 2 | 3 | |
combinationUtil | 1 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 4 | {1, 3, 4} | recursing
combinationUtil | 2 | 3 | |
combinationUtil | 1 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 3 | 5 | {1, 3, 5} | printing
combinationUtil | 2 | 4 | |
combinationUtil | 2 | 3 | |
combinationUtil | 1 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
----------------------+-------+-------+-----------+------------------------------------
> combinationUtil | 2 | 5 | {1, 3, 5} | returning
combinationUtil | 2 | 4 | |
combinationUtil | 2 | 3 | |
combinationUtil | 1 | 2 | |
combinationUtil | 1 | 1 | |
combinationUtil | 0 | 0 | |
And the call graph up to this point looks like:
[] (0, 0)
/
1 (1, 1)
/-------^-------\
12 1 (1, 2)
/ \ /
123 12 13 (2, 3)
/ \ / \
124 12 134 13 (2, 4)
/ \ / \
125 ret 135 ret (2, 5)