Skip to content

Commit 8efa517

Browse files
committed
Add recursive staircase
1 parent fd3c62a commit 8efa517

File tree

5 files changed

+153
-0
lines changed

5 files changed

+153
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Bài Toán Đệ Quy Cầu Thang
2+
3+
## Vấn đề
4+
5+
`n` bậc thang, người đứng dưới cầu thang muốn đi lên trên cùng. Họ có thể lựa chọn bước `1` hoặc `2` bậc tại một thời điểm. _Đếm số cách mà một người có thể đi lên trên cầu thang_.
6+
7+
![](https://cdncontribute.geeksforgeeks.org/wp-content/uploads/nth-stair.png)
8+
9+
## Giải pháp
10+
11+
Đây là một bài toán thú vị vì có nhiều cách để giải nó, dưới đây là minh hoạ của các giải pháp lập trình khác nhau.
12+
13+
- [Phá Mã](./recursiveStaircaseBF.js) - Thời gian: `O(2^n)`; Không gian: `O(1)`
14+
- [Dùng Bộ Nhớ Phụ](./recursiveStaircaseMEM.js) - Thời gian: `O(n)`; Không gian: `O(n)`
15+
- [Quy Hoạch Động](./recursiveStaircaseDP.js) - Thời gian: `O(n)`; Không gian: `O(n)`
16+
- [Vòng Lặp](./recursiveStaircaseIT.js) - Time: `O(n)`; Space: `O(1)`
17+
18+
## Vòng Lặp
19+
20+
- [On YouTube by Gayle Laakmann McDowell](https://www.youtube.com/watch?v=eREiwuvzaUM&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8&index=81&t=0s)
21+
- [GeeksForGeeks](https://www.geeksforgeeks.org/count-ways-reach-nth-stair/)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Bài Toán Đệ Quy Cầu Thang (Phá Mã)
3+
*
4+
* @param {number} stairsNum - số bậc có thể bước.
5+
* @return {number} - Số cách để bước lên cầu thang.
6+
*/
7+
export default function recursiveStaircaseBF(stairsNum) {
8+
if (stairsNum <= 0) {
9+
// Số bậc nhỏ hơn 1 thì không có gì để bước
10+
return 0;
11+
}
12+
13+
if (stairsNum === 1) {
14+
// Nếu chỉ có 1 bậc chỉ có một cách bước.
15+
return 1;
16+
}
17+
18+
if (stairsNum === 2) {
19+
// Nếu có hai bậc có hai cách bước là (1+1) và (2).
20+
return 2;
21+
}
22+
23+
// Tính tổng số bước ta cần thực hiện sau khi thực hiện một bước
24+
// với số bước ta cần thực hiện sau khi thực hiện hai bước lên.
25+
return recursiveStaircaseBF(stairsNum - 1) + recursiveStaircaseBF(stairsNum - 2);
26+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Bài Toán Đệ Quy Cầu Thang (Quy Hoạch Động)
3+
*
4+
* @param {number} stairsNum - số bậc có thể bước.
5+
* @return {number} - Số cách để bước lên cầu thang.
6+
*/
7+
export default function recursiveStaircaseDP(stairsNum) {
8+
if (stairsNum < 0) {
9+
// Không có bậc thang nào để bước.
10+
return 0;
11+
}
12+
13+
// Khởi tạo vector "steps" sẽ chứa tất cả các cách có thể đến bước tương ứng.
14+
const steps = new Array(stairsNum + 1).fill(0);
15+
16+
// Khởi tạo số cách để đến bậc thứ 0, 1 và 2.
17+
steps[0] = 0;
18+
steps[1] = 1;
19+
steps[2] = 2;
20+
21+
if (stairsNum <= 2) {
22+
// Trả về số cách để đến bậc thứ 0, 1 và 2.
23+
return steps[stairsNum];
24+
}
25+
26+
// Tính toán bước kế tiếp dựa trên hai bước trước nó.
27+
for (let currentStep = 3; currentStep <= stairsNum; currentStep += 1) {
28+
steps[currentStep] = steps[currentStep - 1] + steps[currentStep - 2];
29+
}
30+
31+
// Trả về các cách để có thể đi hết cầu thang.
32+
return steps[stairsNum];
33+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Bài Toán Đệ Quy Cầu Thang (Vòng Lặp)
3+
*
4+
* @param {number} totalStairs - số bậc có thể bước.
5+
* @return {number} - Số cách để bước lên cầu thang.
6+
*/
7+
export default function recursiveStaircaseIT(stairsNum) {
8+
if (stairsNum <= 0) {
9+
// Số bậc nhỏ hơn 1 thì không có gì để bước
10+
return 0;
11+
}
12+
13+
// Tạo mảng lưu số cách đến bậc thứ 0, 1 và 2.
14+
const steps = [1, 2];
15+
16+
if (stairsNum <= 2) {
17+
// Trả về số cách có thể để đến bậc thứ 1 hoặc thứ 2.
18+
return steps[stairsNum - 1];
19+
}
20+
21+
// Tính số cách đi đến bước thứ n dựa trên những cách trước đó.
22+
// So với giải pháp Quy hoạch động, ta chỉ lưu trữ thông tin cho hai bước
23+
// trước nó thay vì lưu tất cả các bước.
24+
for (let currentStep = 3; currentStep <= stairsNum; currentStep += 1) {
25+
[steps[0], steps[1]] = [steps[1], steps[0] + steps[1]];
26+
}
27+
28+
// Trả lại các cách có thể để đến bậc được yêu cầu.
29+
return steps[1];
30+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Bài Toán Đệ Quy Cầu Thang (Giải Pháp dùng Bộ Nhớ Phụ)
3+
*
4+
* @param {number} totalStairs - số bậc có thể bước.
5+
* @return {number} - Số cách để bước lên cầu thang.
6+
*/
7+
export default function recursiveStaircaseMEM(totalStairs) {
8+
// Bảng ghi nhớ sẽ chứa tất cả các kết quả được tính toán đệ quy để
9+
// tránh tính toán chúng nhiều lần.
10+
const memo = [];
11+
12+
// Đệ quy với closure.
13+
const getSteps = (stairsNum) => {
14+
if (stairsNum <= 0) {
15+
// Số bậc nhỏ hơn 1 thì không có gì để bước
16+
return 0;
17+
}
18+
19+
if (stairsNum === 1) {
20+
// Nếu chỉ có 1 bậc chỉ có một cách bước.
21+
return 1;
22+
}
23+
24+
if (stairsNum === 2) {
25+
// Nếu có hai bậc có hai cách bước là (1+1) và (2).
26+
return 2;
27+
}
28+
29+
// Tránh đệ quy cho các bước đã tính toán gần đây.
30+
if (memo[stairsNum]) {
31+
return memo[stairsNum];
32+
}
33+
34+
// Tính tổng số bước ta cần thực hiện sau khi thực hiện một bước
35+
// với số bước ta cần thực hiện sau khi thực hiện hai bước lên.
36+
memo[stairsNum] = getSteps(stairsNum - 1) + getSteps(stairsNum - 2);
37+
38+
return memo[stairsNum];
39+
};
40+
41+
// Trả về các cách để có thể đi hết cầu thang.
42+
return getSteps(totalStairs);
43+
}

0 commit comments

Comments
 (0)