Reputation:
I need to solve the following task recursively:
Find combinations of ways to climb a stair case, you can take only one or two steps at a time.
For example, for n = 3
, it should output [1,1,1],[2,1],[1,2]
.
This is what I got so far:
function num_ways(n, alreadyTakenSteps) {
var output = [];
if (n == 0) {
console.log(...output, ...alreadyTakenSteps);
return [...output, ...alreadyTakenSteps];
}
if (n >= 1) {
var res1 = num_ways(n - 1, [...[1], ...alreadyTakenSteps]);
if (res1.length) {
output=[...output, ...res1];
}
//console.log(res1);
}
if (n >= 2) {
var res2 = num_ways(n - 2, [...[2], ...alreadyTakenSteps]);
output=[...output, ...res2];
}
return output;
}
var output = num_ways(3, []);
console.log(output);
In the stop-condition (first if-statement), I output the result. There, all outputs are fine. But the overall returned value is wrong (see output of console.log(output)
).
How can i make this working?
Upvotes: 0
Views: 68
Reputation: 3444
Now, the only problem with your code is in case n == 0
.
Here, you currently return an array containing the elements of alreadyTakenSteps
. This is wrong, because the return-value is then destructured, which results in losing the combination of a way among other destructured ways.
You can imagine it like this:
Currently, you return a way of taking the steps, which is then destructured and placed next to other destructured ways. We are essentially missing the ways among themselves, as we remove the arrays which identify them.
Instead, we want to return an array containing the ways. That array will then be destructured, placing all the different ways in one array. This will result in an array containing all the ways.
Simply put:
In case n == 0
, return [alreadyTakenSteps]
; return the existing way in a wrapper-array.
...[x]
will result in x
. It basically creates an immediately destructured array, which may impact memory-usage for a large n
.x
.var
has become somewhat obsolete, as it can be replaced by either let
or const
in most cases.var
is not a problem, but the alternatives may convey more meaning (e.g. the reference of a const
is never changed, the reference of a let
may be changed).num_ways()
; more precisely, we can default the alreadyTakenSteps
-parameter to []
:function num_ways(n, alreadyTakenSteps = []) { ... }
num_ways(3)
, but we can still pass the second argument in case we want to extend some already taken steps.n == 0
:output
-array, effectively doing nothing. In this case, output
actually doesn't need to be initialized yet.[alreadyTakenSteps]
(incorporating the solution from before), with var output = [];
after this if-statement.n >= 1
:if (res1.length)
is unnecessary, since we pass [1, ...alreadyTakenSteps]
as the second argument, which contains at least 1
as an element.What I personally dislike is your way of re-instantiating the output-array for cases n >= 1
and n >= 2
. This can be circumvented by using more cases.
Here is my implementation of the task. Difference between yours and mine is, that mine will return an empty array instead of an array containing an empty array.
Semantically, this would be equal to…
console.log(recursiveNumWays(3));
function recursiveNumWays(n, arr = []) {
if (n == 0) return arr;
if (n == 1) return [1, ...arr];
const ways1 = recursiveNumWays(n - 1, [1, ...arr]);
const ways2 = recursiveNumWays(n - 2, [2, ...arr]);
if (n == 2)
return [ways1, ways2];
if (n == 3)
return [...ways1, ways2];
return [...ways1, ...ways2];
}
.as-console-wrapper{max-height:100%!important;top:0}
Upvotes: 0
Reputation: 50797
I just want to point out a perhaps cleaner implementation of the same idea.
const climbStairs = (n) =>
n < 1
? [[]]
: [
... climbStairs (n - 1) .map (p => [1, ... p]),
... (n > 1 ? climbStairs (n - 2) .map (p => [2, ... p]) : []),
]
console .log (climbStairs (5))
.as-console-wrapper {max-height: 100% !important; top: 0}
If there are no stairs remaining, there is only one path, up them, the empty path. If there's at least one, then we can take one step up and recur on the remaining stairs, or, if there are at least two stairs, then we can take two steps up and recur on the remainder. The .map
calls just combine our current step with each of the recursive results.
One nice thing about this is how well it maps to the similar counting problem, where we don't want to list all the paths, but only to count them. Note how close this is to climbStairs
:
const nbrOfWays = (n) =>
n < 1
? 1
: nbrOfWays (n - 1) +
(n > 1 ? nbrOfWays (n - 2) : 0)
console .log (nbrOfWays (5))
Upvotes: 2
Reputation: 3440
When there are no more steps to take you need to encapsulate the walk/array. So instead of return [...output,...alreadyTakenSteps]; do return [...output,...[alreadyTakenSteps]];
function num_ways(n,alreadyTakenSteps) {
var output=[];
if (n == 0) {
console.log(...output,...alreadyTakenSteps);
return [...output,...[alreadyTakenSteps]];
}
if (n >= 1) {
var res1=num_ways(n - 1, [...[1],...alreadyTakenSteps]);
if(res1.length){
output=[...output,...res1];
}
//console.log(res1);
}
if (n >= 2) {
var res2= num_ways(n - 2, [...[2],...alreadyTakenSteps]);
output=[...output,...res2];
}
return output;
};
var output= num_ways(3,[]);
console.log(output);
Upvotes: 0