Bài tập lập trình - Nâng cao
Trang này tổng hợp 100 bài tập lập trình nâng cao, dành cho bạn nào đã làm quen với các kiến thức cơ bản ở trang Bài tập lập trình - Cơ bản. Nội dung xoay quanh thuật toán sắp xếp/tìm kiếm, đệ quy & backtracking, quy hoạch động, cấu trúc dữ liệu, lập trình hướng đối tượng, lập trình hàm và các thư viện chuẩn hữu ích của từng ngôn ngữ. Mỗi bài có đáp án bằng nhiều ngôn ngữ lập trình khác nhau để bạn tiện đối chiếu.
Mỗi bài đều có phần đáp án gợi ý ở dưới, mặc định ẩn đi - bạn nên tự làm trước, sau đó bấm vào “Xem đáp án” để đối chiếu. Đáp án chỉ là một cách giải, không phải cách duy nhất và không phải lúc nào cũng tối ưu nhất.
Nhóm 1: Thuật toán sắp xếp nâng cao
Phần tiêu đề “Nhóm 1: Thuật toán sắp xếp nâng cao”1. Selection Sort
Cài đặt thuật toán sắp xếp chọn (selection sort) để sắp xếp tăng dần một list số.
Ví dụ:
Input: [64, 25, 12, 22, 11]Output: [11, 12, 22, 25, 64]Xem đáp án
def selection_sort(arr): n = len(arr) for i in range(n): # Tìm vị trí phần tử nhỏ nhất trong phần chưa sắp xếp min_idx = i for j in range(i + 1, n): if arr[j] < arr[min_idx]: min_idx = j arr[i], arr[min_idx] = arr[min_idx], arr[i] return arr
print(selection_sort([64, 25, 12, 22, 11]))#include <iostream>#include <vector>using namespace std;
vector<int> selectionSort(vector<int> arr) { int n = arr.size(); for (int i = 0; i < n; i++) { int minIdx = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) minIdx = j; } swap(arr[i], arr[minIdx]); } return arr;}
int main() { vector<int> arr = {64, 25, 12, 22, 11}; arr = selectionSort(arr); for (int x : arr) cout << x << " "; cout << endl; return 0;}import java.util.Arrays;
public class Main { static int[] selectionSort(int[] arr) { int n = arr.length; for (int i = 0; i < n; i++) { int minIdx = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) minIdx = j; } int tmp = arr[i]; arr[i] = arr[minIdx]; arr[minIdx] = tmp; } return arr; }
public static void main(String[] args) { int[] arr = {64, 25, 12, 22, 11}; System.out.println(Arrays.toString(selectionSort(arr))); }}fun selectionSort(arr: MutableList<Int>): MutableList<Int> { val n = arr.size for (i in 0 until n) { var minIdx = i for (j in i + 1 until n) { if (arr[j] < arr[minIdx]) minIdx = j } val tmp = arr[i] arr[i] = arr[minIdx] arr[minIdx] = tmp } return arr}
fun main() { val arr = mutableListOf(64, 25, 12, 22, 11) println(selectionSort(arr))}List<int> selectionSort(List<int> arr) { int n = arr.length; for (int i = 0; i < n; i++) { int minIdx = i; for (int j = i + 1; j < n; j++) { if (arr[j] < arr[minIdx]) minIdx = j; } int tmp = arr[i]; arr[i] = arr[minIdx]; arr[minIdx] = tmp; } return arr;}
void main() { var arr = [64, 25, 12, 22, 11]; print(selectionSort(arr));}2. Insertion Sort
Cài đặt thuật toán sắp xếp chèn (insertion sort) để sắp xếp tăng dần một list số.
Ví dụ:
Input: [12, 11, 13, 5, 6]Output: [5, 6, 11, 12, 13]Xem đáp án
3. Merge Sort
Cài đặt thuật toán sắp xếp trộn (merge sort) theo kiểu chia để trị (divide and conquer).
Ví dụ:
Input: [38, 27, 43, 3, 9, 82, 10]Output: [3, 9, 10, 27, 38, 43, 82]Xem đáp án
4. Quick Sort
Cài đặt thuật toán sắp xếp nhanh (quick sort) dùng phần tử cuối làm chốt (pivot).
Ví dụ:
Input: [10, 7, 8, 9, 1, 5]Output: [1, 5, 7, 8, 9, 10]Xem đáp án
5. Counting Sort
Cài đặt thuật toán sắp xếp đếm (counting sort), áp dụng cho list số nguyên không âm.
Ví dụ:
Input: [4, 2, 2, 8, 3, 3, 1]Output: [1, 2, 2, 3, 3, 4, 8]Xem đáp án
6. Sắp xếp theo nhiều tiêu chí
Cho một list các dictionary học sinh {"ten": ..., "diem": ..., "tuoi": ...}. Sắp xếp giảm dần theo điểm, nếu điểm bằng nhau thì sắp tăng dần theo tuổi.
Xem đáp án
Nhóm 2: Tìm kiếm nâng cao
Phần tiêu đề “Nhóm 2: Tìm kiếm nâng cao”7. Tìm kiếm nhị phân (Binary Search)
Cài đặt tìm kiếm nhị phân trên một list đã sắp xếp tăng dần, trả về index hoặc -1 nếu không tìm thấy.
Ví dụ:
Input: arr=[1, 3, 5, 7, 9, 11], target=7Output: 3Xem đáp án
8. Tìm kiếm nhị phân đệ quy
Viết lại bài toán tìm kiếm nhị phân bằng đệ quy thay vì vòng lặp.
Xem đáp án
9. Tìm kiếm trong list đã xoay (Rotated Sorted Array)
Cho một list đã sắp xếp tăng dần rồi bị xoay tại một điểm bất kỳ (ví dụ [4,5,6,7,0,1,2]). Tìm vị trí của target với độ phức tạp O(log n) (nghĩa là mỗi bước loại bỏ được một nửa số phần tử còn lại cần xét, giống tìm kiếm nhị phân, thay vì duyệt qua từng phần tử).
Ví dụ:
Input: arr=[4, 5, 6, 7, 0, 1, 2], target=0Output: 4Xem đáp án
10. Tìm phần tử xuất hiện lẻ số lần (dùng XOR)
Cho một list mà mọi phần tử đều xuất hiện đúng 2 lần, trừ một phần tử xuất hiện đúng 1 lần. Tìm phần tử đó, dùng phép toán XOR (^), không dùng thêm bộ nhớ phụ.
Ví dụ:
Input: [4, 1, 2, 1, 2]Output: 4Xem đáp án
Nhóm 3: Đệ quy & Backtracking
Phần tiêu đề “Nhóm 3: Đệ quy & Backtracking”Backtracking (quay lui) là kỹ thuật thử từng lựa chọn một cách đệ quy; nếu lựa chọn đó dẫn đến ngõ cụt (không thể tạo ra lời giải hợp lệ), quay lại bước trước và thử lựa chọn khác, cho đến khi tìm ra lời giải hoặc thử hết mọi khả năng. Xem thêm lý thuyết: Đệ quy (Recursion).
11. Tháp Hà Nội (Tower of Hanoi)
Viết hàm đệ quy in ra các bước di chuyển để giải bài toán Tháp Hà Nội với n đĩa.
Ví dụ:
Input: n=2, source=A, destination=C, auxiliary=BOutput:Di chuyển đĩa 1 từ A sang BDi chuyển đĩa 2 từ A sang CDi chuyển đĩa 1 từ B sang CXem đáp án
12. Tổ hợp chập k (Combinations)
Viết hàm đệ quy combinations(arr, k) sinh ra tất cả tổ hợp chập k phần tử từ list arr (không dùng itertools).
Ví dụ:
Input: arr=[1, 2, 3, 4], k=2Output: [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]Xem đáp án
13. Hoán vị của list (Permutations)
Viết hàm đệ quy permutations(arr) sinh ra tất cả hoán vị của list arr (không dùng itertools).
Ví dụ:
Input: [1, 2, 3]Output: [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]Xem đáp án
14. Tập con (Subsets / Power Set)
Viết hàm đệ quy sinh ra tất cả tập con (kể cả tập rỗng) của một list.
Ví dụ:
Input: [1, 2, 3]Output: [], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]Xem đáp án
15. Bài toán N-Queens
Đếm số cách đặt n quân hậu trên bàn cờ n x n sao cho không có 2 quân nào ăn nhau, dùng backtracking.
Ví dụ:
Input: n=4Output: 2Xem đáp án
16. Đường đi trong lưới (Grid Paths)
Đếm số đường đi từ góc trên-trái đến góc dưới-phải của một lưới m x n, chỉ được di chuyển sang phải hoặc xuống dưới.
Ví dụ:
Input: m=3, n=3Output: 6Xem đáp án
17. Subset Sum
Cho một list số nguyên dương và một tổng đích target. Kiểm tra xem có tồn tại một tập con nào của list có tổng bằng target hay không, dùng đệ quy.
Ví dụ:
Input: arr=[3, 34, 4, 12, 5, 2], target=9Output: TrueXem đáp án
18. Số Catalan bằng đệ quy
Số Catalan thứ n được tính bằng công thức đệ quy: C(0) = 1, C(n) = sum(C(i) * C(n-1-i)) với i từ 0 đến n-1. Viết hàm đệ quy tính số Catalan thứ n.
Ví dụ:
Input: n=4Output: 14Xem đáp án
19. Ghép ngoặc hợp lệ (Generate Parentheses)
Với n cặp ngoặc, sinh ra tất cả các chuỗi ngoặc () hợp lệ có thể tạo được, dùng backtracking.
Ví dụ:
Input: n=3Output: ['((()))', '(()())', '(())()', '()(())', '()()()']Xem đáp án
20. Chia list thành 2 phần có tổng gần bằng nhau
Dùng đệ quy để tìm cách chia một list số nguyên dương thành 2 phần sao cho hiệu tổng 2 phần là nhỏ nhất có thể.
Ví dụ:
Input: [1, 6, 11, 5]Output: 1Xem đáp án
Nhóm 4: Quy hoạch động (Dynamic Programming)
Phần tiêu đề “Nhóm 4: Quy hoạch động (Dynamic Programming)”Quy hoạch động (Dynamic Programming - DP) là kỹ thuật giải bài toán lớn bằng cách chia thành các bài toán con nhỏ hơn có tính chất lặp lại, giải từng bài toán con một lần rồi lưu lại kết quả (thường trong một mảng gọi là dp) để tái sử dụng thay vì tính lại nhiều lần.
21. Fibonacci với Memoization
Tối ưu hàm tính số Fibonacci thứ n bằng kỹ thuật ghi nhớ (memoization) để tránh tính lại nhiều lần.
Ví dụ:
Input: n=50Output: 12586269025Xem đáp án
22. Fibonacci Bottom-up
Tính số Fibonacci thứ n bằng quy hoạch động kiểu bottom-up (dùng vòng lặp, không đệ quy).
Ví dụ:
Input: n=30Output: 832040Xem đáp án
23. Bài toán cái túi 0/1 (0/1 Knapsack)
Cho n món đồ, mỗi món có trọng lượng và giá trị, và một túi có sức chứa capacity. Tìm giá trị lớn nhất có thể mang được (mỗi món chỉ lấy 0 hoặc 1 lần).
Ví dụ:
Input: weights=[1, 3, 4, 5], values=[1, 4, 5, 7], capacity=7Output: 9Xem đáp án
24. Dãy con chung dài nhất (Longest Common Subsequence)
Tìm độ dài dãy con chung dài nhất giữa 2 chuỗi.
Ví dụ:
Input: s1="ABCBDAB", s2="BDCABA"Output: 4Xem đáp án
25. Khoảng cách chỉnh sửa (Edit Distance)
Tính số phép biến đổi tối thiểu (thêm, xóa, sửa 1 ký tự) để biến chuỗi s1 thành chuỗi s2.
Ví dụ:
Input: s1="kitten", s2="sitting"Output: 3Xem đáp án
26. Đổi tiền tối ưu (Coin Change)
Cho một list mệnh giá tiền xu và một số tiền amount. Tìm số lượng xu tối thiểu để tạo thành amount (trả về -1 nếu không thể).
Ví dụ:
Input: coins=[1, 2, 5], amount=11Output: 3Xem đáp án
27. Dãy con tăng dài nhất (Longest Increasing Subsequence)
Tìm độ dài dãy con tăng dần dài nhất trong một list số.
Ví dụ:
Input: [10, 9, 2, 5, 3, 7, 101, 18]Output: 4Xem đáp án
28. Tổng dãy con lớn nhất (Kadane’s Algorithm)
Tìm tổng lớn nhất của một dãy con liên tiếp trong list số (có thể có số âm).
Ví dụ:
Input: [-2, 1, -3, 4, -1, 2, 1, -5, 4]Output: 6Xem đáp án
29. Leo cầu thang (Climbing Stairs)
Có n bậc cầu thang, mỗi bước bạn có thể leo 1 hoặc 2 bậc. Đếm số cách khác nhau để leo lên đến bậc thứ n.
Ví dụ:
Input: n=5Output: 8Xem đáp án
30. Kẻ trộm nhà (House Robber)
Một tên trộm không thể trộm 2 nhà liền kề nhau. Cho list giá trị tiền ở mỗi nhà, tìm số tiền tối đa có thể trộm được.
Ví dụ:
Input: [2, 7, 9, 3, 1]Output: 12Xem đáp án
Nhóm 5: Cấu trúc dữ liệu
Phần tiêu đề “Nhóm 5: Cấu trúc dữ liệu”31. Stack (Ngăn xếp)
Cài đặt cấu trúc dữ liệu Stack bằng class, hỗ trợ push, pop, peek, is_empty.
Xem đáp án
32. Kiểm tra ngoặc hợp lệ (dùng Stack)
Dùng Stack để kiểm tra một chuỗi ngoặc (gồm (), [], {}) có hợp lệ (đóng mở đúng thứ tự) hay không.
Ví dụ:
Input: "({[]})"Output: True
Input: "([)]"Output: FalseXem đáp án
33. Queue (Hàng đợi) bằng deque
Cài đặt cấu trúc dữ liệu Queue bằng collections.deque, hỗ trợ enqueue, dequeue.
Xem đáp án
34. Linked List đơn giản
Cài đặt Linked List (danh sách liên kết đơn) với các thao tác append và print_list.
Xem đáp án
35. Đảo ngược Linked List
Viết hàm đảo ngược một Linked List (dùng lại class Node/LinkedList ở bài trước).
Xem đáp án
36. Binary Tree - Duyệt cây
Cài đặt cây nhị phân đơn giản và viết 3 hàm duyệt: preorder, inorder, postorder.
Xem đáp án
37. Tính chiều cao cây nhị phân
Viết hàm đệ quy tính chiều cao (số tầng) của một cây nhị phân.
Ví dụ: cây 1 có con trái 2 (con trái là 4) và con phải 3.
Output: 3Xem đáp án
38. Kiểm tra cây đối xứng (Symmetric Tree)
Kiểm tra một cây nhị phân có đối xứng qua trục dọc hay không.
Ví dụ: cây gốc 1, con trái 2 (con trái 3, con phải 4), con phải 2 (con trái 4, con phải 3).
Output: TrueXem đáp án
39. Duyệt cây theo tầng (Level Order / BFS)
Duyệt cây nhị phân theo từng tầng, in ra danh sách giá trị của mỗi tầng.
Ví dụ: cây gốc 3, con trái 9, con phải 20 (con trái 15, con phải 7).
Output: [[3], [9, 20], [15, 7]]Xem đáp án
40. Binary Search Tree - Thêm và tìm kiếm
Cài đặt cây tìm kiếm nhị phân (BST) với thao tác insert và search.
Ví dụ:
Input: insert [50, 30, 70, 20, 40, 60, 80] rồi search(40)Output: True
Input: search(100)Output: FalseXem đáp án
Nhóm 6: Lập trình hướng đối tượng (OOP) nâng cao
Phần tiêu đề “Nhóm 6: Lập trình hướng đối tượng (OOP) nâng cao”Xem thêm lý thuyết: Classes và Objects, Kế thừa (Inheritance), Đa hình (Polymorphism), Đóng gói (Encapsulation), Special Methods (Magic Methods), Constructor và Methods.
41. Kế thừa (Inheritance)
Viết class Animal với phương thức speak(), sau đó viết class Dog và Cat kế thừa từ Animal và ghi đè (override) phương thức speak().
Xem đáp án
42. Đa hình (Polymorphism)
Viết một hàm calculate_area(shape) nhận vào các đối tượng hình học khác nhau (Square, Circle) và gọi đúng phương thức area() tương ứng nhờ đa hình.
Xem đáp án
43. Encapsulation (property, getter/setter)
Viết class BankAccount với thuộc tính _balance được bảo vệ, dùng @property để đọc và @balance.setter để kiểm tra không cho set số dư âm.
Xem đáp án
44. Static Method và Class Method
Viết class MathUtils có 1 @staticmethod tính bình phương và 1 @classmethod tạo đối tượng Point từ chuỗi "x,y".
Xem đáp án
45. __str__ và __repr__
Viết class Product với __str__ (hiển thị thân thiện cho người dùng) và __repr__ (hiển thị cho lập trình viên/debug).
Xem đáp án
46. Nạp chồng toán tử (Operator Overloading)
Viết class Vector2D biểu diễn vector 2 chiều, nạp chồng toán tử +, - và ==.
Xem đáp án
47. Abstract Base Class
Dùng module abc để tạo class trừu tượng Shape với phương thức trừu tượng perimeter(), ép các class con phải cài đặt phương thức này.
Xem đáp án
48. Dataclass
Dùng @dataclass để viết class Employee gọn hơn, tự động có __init__, __repr__ và __eq__.
Xem đáp án
49. So sánh đối tượng (__eq__, __lt__) để sắp xếp
Viết class Student cài đặt __eq__ và __lt__ để có thể dùng trực tiếp sorted() theo điểm số.
Xem đáp án
50. Singleton Pattern
Cài đặt mẫu thiết kế Singleton đơn giản, đảm bảo một class chỉ có duy nhất 1 đối tượng được tạo ra.
Xem đáp án
Nhóm 7: Closures, Decorators, Generators
Phần tiêu đề “Nhóm 7: Closures, Decorators, Generators”Closure là khi một hàm con “nhớ” được các biến trong hàm cha bao quanh nó, ngay cả sau khi hàm cha đã chạy xong (xem ví dụ ở bài 51). Xem thêm lý thuyết: Decorators (Hàm trang trí), Generators và Iterators, Context Managers (with statement).
51. Closure - Bộ đếm
Viết một closure make_counter() trả về hàm count() mỗi lần gọi sẽ tăng và trả về một biến đếm được “nhớ” bên trong closure.
Xem đáp án
52. Decorator đo thời gian chạy hàm
Viết decorator @timer in ra thời gian thực thi của hàm được trang trí.
Xem đáp án
53. Decorator ghi log
Viết decorator @log_calls in ra tên hàm cùng tham số truyền vào mỗi khi hàm được gọi.
Xem đáp án
54. Decorator tự động thử lại (Retry)
Viết decorator @retry(times) tự động gọi lại hàm tối đa times lần nếu hàm ném ra exception.
Xem đáp án
55. Generator sinh dãy Fibonacci
Viết một generator function fibonacci_gen() sinh vô hạn các số Fibonacci, dùng yield.
Xem đáp án
56. Generator đọc dữ liệu lớn theo từng dòng
Viết generator read_file_lines(file_path) đọc file lớn từng dòng một, tránh load toàn bộ file vào bộ nhớ.
Xem đáp án
57. yield from
Viết generator chain_generators(gen1, gen2) dùng yield from để nối 2 generator lại thành 1 chuỗi giá trị liên tục.
Xem đáp án
58. Generator Expression vs List Comprehension
Viết cùng 1 phép tính bình phương các số từ 1 đến 1 triệu bằng cả list comprehension và generator expression, so sánh kích thước bộ nhớ bằng sys.getsizeof.
Xem đáp án
59. Decorator cache kết quả (tự viết memoization)
Viết decorator @cache_result tự lưu lại kết quả các lần gọi hàm trước đó, tránh tính toán lại (không dùng functools.lru_cache).
Xem đáp án
60. Context Manager tự viết (class)
Viết một class context manager FileOpener (cài đặt __enter__ và __exit__) để dùng với cú pháp with.
Xem đáp án
Nhóm 8: Lập trình hàm (Functional Programming)
Phần tiêu đề “Nhóm 8: Lập trình hàm (Functional Programming)”61. reduce tính tổng và tích
Dùng functools.reduce để tính tổng và tích các phần tử của một list số.
Xem đáp án
62. Kết hợp map và filter
Cho một list chuỗi số, dùng filter để loại các chuỗi không phải số, dùng map để chuyển các chuỗi còn lại thành int và nhân đôi giá trị.
Xem đáp án
63. functools.partial
Dùng functools.partial để tạo ra một hàm mới từ hàm nhan(a, b) với a đã được cố định sẵn.
Xem đáp án
64. itertools.combinations
Dùng itertools.combinations để in ra tất cả tổ hợp chập 2 của một list.
Xem đáp án
65. itertools.permutations
Dùng itertools.permutations để in ra tất cả hoán vị của một list 3 phần tử.
Xem đáp án
66. itertools.groupby
Cho một list số đã sắp xếp, dùng itertools.groupby để nhóm các số theo tính chẵn/lẻ.
Xem đáp án
67. sorted với key phức tạp
Cho một list các tuple (ten, tuoi). Sắp xếp theo độ dài tên tăng dần, nếu bằng nhau thì theo tuổi giảm dần.
Xem đáp án
68. any và all nâng cao
Cho một list các list con điểm số, dùng any/all kết hợp generator expression để kiểm tra: (1) có học sinh nào toàn điểm 10 không, (2) tất cả học sinh có ít nhất 1 điểm trên 8 không.
Xem đáp án
Nhóm 9: Module chuẩn hữu ích
Phần tiêu đề “Nhóm 9: Module chuẩn hữu ích”Xem thêm lý thuyết: Date and Time (datetime module), Regular Expressions, Làm việc với JSON.
69. collections.Counter - Ký tự phổ biến nhất
Dùng Counter để tìm ra 3 ký tự xuất hiện nhiều nhất trong một chuỗi.
Xem đáp án
70. collections.defaultdict - Nhóm dữ liệu
Cho một list các tuple (name, subject). Dùng defaultdict để nhóm danh sách môn học theo từng người.
Xem đáp án
71. collections.namedtuple
Dùng namedtuple để tạo kiểu dữ liệu Point (có x, y) gọn nhẹ hơn class thông thường.
Xem đáp án
72. datetime - Tính số ngày giữa 2 mốc thời gian
Dùng module datetime để tính số ngày giữa 2 ngày cho trước.
Xem đáp án
73. re - Kiểm tra định dạng email
Dùng module re (regular expression) để kiểm tra một chuỗi có đúng định dạng email cơ bản hay không, có cho nhập lại nếu sai định dạng.
Xem đáp án
74. re - Trích xuất số điện thoại
Dùng re.findall để trích xuất tất cả số điện thoại (dạng 10 chữ số) xuất hiện trong một đoạn văn bản.
Xem đáp án
75. json - Đọc và ghi dữ liệu JSON
Dùng module json để lưu một dictionary vào file .json, sau đó đọc lại và in ra.
Xem đáp án
76. os / pathlib - Liệt kê file trong thư mục
Dùng pathlib để liệt kê tất cả các file có đuôi .txt trong thư mục hiện tại.
Xem đáp án
77. random - Chọn ngẫu nhiên không trùng
Dùng random.sample để chọn ngẫu nhiên 5 số không trùng nhau từ 1 đến 45 (giống quay số trúng thưởng).
Xem đáp án
78. statistics - Thống kê cơ bản
Dùng module statistics để tính trung bình cộng, trung vị (median) và độ lệch chuẩn (standard deviation) của một list điểm số.
Xem đáp án
Nhóm 10: Xử lý ngoại lệ nâng cao
Phần tiêu đề “Nhóm 10: Xử lý ngoại lệ nâng cao”Xem thêm lý thuyết: Exception Handling (Try/Except).
79. Phân cấp Exception tùy chỉnh
Tạo một hệ thống exception phân cấp cho việc rút tiền ngân hàng: AccountError (lớp cha), InsufficientBalanceError và InvalidAmountError (kế thừa từ lớp cha).
Xem đáp án
80. Chained Exception (raise ... from ...)
Viết chương trình đọc số từ chuỗi, khi gặp lỗi định dạng thì ném ra một exception mới nhưng vẫn giữ lại nguyên nhân gốc bằng raise ... from ....
Xem đáp án
81. Context Manager xử lý lỗi (__exit__ trả về True)
Viết context manager SuppressError cho phép bỏ qua một loại exception cụ thể xảy ra bên trong khối with.
Xem đáp án
82. finally luôn được thực thi
Viết chương trình minh họa khối finally luôn chạy dù có exception hay không, hay dù có return sớm trong hàm.
Xem đáp án
83. Validate dữ liệu nhập với nhiều loại lỗi
Viết hàm input_age() yêu cầu người dùng nhập tuổi, bắt cả lỗi ValueError (không phải số) lẫn lỗi tuổi không hợp lệ (âm hoặc quá lớn), cho nhập lại đến khi hợp lệ.
Xem đáp án
Nhóm 11: Thuật toán số học & ma trận nâng cao
Phần tiêu đề “Nhóm 11: Thuật toán số học & ma trận nâng cao”84. Sàng Eratosthenes
Cài đặt thuật toán Sàng Eratosthenes để tìm tất cả số nguyên tố nhỏ hơn n, hiệu quả hơn nhiều so với kiểm tra từng số.
Ví dụ:
Input: n=20Output: [2, 3, 5, 7, 11, 13, 17, 19]Xem đáp án
85. Nhân 2 ma trận
Viết hàm nhân 2 ma trận (list 2 chiều) với nhau, không dùng thư viện ngoài.
Ví dụ:
Input: a=[[1,2],[3,4]], b=[[5,6],[7,8]]Output: [[19, 22], [43, 50]]Xem đáp án
86. Chuyển vị ma trận (Transpose)
Viết hàm chuyển vị một ma trận (đổi hàng thành cột), không dùng thư viện ngoài.
Ví dụ:
Input: [[1, 2, 3], [4, 5, 6]]Output: [[1, 4], [2, 5], [3, 6]]Xem đáp án
87. Xoay ma trận vuông 90 độ
Viết hàm xoay một ma trận vuông 90 độ theo chiều kim đồng hồ, không dùng bộ nhớ phụ (in-place).
Ví dụ:
Input: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]Output: [[7, 4, 1], [8, 5, 2], [9, 6, 3]]Xem đáp án
88. Kiểm tra số chính phương không dùng sqrt
Kiểm tra một số nguyên dương có phải là số chính phương hay không, dùng thuật toán tìm kiếm nhị phân thay vì math.sqrt.
Ví dụ:
Input: 16Output: True
Input: 18Output: FalseXem đáp án
89. Số nguyên tố Mersenne
Số Mersenne có dạng 2p - 1. Viết chương trình kiểm tra với p là số nguyên tố, số Mersenne tương ứng có phải cũng là số nguyên tố hay không.
Xem đáp án
Nhóm 12: Đồ thị cơ bản (Graph)
Phần tiêu đề “Nhóm 12: Đồ thị cơ bản (Graph)”BFS (Breadth-First Search - duyệt theo chiều rộng) thăm hết các đỉnh gần nhất trước, giống lan ra từng vòng tròn đồng tâm. DFS (Depth-First Search - duyệt theo chiều sâu) đi sâu theo một nhánh đến hết mức có thể rồi mới quay lại thử nhánh khác. Đồ thị trong nhóm bài này được biểu diễn bằng adjacency list (danh sách kề): một dictionary mà mỗi key là 1 đỉnh, value là list các đỉnh kề với nó.
90. Duyệt đồ thị theo chiều rộng (BFS)
Cho một đồ thị biểu diễn bằng dictionary (adjacency list), duyệt đồ thị theo chiều rộng (BFS) bắt đầu từ 1 đỉnh.
Ví dụ:
Input: graph={"A":["B","C"],"B":["A","D","E"],"C":["A","F"],"D":["B"],"E":["B","F"],"F":["C","E"]}, start="A"Output: ['A', 'B', 'C', 'D', 'E', 'F']Xem đáp án
91. Duyệt đồ thị theo chiều sâu (DFS) đệ quy
Với đồ thị ở bài trước, viết hàm duyệt theo chiều sâu (DFS) bằng đệ quy.
Ví dụ:
Input: graph (như bài 90), start="A"Output: ['A', 'B', 'D', 'E', 'F', 'C']Xem đáp án
92. Kiểm tra đồ thị vô hướng có chu trình
Kiểm tra một đồ thị vô hướng (dạng adjacency list) có tồn tại chu trình (cycle) hay không, dùng DFS.
Ví dụ:
Input: {"A": ["B"], "B": ["A", "C"], "C": ["B", "A"]}Output: True
Input: {"A": ["B"], "B": ["A", "C"], "C": ["B"]}Output: FalseXem đáp án
93. Đường đi ngắn nhất không trọng số (BFS)
Tìm đường đi ngắn nhất (số bước ít nhất) giữa 2 đỉnh trong đồ thị không trọng số, dùng BFS.
Ví dụ:
Input: graph={"A":["B","C"],"B":["A","D"],"C":["A","D"],"D":["B","C","E"],"E":["D"]}, start="A", end="E"Output: ['A', 'B', 'D', 'E']Xem đáp án
94. Đếm số thành phần liên thông (Connected Components)
Đếm số thành phần liên thông trong một đồ thị vô hướng có thể không liên thông hoàn toàn.
Ví dụ:
Input: {"A": ["B"], "B": ["A"], "C": ["D"], "D": ["C"], "E": []}Output: 3Xem đáp án
95. Sắp xếp Topo (Topological Sort)
Với một đồ thị có hướng không có chu trình (DAG), sắp xếp các đỉnh theo thứ tự topo bằng thuật toán Kahn (dùng bậc vào - in-degree).
Ví dụ:
Input: {"ao": ["quan"], "quan": ["giay"], "vo": ["quan"], "giay": []}Output: ['ao', 'vo', 'quan', 'giay']Xem đáp án
Nhóm 13: Kiểm thử (Testing)
Phần tiêu đề “Nhóm 13: Kiểm thử (Testing)”96. Unit Test với unittest
Viết hàm cong(a, b) và một bộ test dùng module unittest để kiểm tra hàm hoạt động đúng.
Xem đáp án
97. Kiểm tra hàm bằng assert
Viết hàm is_palindrome(s) và dùng các câu lệnh assert để tự kiểm tra nhanh các trường hợp cơ bản.
Xem đáp án
Nhóm 14: Lập trình đồng thời (Concurrency) cơ bản
Phần tiêu đề “Nhóm 14: Lập trình đồng thời (Concurrency) cơ bản”98. threading - Chạy song song đơn giản
GIL (Global Interpreter Lock) là cơ chế trong CPython chỉ cho phép 1 luồng (thread) thực thi code Python tại một thời điểm, nên nhiều thread không thực sự chạy song song mà chỉ xen kẽ nhau rất nhanh. Dùng module threading để chạy 2 tác vụ “song song” theo kiểu này, so sánh với chạy tuần tự.
Xem đáp án
99. multiprocessing - Tính tổng song song
Dùng module multiprocessing để chia một list số lớn thành nhiều phần, tính tổng từng phần song song trên nhiều tiến trình (process), rồi cộng kết quả lại.
Xem đáp án
100. Mô phỏng nhiều tác vụ chờ với concurrent.futures
Dùng concurrent.futures.ThreadPoolExecutor để tải “giả lập” 5 trang web cùng lúc (mỗi trang mất 1 giây), thay vì tải tuần tự mất 5 giây.
Xem đáp án
Bạn đã hoàn thành cả 100 bài cơ bản và 100 bài nâng cao? Quay lại trang Bài tập lập trình - Cơ bản để ôn lại, hoặc thử sức với các bài tập theo từng chủ đề riêng ở sidebar bên trái.
Muốn thử sức với các bài toán phong cách phỏng vấn/LeetCode? Ghé qua Bài tập lập trình - Luyện thuật toán với 200 bài từ Dễ đến Khó, có ví dụ minh họa và ràng buộc chi tiết cho từng bài.