Hàm đệ Quy Trong Javascript - KungFu Tech
Có thể bạn quan tâm
Hàm đệ quy trong JavaScript chính là một hàm tự gọi lại chính nó.

Ví dụ sau in ra Hello world! n lần sử dụng hàm đệ quy:
js Copy function sayHello(count) { if (count <= 0) { return; } console.log("Hello world!"); sayHello(count - 1); } // sayHello 5 lần sayHello(5);Kết quả
Hello world! Hello world! Hello world! Hello world! Hello world!
Đây chỉ là ví dụ minh họa về hàm đệ quy trong JavaScript. Thực tế, bạn có thể sử dụng vòng lặp for để giải quyết bài toán trên:
js Copy function sayHello(count) { for (let i = 0; i < count; i++) { console.log("Hello world!"); } } sayHello(5);Kết quả hoàn toàn tương đương. Tuy nhiên, có rất nhiều trường hợp việc sử dụng hàm đệ quy lại giúp code trở nên ngắn gọn, rõ ràng và dễ bảo trì hơn sử dụng vòng lặp.
Vì vậy, mình hãy cùng tìm hiểu về hàm đệ quy trong JavaScript để biết cách áp dụng khi cần thiết.
Các thành phần cơ bản của hàm đệ quy
Hàm đệ quy nói chung và hàm đệ quy trong JavaScript nói riêng, có hai thành phần đặc trưng:
- Phần cơ sở: điều kiện để thoát đệ quy.
- Phần đệ quy: gọi lại chính nó.
Cũng tương tự như điều kiện để thoát vòng lặp, nếu không có điều kiện cơ sở thì hàm đệ quy sẽ không bao giờ dừng lại (đệ quy vô hạn), dẫn đến tràn stack.
Ví dụ bỏ qua điều kiện cơ cở của hàm đệ quy trên:
js Copy function sayHello(count) { // // phần cơ sở: điều kiện thoát đệ quy là biến count <= 0 // if (count <= 0) { // return; // } console.log("Hello world!"); // phần đệ quy: gọi lại chính hàm sayHello sayHello(count - 1); } sayHello();Kết quả là Hello world! được in ra khoảng hơn 10000 lần thì bị lỗi tràn stack, cụ thể: Uncaught RangeError: Maximum call stack size exceeded.
Kết quả
Hello world! Hello world! ... Hello world!
Uncaught RangeError: Maximum call stack size exceeded
Chú ý: con số 10000 trên chỉ là tương đối, phụ thuộc vào từng JavaScript Engine
Call stack là gì?
- Call: là lời gọi hàm.
- Stack: là ngăn xếp, hoạt động theo nguyên tắc "vào sau ra trước", tiếng anh là Last In First Out - LIFO.
Khi gọi hàm, JavaScript Engine đưa các lời gọi hàm vào trong một ngăn xếp.
- Hàm gọi sau được đưa lên đầu ngăn xếp, đến khi gọi xong thì đưa hàm ra khỏi ngăn xếp.
- Cứ như vậy đến khi ngăn xếp trống thì nghĩa là đã gọi xong hàm.
Việc lưu lời gọi hàm vào ngăn xếp là tốn bộ nhớ. Vì vậy, JavaScript Engine sẽ giới hạn kích thước của ngăn xếp (khoảng 10000 hoặc hơn, tùy thuộc vào engine).
Khi sử dụng hàm đệ quy trong JavaScript, bạn cần chú ý đến điều kiện cơ sở để thoát đệ quy, tránh đệ quy vô hạn dẫn đến tràn stack như ví dụ trên.
Sử dụng hàm đệ quy khi nào?
Khi một bài toán có thể chia ra thành nhiều bài toán con và bài toán con có dạng tương tự như bài toán cha thì bạn có thể sử dụng hàm đệ quy.
Ví dụ bài toán tính giá trị của lũy thừa a^b (a mũ b) với định nghĩa toán học là:
js Copy a^b = 1, nếu b = 0 a^b = a * a^(b-1), nếu b > 0Theo định nghĩa trên, bài toán cha là tính a^b lại dựa trên bài toán con a^(b-1). Vì vậy, bạn có thể áp dụng hàm đệ quy để tính giá trị a^b như sau:
js Copy function power(a, b) { // điều kiện dừng đệ quy if (b === 0) { return 1; } // gọi lại chính nó return a * power(a, b - 1); } console.log(power(2, 0)); // 1 console.log(power(2, 1)); // 2 console.log(power(2, 2)); // 4 console.log(power(2, 3)); // 8So sánh hàm đệ quy và vòng lặp
Bài toán tính lũy thừa trên có thể giải quyết bằng cách sử dụng vòng lặp:
js Copy function power(a, b) { let ret = 1; for (let i = 0; i < b; i++) { ret *= a; } return ret; } console.log(power(2, 0)); // 1 console.log(power(2, 1)); // 2 console.log(power(2, 2)); // 4 console.log(power(2, 3)); // 8Đa số các bài toán có thể sử dụng hàm đệ quy thì đều có thể giải bằng cách sử dụng vòng lặp.
Việc sử dụng vòng lặp nhìn chung là chạy nhanh và tiết kiệm bộ nhớ hơn cách sử dụng hàm đệ quy. Vì sử dụng vòng lặp chỉ đưa lời gọi hàm vào call stack 1 lần - không mất thời gian và không gian bộ nhớ như đệ quy.
Ngược lại, cách sử dụng hàm đệ quy lại giúp code trở nên ngắn gọn và rõ ràng hơn sử dụng vòng lặp.
Có thể bạn chưa biết
Đối với một số bài toán có thể giải bằng hai cách mà không quá quan trọng thời gian và không gian bộ nhớ thì mình sẽ ưu tiên sử dụng hàm đệ quy trong JavaScript.
Tổng kết
Hàm đệ quy trong JavaScript là hàm gọi lại chính nó, với hai thành phần cơ bản là:
- Phần cơ sở: điều kiện để thoát đệ quy. Nếu điều kiện cơ sở không chính xác thì có thể dẫn tới đệ quy vô hạn, gây ra lỗi tràn stack.
- Phần đệ quy: gọi lại chính nó.
Đa số các bài toán có thể sử dụng hàm đệ quy thì đều có thể giải bằng cách sử dụng vòng lặp. Tùy thuộc vào yêu cầu của từng bài toán mà bạn lựa chọn cách làm phù hợp:
- Nếu bạn cần tối ưu thời gian, không gian bộ nhớ thì sử dụng vòng lặp.
- Nếu bạn ưu tiên tính ngắn ngọn, dễ bảo trì thì có thể sử dụng hàm đệ quy.
Thực hành
Bài 1
Viết hàm sumTo(n) để tính tổng các số từ 1 đến n: 1 + 2 + ... + n, ví dụ:
js Copy sumTo(1) = 1 sumTo(2) = 2 + 1 = 3 sumTo(3) = 3 + 2 + 1 = 6 sumTo(4) = 4 + 3 + 2 + 1 = 10 ... sumTo(100) = 100 + 99 + ... + 2 + 1 = 5050Triển khai hàm sumTo(n) theo 3 cách khác nhau:
- Sử dụng vòng lặp.
- Sử dụng đệ quy.
- Sử dụng công thức toán học.
Xem đáp án
► Cách 1: sử dụng vòng lặp
js Copy function sumTo(n) { let sum = 0; for (i = 0; i <= n; i++) { sum += i; } return sum; }► Cách 2: sử dụng đệ quy
js Copy function sumTo(n) { if (n === 1) return 1; return n + sumTo(n - 1); }► Cách 3: sử dụng công thức toán học
js Copy function sumTo(n) { return (n * (n + 1)) / 2; }Bài 2
Viết hàm factorial(n) tính n giai thừa: n! = (n) * (n-1) * (n-2) * ... * 1, ví dụ:
js Copy 1! = 1 2! = 2 * 1 = 2 3! = 3 * 2 * 1 = 6 4! = 4 * 3 * 2 * 1 = 24 5! = 5 * 4 * 3 * 2 * 1 = 120Triển khai hàm factorial(n) theo hai cách:
- Sử dụng vòng lặp.
- Sử dụng đệ quy.
Xem đáp án
► Cách 1: sử dụng vòng lặp
js Copy function factorial(n) { let ret = 1; for (let i = 1; i <= n; i++) { ret *= i; } return ret; }► Cách 2: sử dụng đệ quy
js Copy function factorial(n) { if (n === 0) return 1; return n * factorial(n - 1); }Bài 3
Viết hàm fibonacci(n) tính số fibonacci theo công thức:
js Copy fibonacci(n) = n nếu n = 0 hoặc n = 1 fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2)Ví dụ:
js Copy fibonacci(0) = 0 fibonacci(1) = 1 fibonacci(2) = 1 + 0 = 1 fibonacci(3) = 1 + 1 = 2 fibonacci(4) = 2 + 1 = 3 fibonacci(5) = 3 + 2 = 5Xem đáp án
js Copy function fibonacci(n) { if (n === 0 || n === 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); }Bài 4
Cho danh sách liên kết đơn như sau:
js Copy let list = { value: 1, next: { value: 2, next: { value: 3, next: { value: 4, next: null, }, }, }, };Viết hàm printList(singleLinkedList) để in ra các phần tử của list theo hai cách:
- Sử dụng vòng lặp.
- Sử dụng hàm đệ quy.
Kết quả hiển thị trên console là:
Kết quả
1 2 3 4
Xem đáp án
► Cách 1: sử dụng vòng lặp
js Copy function printList(singleLinkedList) { let p = singleLinkedList; while (p) { console.log(p.value); p = p.next; } }► Cách 2: sử dụng đệ quy
js Copy function printList(singleLinkedList) { if (singleLinkedList === null) return; console.log(singleLinkedList.value); printList(singleLinkedList.next); }Bài 5
Cho danh sách liên kết đơn như sau:
js Copy let list = { value: 1, next: { value: 2, next: { value: 3, next: { value: 4, next: null, }, }, }, };Viết hàm printReverseList(singleLinkedList) để in ra các phần tử của list theo thứ tự ngược lại với bài 4 bằng hai cách:
- Sử dụng vòng lặp.
- Sử dụng hàm đệ quy.
Kết quả hiển thị trên console là:
Kết quả
4 3 2 1
Xem đáp án
► Cách 1: sử dụng vòng lặp
js Copy function printReverseList(singleLinkedList) { let arr = []; let p = singleLinkedList; while (p) { arr.push(p.value); p = p.next; } for (let i = arr.length - 1; i >= 0; i--) { console.log(arr[i]); } }► Cách 2: sử dụng đệ quy
js Copy function printReverseList(singleLinkedList) { if (singleLinkedList.next) { printReverseList(singleLinkedList.next); } console.log(singleLinkedList.value); }Từ khóa » đệ Quy Tính Lũy Thừa
-
Chương Trình Tính X Mũ N Sử Dụng đệ Quy? - Dạy Nhau Học
-
[Basic-DSAA] Giải Thuật đệ Quy - Tính Lũy Thừa. - CodeLearn
-
Tính X Lũy Thừa N Theo đệ Quy - Cộng đồng C Việt
-
Chương Trình Tính X Mũ N Sử Dụng đệ Quy? - Code 24h
-
[C] - Đệ Quy - Fibo - Giai Thừa - Lũy Thừa - NP | Học Lập Trình - YouTube
-
Top 14 đệ Quy Tính Lũy Thừa
-
Tính Lũy Thừa Trong Scratch đệ Quy Và Không đệ Quy - Ôn Thi HSG
-
Tính Lũy Thừa Nhanh Bằng Bình Phương Và Nhân - VietCodes
-
Thuật Toán Tính Lũy Thừa Nhanh Trong C/C++
-
Tính T(x, N) = X^n Bằng C / C++
-
Thuật Toán Đệ Quy Và Một Số Bài Toán Đệ Quy Cơ Bản - VN SEEDER
-
Tính Luỹ Thừa Với đệ Quy - »-(¯`°•.¸¯`°•._Diễn Đàn 51CTH_.•°´¯¸.•° ´¯)-«
-
Bai De Quy - SlideShare
-
Bài Tập Pascal 03 Đệ Quy - Tài Liệu Text - 123doc
-
Phép Nhân Ấn Độ Và Lũy Thừa Modulo - Viblo
-
Cách Tính Lũy Thừa Nhanh Nhất - Toploigiai