CodeHub
Khóa Học Nền Tảng Khoa Học Máy Tính & Lập Trình

Nhập Môn Lập Trình

Xây dựng tư duy giải quyết vấn đề logic, nắm vững nguyên lý hoạt động của máy tính, hệ thống kiểu dữ liệu, các cấu trúc điều khiển luồng, kỹ thuật hàm & đệ quy, cấu trúc dữ liệu mảng/ma trận, đánh giá độ phức tạp Big-O, giải thuật tìm kiếm/sắp xếp và quản lý bộ nhớ con trỏ với ngôn ngữ song song C++ và Python.

14 Bài học chuyên sâu 56 Bài tập trắc nghiệm nhanh Đề thi tốt nghiệp 20 câu So sánh song song C++ & Python
Khoa học máy tính cơ bản 45 phút
#bai-1

Bài 1: Tư duy máy tính, Hệ nhị phân & Kiến trúc máy tính

Mục tiêu bài học: Nắm vững 4 trụ cột của tư duy tính toán, hiểu bản chất hệ nhị phân (Binary), cơ chế biểu diễn dữ liệu trong bộ nhớ, kiến trúc Von Neumann và sự khác biệt căn bản giữa Trình biên dịch (Compiler) và Trình thông dịch (Interpreter).

1. Bốn trụ cột của Tư duy tính toán (Computational Thinking)

Tư duy máy tính không đơn thuần là việc gõ code, mà là phương pháp tư duy giải quyết vấn đề logic có hệ thống để con người hoặc máy tính có thể thực thi một cách tối ưu.

1. Phân rã (Decomposition)

Chia nhỏ một bài toán phức tạp thành nhiều bài toán con độc lập, đơn giản hơn để lần lượt giải quyết.

2. Nhận diện mẫu (Pattern Recognition)

Tìm kiếm các điểm tương đồng, xu hướng lặp lại giữa các vấn đề nhằm tái sử dụng các giải pháp đã biết.

3. Trừu tượng hóa (Abstraction)

Lược bỏ các chi tiết thừa thãi, chỉ tập trung vào các đặc trưng cốt lõi quan trọng nhất của đối tượng.

4. Thiết kế thuật toán (Algorithm Design)

Xây dựng chuỗi các chỉ dẫn từng bước rõ ràng, hữu hạn, không mập mờ để giải quyết trọn vẹn bài toán.

2. Hệ đếm nhị phân (Binary) & Biểu diễn dữ liệu

Mọi thiết bị điện tử kỹ thuật số đều hoạt động dựa trên các bóng bán dẫn (Transistor) đóng vai trò như các công tắc đóng/ngắt (1 = Có điện thế cao, 0 = Điện thế thấp).

Hệ đếm Cơ số Tập ký tự Ví dụ giá trị 45 Ứng dụng trong CNTT
Thập phân (Decimal) 10 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 45 Giao tiếp thường nhật của con người
Nhị phân (Binary) 2 0, 1 101101₂ Mã máy, cổng logic điện tử, lưu trữ bit/byte
Thập lục phân (Hexadecimal) 16 0-9, A, B, C, D, E, F 2D₁₆ (0x2D) Địa chỉ ô nhớ RAM, mã màu Web (#RRGGBB), mã hóa Byte
Công thức tính giá trị số nhị phân 101101₂:
1 × 2⁵ + 0 × 2⁴ + 1 × 2³ + 1 × 2² + 0 × 2¹ + 1 × 2⁰ = 32 + 0 + 8 + 4 + 0 + 1 = 45 (Thập phân)

Đơn vị lưu trữ chuẩn: 1 Byte = 8 Bits. 1 KiB = 1024 Bytes. Kiểu số nguyên có dấu 32-bit (int) lưu từ -2³¹ đến 2³¹-1 theo phương pháp Bù hai (Two's complement).

3. Kiến trúc máy tính & Compiler vs Interpreter

Kiến trúc Von Neumann gồm 3 khối cốt lõi: CPU (Bộ vi xử lý), Bộ nhớ chính (RAM) và Thiết bị I/O. CPU thực hiện chu trình liên tục: Fetch (Tìm nạp lệnh) → Decode (Giải mã lệnh) → Execute (Thực thi) → Store (Ghi kết quả).

Trình biên dịch (Compiler) - Ví dụ: C++
  • • Quét toàn bộ mã nguồn (.cpp), kiểm tra cú pháp và dịch toàn bộ thành mã máy nhị phân (.exe hoặc file ELF) một lần duy nhất trước khi chạy.
  • • Ưu điểm: Tốc độ thực thi cực kỳ nhanh, tối ưu hóa phần cứng sâu, không cần mã nguồn khi phân phối.
  • • Nhược điểm: Thời gian build lâu hơn, mã máy phụ thuộc vào kiến trúc CPU đích (x86_64, ARM).
Trình thông dịch (Interpreter) - Ví dụ: Python
  • • Đọc, dịch và thực thi mã nguồn (.py) theo từng dòng lệnh một thông qua máy ảo PVM (Python Virtual Machine) dịch sang Bytecode (.pyc).
  • • Ưu điểm: Thử nghiệm nhanh, viết mã linh hoạt, chạy đa nền tảng dễ dàng không cần biên dịch lại.
  • • Nhược điểm: Tốc độ thực thi chậm hơn C++ từ 10 - 50 lần do chi phí thông dịch run-time.

Câu hỏi củng cố kiến thức (4 câu)

1 Trong 4 trụ cột của tư duy tính toán, kỹ năng 'lược bỏ các chi tiết thừa thãi, chỉ giữ lại những thông tin mấu chốt quan trọng nhất' được gọi là gì?

2 Giá trị của số nhị phân 101101₂ trong hệ thập phân (Decimal) là bao nhiêu?

3 Một byte trong kiến trúc máy tính tiêu chuẩn bao gồm bao nhiêu bit?

4 Điểm khác biệt cốt lõi nhất giữa Trình biên dịch (Compiler như C++) và Trình thông dịch (Interpreter như Python) là gì?

Cú pháp & Môi trường 45 phút
#bai-2

Bài 2: Môi trường lập trình & Chương trình đầu tiên (C++ vs Python)

Mục tiêu bài học: Hiểu giải phẫu chi tiết cấu trúc chương trình kinh điển 'Hello World' trong C++ và Python; nắm vững quy tắc đặt tên biến, phong cách viết code chuẩn (Coding Conventions) và văn hóa Clean Code.

1. Giải phẫu chương trình đầu tiên: C++ vs Python

Hãy quan sát và đối chiếu cấu trúc chương trình in dòng chữ Hello, World! giữa C++ (ngôn ngữ hướng cấu trúc tường minh) và Python (ngôn ngữ hướng biểu cảm, tối giản).

main.cpp (C++20) Compiler: g++
#include <iostream> // Thư viện nhập xuất chuẩn

using namespace std; // Sử dụng không gian tên std

int main() {
    // std::cout gửi dữ liệu ra màn hình console
    cout << "Hello, World!" << endl;
    
    return 0; // Trả về 0: Báo hệ điều hành chương trình kết thúc thành công
}
main.py (Python 3.12) Interpreter: python
# Trong Python, không cần khai báo thư viện nhập xuất, không cần hàm main bắt buộc
# Lệnh print tự động thêm ký tự xuống dòng (\n)

print("Hello, World!")

# Viết chuẩn module phong cách Python:
if __name__ == "__main__":
    print("Chương trình chạy trực tiếp!")
Quy tắc Thụt lề (Indentation) sống còn trong Python: C++ sử dụng cặp ngoặc nhọn { } để phân chia khối lệnh (Block Scope). Ngược lại, Python dùng khoảng trắng thụt dòng (chuẩn PEP 8 là đúng 4 dấu cách) để xác định khối lệnh. Thụt lề sai sẽ gây lỗi ngay lập tức: IndentationError!

2. Quy ước đặt tên (Naming Conventions) & Clean Code

Mã nguồn được đọc nhiều gấp 10 lần thời gian nó được viết ra. Việc tuân thủ quy ước đặt tên giúp dự án chuyên nghiệp, dễ bảo trì:

camelCase

Từ đầu viết thường, các từ sau viết hoa chữ cái đầu.

totalStudentCount Phổ biến cho biến/hàm C++
snake_case

Tất cả viết thường, phân cách bởi dấu gạch dưới.

student_total_score Chuẩn số 1 của Python (PEP 8)
PascalCase

Tất cả các từ đều viết hoa chữ cái đầu.

StudentManager Đặt tên Class, Struct
SCREAMING_SNAKE

Viết hoa toàn bộ, cách nhau bằng gạch dưới.

MAX_BUFFER_SIZE Dành riêng cho Hằng số

Câu hỏi củng cố kiến thức (4 câu)

1 Trong ngôn ngữ C++, hàm nào đóng vai trò là điểm bắt đầu thực thi bắt buộc của toàn bộ chương trình?

2 Ngôn ngữ Python dùng cơ chế gì để phân định các khối lệnh (Block of code) thay cho cặp ngoặc nhọn `{}` trong C++?

3 Ký tự kết thúc câu lệnh bắt buộc trong ngôn ngữ C++ là gì?

4 Theo quy ước viết mã chuẩn của Python (PEP 8), tên biến và tên hàm nên được đặt theo phong cách nào?

Kiểu dữ liệu & Bộ nhớ 50 phút
#bai-3

Bài 3: Biến, Hằng số & Hệ thống kiểu dữ liệu cơ bản

Mục tiêu bài học: Làm chủ bản chất biến trong bộ nhớ RAM, phân biệt ngôn ngữ định kiểu tĩnh (C++) và định kiểu động (Python), nắm vững các kiểu dữ liệu nguyên thủy, kỹ thuật ép kiểu và cách dùng hằng số.

1. Bản chất của Biến trong bộ nhớ RAM

Một biến (Variable) thực chất là tên gọi định danh cho một vùng ô nhớ xác định trên RAM. Khi khai báo biến, hệ điều hành cấp phát một số lượng byte tương ứng với kiểu dữ liệu của biến đó.

Kiểu dữ liệu (C++) Tương đương (Python) Kích thước (C++) Miền giá trị lưu trữ Ví dụ khai báo
int int 4 bytes (32-bit) -2.147.483.648 đến 2.147.483.647 (-2³¹ đến 2³¹-1) int age = 18;
long long int (arbitrary) 8 bytes (64-bit) ≈ -9 × 10¹⁸ đến 9 × 10¹⁸ (-2⁶³ đến 2⁶³-1) long long big_id = 9999999999LL;
double float 8 bytes (64-bit) Số thực dấu phẩy động độ chính xác kép (15-17 chữ số) double pi = 3.14159265;
char str (độ dài 1) 1 byte (8-bit) 256 ký tự trong bảng mã ASCII (0 đến 255) char grade = 'A';
bool bool 1 byte true (1) hoặc false (0) bool is_passed = true;
Điểm khác biệt thú vị giữa C++ và Python:
Trong C++, biến có kiểu tĩnh (Statically-typed), không thể gán chuỗi vào một biến kiểu int. Trong khi đó, Python là ngôn ngữ kiểu động (Dynamically-typed), kiểu của biến được xác định tự động dựa trên giá trị mà nó đang tham chiếu tại runtime.

2. Ép kiểu dữ liệu (Type Casting) & Hằng số (Constants)

Ép kiểu trong C++
int a = 7, b = 2;

// Phép chia nguyên: 7 / 2 = 3
double c1 = a / b; // c1 = 3.0 (SAI LOGIC!)

// Ép kiểu tường minh chuẩn C++: static_cast
double c2 = static_cast<double>(a) / b; // c2 = 3.5

// Khai báo Hằng số (Không thể thay đổi giá trị)
const double PI = 3.1415926535;
// PI = 3.14; // LỖI BIÊN DỊCH!
Ép kiểu trong Python
a = 7
b = 2

# Trong Python: / là chia thực, // là chia nguyên
c1 = a / b   # c1 = 3.5 (Tự động ra float)
c2 = a // b  # c2 = 3 (Chia lấy phần nguyên)

# Ép kiểu dữ liệu qua hàm dựng sẵn
s = "123"
num = int(s)      # Chuyển chuỗi sang số nguyên
flt = float(5)    # flt = 5.0

# Hằng số trong Python được viết HOA theo quy ước
PI = 3.1415926535

Câu hỏi củng cố kiến thức (4 câu)

1 Trong C++, biểu thức `7 / 2` sẽ trả về kết quả là bao nhiêu?

2 Kiểu dữ liệu `int` 32-bit trong C++ có thể lưu trữ giá trị số nguyên tối đa là bao nhiêu?

3 Từ khóa nào trong C++ được dùng để khai báo một biến có giá trị bất biến (Hằng số)?

4 Trong Python, kết quả của phép toán `17 // 5` là gì?

Toán tử & Nhập xuất 45 phút
#bai-4

Bài 4: Toán tử, Biểu thức & Nhập/Xuất chuẩn (I/O)

Mục tiêu bài học: Làm chủ các nhóm toán tử số học, quan hệ, logic; hiểu sâu cơ chế đoản mạch (Short-circuit evaluation), sự khác biệt tiền tố/hậu tố (++x vs x++), xử lý triệt để bẫy trôi lệnh khi nhập dữ liệu trong C++ và định dạng chuỗi hiện đại trong Python.

1. Các nhóm toán tử & Cơ chế đoản mạch (Short-circuit)

Toán tử là các ký hiệu thực hiện các phép biến đổi toán học hoặc logic trên một hoặc nhiều toán hạng.

Toán tử số học
  • + , - , *: Cộng, trừ, nhân
  • /: Chia (C++ chia nguyên nếu cả 2 int)
  • %: Chia lấy phần dư (Modulus)
  • ** (Python): Lũy thừa (vd: 2**10 = 1024)
Toán tử so sánh
  • ==: So sánh bằng (tránh nhầm với = gán!)
  • !=: So sánh khác nhau
  • > , >=: Lớn hơn, lớn hơn hoặc bằng
  • < , <=: Nhỏ hơn, nhỏ hơn hoặc bằng
Toán tử Logic
  • && (C++) / and (Python): VÀ logic
  • || (C++) / or (Python): HOẶC logic
  • ! (C++) / not (Python): PHỦ ĐỊNH logic
Phân biệt Tiền tố (++x) và Hậu tố (x++) trong C++: int x = 5; int y = ++x; → x tăng lên 6 trước, rồi gán cho y (y = 6).
int x = 5; int y = x++; → y nhận giá trị 5 trước, sau đó x mới tăng lên 6 (y = 5, x = 6).
Lưu ý: Python không có toán tử ++ hoặc --, lập trình viên sử dụng x += 1.

2. Nhập/Xuất chuẩn & Bẫy trôi lệnh trong C++

Xử lý Nhập/Xuất trong C++
int age;
string fullname;

cout << "Nhap tuoi: ";
cin >> age; // Khi gõ 18 rồi nhấn Enter, phím Enter '\n' còn lưu trong bộ nhớ đệm (buffer)

// BẮT BUỘC: Xóa ký tự '\n' còn sót lại để tránh trôi dòng!
cin.ignore(); 

cout << "Nhap ho ten day du: ";
getline(cin, fullname); // Đọc cả chuỗi có chứa dấu cách

cout << "Xin chao " << fullname << ", " << age << " tuoi!" << endl;
Xử lý Nhập/Xuất trong Python
# input() luôn trả về kiểu str (chuỗi ký tự)
# Cần ép kiểu sang int/float nếu muốn tính toán
age = int(input("Nhap tuoi: "))

# Nhập chuỗi có dấu cách hoàn toàn tự nhiên
fullname = input("Nhap ho ten: ")

# Định dạng chuỗi siêu tiện lợi bằng F-String:
print(f"Xin chao {fullname}, {age} tuoi!")

# Định dạng số thực 2 chữ số thập phân:
gpa = 8.66666
print(f"Diem GPA: {gpa:.2f}") # In ra: 8.67

Câu hỏi củng cố kiến thức (4 câu)

1 Đoạn mã C++ sau in ra màn hình giá trị của a và b là bao nhiêu? ```cpp int a = 10; int b = a++; cout << a << ' ' << b; ```

2 Cơ chế đoản mạch (Short-circuit evaluation) trong biểu thức logic `(A && B)` hoạt động như thế nào?

3 Trong C++, sau khi dùng lệnh `cin >> x;` để nhập số, trước khi dùng `getline(cin, str);` để đọc chuỗi ký tự, ta bắt buộc phải gọi lệnh nào để tránh bị trôi lệnh?

4 Trong Python, hàm `input()` luôn luôn trả về dữ liệu thuộc kiểu nào?

Điều khiển luồng 50 phút
#bai-5

Bài 5: Cấu trúc rẽ nhánh điều kiện (if - else, switch - case)

Mục tiêu bài học: Làm chủ cấu trúc rẽ nhánh if, if-else, if-else if; câu lệnh lựa chọn switch-case (C++) và match-case (Python 3.10+); vận dụng toán tử 3 ngôi (Ternary Operator) để tối ưu hóa mã nguồn gọn gàng.

1. Cấu trúc rẽ nhánh đa trường hợp

Rẽ nhánh cho phép máy tính đưa ra quyết định thực thi các đoạn mã khác nhau tùy thuộc vào điều kiện đúng hay sai.

C++ if - else if - else
double score = 8.5;

if (score >= 9.0) {
    cout << "Xuat sac";
} else if (score >= 8.0) {
    cout << "Gioi";
} else if (score >= 6.5) {
    cout << "Kha";
} else {
    cout << "Trung binh / Yeu";
}

// Toán tử 3 ngôi (Ternary Operator):
string result = (score >= 5.0) ? "Dat" : "Truot";
Python if - elif - else
score = 8.5

if score >= 9.0:
    print("Xuat sac")
elif score >= 8.0:
    print("Gioi")
elif score >= 6.5:
    print("Kha")
else:
    print("Trung binh / Yeu")

# Toán tử 3 ngôi trong Python:
result = "Dat" if score >= 5.0 else "Truot"

2. Switch-case trong C++ vs Match-case trong Python

Khi kiểm tra một biến với nhiều giá trị rời rạc cụ thể (như mã menu, ngày trong tuần, lệnh bàn phím), việc dùng switch-case giúp mã nguồn trong sáng hơn nhiều chuỗi if-else dài dòng:

switch - case (C++)
int day = 3;
switch (day) {
    case 2: cout << "Thu Hai"; break;
    case 3: cout << "Thu Ba";  break;
    case 4: cout << "Thu Tu";  break;
    default:
        cout << "Ngay khong hop le";
}
// LƯU Ý: Quên lệnh 'break' sẽ gây lỗi Fallthrough (chạy tuột xuống case dưới)!
match - case (Python 3.10+)
day = 3
match day:
    case 2:
        print("Thu Hai")
    case 3:
        print("Thu Ba")
    case 4:
        print("Thu Tu")
    case _:
        print("Ngay khong hop le")
# Ký tự '_' đóng vai trò tương đương default

Câu hỏi củng cố kiến thức (4 câu)

1 Điều gì sẽ xảy ra trong lệnh `switch-case` của C++ nếu lập trình viên quên viết lệnh `break;` ở cuối một nhánh `case`?

2 Trong C++, kiểu dữ liệu của biến truyền vào lệnh `switch(expr)` bắt buộc phải là kiểu gì?

3 Trong Python, từ khóa nào được dùng cho nhánh 'nếu không thì nếu' (else if)?

4 Biểu thức toán tử 3 ngôi `x = (a > b) ? a : b;` tương đương với cấu trúc điều khiển nào?

Vòng lặp & Luồng lặp 55 phút
#bai-6

Bài 6: Cấu trúc vòng lặp & Lặp lồng nhau (while, for, do-while)

Mục tiêu bài học: Làm chủ 3 cấu trúc vòng lặp kinh điển (for, while, do-while), phân biệt cơ chế ngắt luồng break và continue, nắm vững kỹ thuật vòng lặp lồng nhau để xử lý ma trận và vẽ hình học.

1. Các dạng vòng lặp cơ bản: for, while & do-while

Vòng lặp cho phép tái sử dụng đoạn mã nhiều lần cho đến khi một điều kiện dừng được thỏa mãn.

Vòng lặp trong C++
// 1. Vòng lặp for: Biết trước số lần lặp
for (int i = 1; i <= 5; i++) {
    cout << i << " ";
} // In ra: 1 2 3 4 5

// 2. Vòng lặp while: Kiểm tra điều kiện trước
int n = 10;
while (n > 0) {
    n /= 2;
}

// 3. Vòng lặp do-while: Chạy thân vòng lặp ít nhất 1 lần
int pass;
do {
    cout << "Nhap mat khau (123): ";
    cin >> pass;
} while (pass != 123);
Vòng lặp trong Python
# 1. Vòng lặp for với hàm range(start, stop, step)
# Lưu ý: range(1, 6) chạy từ 1 đến 5 (không bao gồm 6!)
for i in range(1, 6):
    print(i, end=" ") # In ra: 1 2 3 4 5

# 2. Vòng lặp while
n = 10
while n > 0:
    n //= 2

# 3. Mô phỏng do-while bằng while True + break
while True:
    password = int(input("Nhap mat khau: "))
    if password == 123:
        break

2. Vòng lặp lồng nhau (Nested Loops) & Lệnh Break / Continue

Khi đặt một vòng lặp bên trong một vòng lặp khác, với mỗi 1 lần chạy của vòng lặp ngoài, vòng lặp trong sẽ chạy hết toàn bộ số vòng của nó. Kỹ thuật này dùng để duyệt bảng 2 chiều, vẽ hình tam giác sao, xử lý ma trận.

Ví dụ vẽ tam giác vuông sao:
for (int i = 1; i <= 4; i++) {
    for (int j = 1; j <= i; j++) cout << "* ";
    cout << "\n";
}
/* Kết quả in ra:
* 
* * 
* * * 
* * * * */

Câu hỏi củng cố kiến thức (4 câu)

1 Điểm đặc trưng nhất của vòng lặp `do-while` trong C++ so với `while` là gì?

2 Trong Python, hàm `range(2, 10, 3)` sẽ sinh ra dãy các số nguyên nào?

3 Câu lệnh `continue` có tác dụng gì khi được gọi bên trong thân một vòng lặp?

4 Đoạn mã C++ sau thực hiện bao nhiêu lần in ký tự '*' ra màn hình? ```cpp for (int i = 0; i < 3; i++) for (int j = 0; j < 4; j++) cout << '*'; ```

Hàm & Mô-đun hóa 55 phút
#bai-7

Bài 7: Hàm, Tham số & Cơ chế truyền dữ liệu (Functions & Scope)

Mục tiêu bài học: Nắm vững nguyên lý module hóa và nguyên tắc DRY; phân biệt sâu sắc giữa truyền tham trị (Pass by Value) và truyền tham chiếu (Pass by Reference); hiểu tường minh tầm vực biến (Scope) và nạp chồng hàm (Function Overloading).
0

Câu hỏi củng cố kiến thức (4 câu)

1 Ký tự nào được dùng trong danh sách tham số hàm C++ để chỉ định cơ chế truyền tham chiếu (Pass by Reference)?

2 Đoạn mã C++ sau in ra giá trị của biến `x` là bao nhiêu? ```cpp void doubleValue(int n) { n = n * 2; } int main() { int x = 5; doubleValue(x); cout << x; } ```

3 Tính năng nạp chồng hàm (Function Overloading) trong C++ cho phép điều gì?

4 Biến được khai báo bên trong một khối lệnh hàm được gọi là loại biến gì và có vòng đời như thế nào?

Giải thuật đệ quy 55 phút
#bai-8

Bài 8: Đệ quy & Giải thuật đệ quy (Recursion & Call Stack)

Mục tiêu bài học: Hiểu sâu sắc bản chất hàm đệ quy, nguyên lý hoạt động của ngăn xếp lời gọi hàm (Call Stack), bẫy tràn ngăn xếp (Stack Overflow) và phương pháp giải quyết các bài toán kinh điển: Giai thừa, Fibonacci, Tháp Hà Nội.

1. Bản chất của Đệ quy & Call Stack

Đệ quy (Recursion) là kỹ thuật lập trình trong đó hàm tự gọi lại chính nó với không gian đầu vào thu nhỏ dần. Mọi hàm đệ quy đúng đắn bắt buộc phải có 2 thành phần:

1. Trường hợp cơ sở (Base Case / Điểm dừng) Điều kiện bắt buộc để hàm dừng việc tự gọi lại và bắt đầu trả giá trị ngược về. Thiếu Base Case sẽ gây ra lặp vô tận và làm sập chương trình do Tràn bộ nhớ ngăn xếp (Stack Overflow).
2. Bước đệ quy (Recursive Step) Phần thân hàm gọi lại chính nó nhưng với tham số nhỏ hơn, đảm bảo tiến gần dần về trường hợp cơ sở sau mỗi bước.

2. Cài đặt các bài toán Đệ quy kinh điển

1. Tính Giai thừa n! (C++)
// Công thức: n! = n * (n-1)! ; 0! = 1
long long factorial(int n) {
    // 1. Base case
    if (n <= 1) return 1;
    
    // 2. Recursive step
    return n * factorial(n - 1);
}
2. Dãy số Fibonacci (Python)
# F(0)=0, F(1)=1, F(n) = F(n-1) + F(n-2)
def fibonacci(n: int) -> int:
    if n <= 0:
        return 0
    elif n == 1:
        return 1
    return fibonacci(n - 1) + fibonacci(n - 2)
# CẢNH BÁO: Đệ quy Fibonacci ngây thơ có độ phức tạp O(2^n)!

Câu hỏi củng cố kiến thức (4 câu)

1 Hiện tượng 'Stack Overflow' (Tràn ngăn xếp) trong giải thuật đệ quy xảy ra do nguyên nhân phổ biến nào?

2 Giá trị trả về của hàm đệ quy tính giai thừa với đầu vào `n = 4` là bao nhiêu?

3 Số phép gọi đệ quy lặp lại trong thuật toán tính số Fibonacci ngây thơ `F(n) = F(n-1) + F(n-2)` khiến độ phức tạp thời gian tăng theo cấp số nào?

4 Bài toán kinh điển 'Tháp Hà Nội' (Tower of Hanoi) với n chiếc đĩa cần tối thiểu bao nhiêu bước di chuyển hợp lệ?

Cấu trúc dữ liệu tuyến tính 55 phút
#bai-9

Bài 9: Mảng 1 chiều, Vector & Xử lý chuỗi (Arrays, Vectors & Strings)

Mục tiêu bài học: Làm chủ cấu trúc mảng tĩnh 1 chiều trong C++, mảng động std::vector, danh sách List và các kỹ thuật xử lý chuỗi ký tự (cắt lát Slicing, tìm kiếm, nối chuỗi) trong C++ và Python.

1. Mảng tĩnh (Array) vs Mảng động (Vector & List)

Mảng là cấu trúc dữ liệu lưu trữ một tập hợp các phần tử có cùng kiểu dữ liệu tại các ô nhớ liên tiếp nhau trên RAM. Chỉ số mảng luôn bắt đầu từ 0 (Zero-indexed).

std::vector trong C++ (Mảng động)
#include <vector>

vector<int> v = {10, 20, 30};
v.push_back(40); // Thêm phần tử vào cuối

// Duyệt mảng theo chỉ số O(1):
for (int i = 0; i < v.size(); i++) {
    cout << v[i] << " ";
}

// Duyệt kiểu Range-based for (C++11):
for (int x : v) {
    cout << x << " ";
}
List trong Python
nums = [10, 20, 30]
nums.append(40) # Thêm 40 vào cuối

# Duyệt phần tử trực tiếp:
for x in nums:
    print(x, end=" ")

# Các hàm tiện ích cực mạnh có sẵn:
print(len(nums))  # 4
print(max(nums))  # 40
print(sum(nums))  # 100

2. Xử lý Chuỗi ký tự (String Processing)

Chuỗi ký tự thực chất là mảng các ký tự. Python hỗ trợ cơ chế cắt lát (Slicing) cực kỳ linh hoạt:

# Kỹ thuật cắt lát chuỗi trong Python: s[start : stop : step]
s = "CodeHub Vietnam"
print(s[0:7]) # "CodeHub" (Lấy từ 0 đến 6)
print(s[8:]) # "Vietnam" (Lấy từ index 8 đến hết)
print(s[::-1]) # "manteiV buHedoC" (ĐẢO NGƯỢC CHUỖI chỉ với 1 dòng!)

Câu hỏi củng cố kiến thức (4 câu)

1 Thời gian truy xuất giá trị của một phần tử tại vị trí chỉ số `i` bất kỳ trong mảng (`a[i]`) có độ phức tạp là bao nhiêu?

2 Trong C++, phương thức nào của `std::vector` dùng để thêm một phần tử mới vào vị trí cuối cùng của mảng?

3 Trong Python, cú pháp cắt lát `s[::-1]` trên một chuỗi `s = 'PYTHON'` sẽ cho kết quả là gì?

4 Lỗi 'Array Index Out of Bounds' (Truy xuất ngoài phạm vi mảng) xảy ra khi nào?

Cấu trúc dữ liệu đa chiều 50 phút
#bai-10

Bài 10: Mảng 2 chiều, Ma trận & Mảng đa chiều (2D Arrays & Matrices)

Mục tiêu bài học: Hiểu bản chất lưu trữ theo dòng (Row-Major Order) của mảng 2 chiều trong RAM; làm chủ kỹ thuật duyệt ma trận, xử lý đường chéo chính/phụ và các bài toán cộng, chuyển vị, nhân ma trận.

1. Tổ chức mảng 2 chiều & Cơ chế Row-Major Order

Mảng 2 chiều được trực quan hóa như một bảng gồm $M$ hàng và $N$ cột. Tuy nhiên, bộ nhớ RAM là một dải ô nhớ tuyến tính 1 chiều, do đó máy tính tổ chức mảng 2 chiều theo thứ tự Row-Major Order (Lưu liên tiếp từng hàng một).

Địa chỉ ô nhớ: &a[i][j] = Base_Address + (i × N + j) × sizeof(kiểu_dữ_liệu)
Ma trận trong C++
int a[3][3] = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

// Tính tổng đường chéo chính (i == j):
int main_diag_sum = 0;
for (int i = 0; i < 3; i++) {
    main_diag_sum += a[i][i]; // 1 + 5 + 9 = 15
}
Ma trận trong Python
# Ma trận 3x3 biểu diễn qua List lồng List:
matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

# Khởi tạo ma trận rỗng RxC an toàn bằng List Comprehension:
R, C = 3, 3
grid = [[0] * C for _ in range(R)]
# TRÁNH: grid = [[0] * C] * R (sẽ gây lỗi sao chép tham chiếu!)

Câu hỏi củng cố kiến thức (4 câu)

1 Trong ma trận vuông kích thước N × N (với chỉ số hàng i và cột j từ 0 đến N-1), các phần tử nằm trên đường chéo chính thỏa mãn điều kiện toán học nào?

2 Trong ma trận vuông N × N, các phần tử nằm trên đường chéo phụ thỏa mãn điều kiện nào?

3 Một mảng 2 chiều kích thước 4 hàng và 5 cột (kiểu `int` 4 bytes) chiếm tổng cộng bao nhiêu byte bộ nhớ RAM?

4 Tại sao trong Python, việc khởi tạo ma trận 3 hàng 3 cột bằng lệnh `matrix = [[0] * 3] * 3` là một sai lầm nguy hiểm?

Phân tích giải thuật 55 phút
#bai-11

Bài 11: Đánh giá độ phức tạp thuật toán (Big-O Notation)

Mục tiêu bài học: Nắm vững khái niệm thời gian thực thi và không gian bộ nhớ; làm chủ ký pháp Big-O để định lượng hiệu năng thuật toán độc lập phần cứng; biết cách phân tích Big-O qua các vòng lặp và câu lệnh.

1. Ký pháp Big-O & Thang bậc độ phức tạp

Ký pháp Big-O ($O$) đo lường tốc độ tăng trưởng của số phép tính khi kích thước đầu vào $N$ tiến tới vô cực ($N ightarrow infty$). Nó biểu thị trường hợp xấu nhất (Worst-Case) để đảm bảo thuật toán không vượt quá giới hạn tài nguyên.

Ký hiệu Big-O Tên gọi Đánh giá hiệu năng Ví dụ thực tế
O(1) Thời gian hằng số Xuất sắc Truy xuất phần tử mảng theo chỉ số a[i], phép cộng trừ
O(log N) Thời gian Logarithm Rất tốt Tìm kiếm nhị phân (Binary Search), cây nhị phân cân bằng
O(N) Thời gian tuyến tính Tốt Duyệt mảng 1 lần, tìm kiếm tuyến tính, tính tổng
O(N log N) Tuyến tính log Chấp nhận được Sắp xếp tối ưu: Merge Sort, Quick Sort, Heap Sort
O(N²) Bình phương Chậm 2 vòng lặp lồng nhau, Bubble Sort, Selection Sort
O(2ⁿ) Hàm mũ Cực kỳ nguy hiểm Đệ quy Fibonacci ngây thơ, duyệt tổ hợp vét cạn

2. Quy tắc đơn giản hóa Big-O

1. Bỏ qua hằng số nhân: Đoạn mã chạy $5N$ phép toán hay $100N$ phép toán đều có cùng độ phức tạp tiệm cận là $O(N)$.
2. Giữ lại số hạng có bậc tăng trưởng cao nhất: Nếu một hàm tốn $N^2 + 500N + 1000$ thao tác, khi $N ightarrow infty$, thành phần $N^2$ lấn át toàn bộ, ta kết luận là $O(N^2)$.

Câu hỏi củng cố kiến thức (4 câu)

1 Độ phức tạp thời gian của đoạn mã lồng 2 vòng lặp sau là bao nhiêu? ```cpp for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { sum += a[i][j]; } } ```

2 Khi kích thước đầu vào N tăng gấp đôi, thời gian chạy của thuật toán có độ phức tạp O(1) sẽ thay đổi như thế nào?

3 Biểu thức độ phức tạp T(n) = 3n³ + 20n² + 500n + 10000 sau khi rút gọn theo ký pháp Big-O sẽ là:

4 Thuật toán tìm kiếm nhị phân (Binary Search) có độ phức tạp thời gian là gì?

Thuật toán cốt lõi 60 phút
#bai-12

Bài 12: Các thuật toán Tìm kiếm & Sắp xếp kinh điển (Searching & Sorting)

Mục tiêu bài học: Làm chủ thuật toán Tìm kiếm tuyến tính và Tìm kiếm nhị phân; so sánh chi tiết cơ chế hoạt động của các thuật toán sắp xếp kinh điển: Bubble Sort, Selection Sort, Insertion Sort và giới thiệu Merge Sort/Quick Sort.

1. Thuật toán Tìm kiếm: Tuyến tính vs Nhị phân

1. Tìm kiếm tuyến tính (Linear Search)

Quét tuần tự từng phần tử từ đầu đến cuối mảng cho đến khi tìm thấy khóa hoặc hết mảng.

  • • Độ phức tạp: O(N)
  • • Điều kiện áp dụng: Mọi mảng, không cần sắp xếp
2. Tìm kiếm nhị phân (Binary Search)

So sánh khóa với phần tử ở giữa; nếu nhỏ hơn, tìm ở nửa trái; nếu lớn hơn, tìm ở nửa phải.

  • • Độ phức tạp: O(log N) siêu nhanh (1 tỷ phần tử chỉ tốn ~30 bước!)
  • • Điều kiện bắt buộc: Mảng đã được sắp xếp
Cài đặt Binary Search chuẩn trong C++
int binarySearch(int a[], int n, int target) {
    int left = 0, right = n - 1;
    while (left <= right) {
        // Tránh tràn số int khi tính (left + right) / 2:
        int mid = left + (right - left) / 2;
        
        if (a[mid] == target) return mid; // Tìm thấy tại vị trí mid
        else if (a[mid] < target) left = mid + 1; // Tìm nửa phải
        else right = mid - 1; // Tìm nửa trái
    }
    return -1; // Không tìm thấy
}

2. Các thuật toán Sắp xếp cơ bản: Bubble, Selection, Insertion Sort

Thuật toán Ý tưởng cốt lõi Worst Case Best Case
Nổi bọt (Bubble Sort) Liên tục hoán đổi các cặp phần tử kề nhau bị sai thứ tự; phần tử lớn nhất 'nổi' về cuối. O(N²) O(N) (khi có cờ kiểm tra)
Chọn (Selection Sort) Tìm phần tử nhỏ nhất trong đoạn chưa sắp xếp, đổi chỗ với phần tử đầu đoạn đó. O(N²) O(N²)
Chèn (Insertion Sort) Lần lượt lấy từng phần tử chèn vào đúng vị trí trong dãy con đã sắp xếp phía trước (như xếp bài tây). O(N²) O(N) (mảng gần như đã sắp)

Câu hỏi củng cố kiến thức (4 câu)

1 Điều kiện tiên quyết bắt buộc để có thể áp dụng thuật toán Tìm kiếm nhị phân (Binary Search) là gì?

2 Với một mảng gồm 1.000.000 (1 triệu) phần tử đã sắp xếp, thuật toán Tìm kiếm nhị phân cần tối đa bao nhiêu lần so sánh để tìm thấy mục tiêu?

3 Thuật toán sắp xếp nào hoạt động dựa trên cơ chế 'liên tục tìm phần tử nhỏ nhất trong đoạn chưa sắp và hoán đổi về đầu'?

4 Thuật toán sắp xếp nâng cao nào sử dụng tư duy 'Chia để trị' (Divide and Conquer), chia đôi mảng rồi trộn lại với độ phức tạp luôn đảm bảo O(N log N)?

Bộ nhớ & Con trỏ 60 phút
#bai-13

Bài 13: Con trỏ & Quản lý bộ nhớ động (Pointers & Dynamic Memory)

Mục tiêu bài học: Làm chủ khái niệm địa chỉ ô nhớ RAM, toán tử con trỏ * và &, cơ chế cấp phát động (new / delete) trên vùng nhớ Heap vs Stack, hiểu cặn kẽ các lỗi chí mạng: Memory Leak, Dangling Pointer và cơ chế thu gom rác tự động của Python.

1. Bản chất của Con trỏ (Pointer) trong C++

Con trỏ là một biến đặc biệt mà giá trị của nó là địa chỉ ô nhớ của một biến khác trên RAM. Con trỏ đem lại sức mạnh can thiệp trực tiếp vào phần cứng, quản lý bộ nhớ động và tối ưu hóa hiệu năng tối đa.

Toán tử lấy địa chỉ: &x Trả về địa chỉ ô nhớ (dạng Hexadecimal, ví dụ 0x7ffee4b5) nơi biến x được lưu trữ.
Toán tử giải tham chiếu: *ptr Truy cập vào giá trị thực sự nằm tại ô nhớ mà con trỏ ptr đang trỏ đến.
Minh họa con trỏ C++
int x = 42;
int* ptr = &x; // ptr lưu địa chỉ của x

cout << "Gia tri x: " << x << endl;       // 42
cout << "Dia chi x: " << ptr << endl;     // 0x7ffee4b5...
cout << "Giai tham chieu: " << *ptr << endl; // 42

*ptr = 100; // Thay đổi giá trị tại ô nhớ, biến x đổi thành 100!
cout << "x sau khi doi: " << x << endl; // 100

2. Vùng nhớ Stack vs Heap & Rò rỉ bộ nhớ (Memory Leak)

Vùng nhớ Stack (Ngăn xếp) Hệ điều hành tự động cấp phát và thu hồi khi hàm kết thúc. Tốc độ truy xuất cực nhanh nhưng dung lượng nhỏ (thường chỉ vài MB). Các biến cục bộ mặc định nằm ở Stack.
Vùng nhớ Heap (Bộ nhớ động) Dung lượng rất lớn (bằng RAM trống của máy). Lập trình viên phải tự cấp phát bằng new và bắt buộc phải giải phóng bằng delete khi dùng xong.
Cảnh báo Rò rỉ bộ nhớ (Memory Leak):
Nếu bạn cấp phát int* a = new int[1000000]; mà quên gọi delete[] a;, bộ nhớ RAM đó sẽ bị giữ vô thời hạn cho đến khi tắt máy, làm ứng dụng ngốn RAM và sập hệ thống!
* Trong Python, cơ chế Garbage Collector (Bộ gom rác tự động) sẽ tự động thu hồi bộ nhớ của các đối tượng không còn biến nào tham chiếu tới.

Câu hỏi củng cố kiến thức (4 câu)

1 Toán tử nào trong C++ dùng để lấy địa chỉ ô nhớ của một biến trên RAM?

2 Lệnh nào sau đây dùng để giải phóng một mảng động được cấp phát bằng `int* arr = new int[100];` trong C++?

3 Lỗi 'Memory Leak' (Rò rỉ bộ nhớ) xảy ra do nguyên nhân nào?

4 Sau khi gọi `delete ptr;` trong C++, con trỏ `ptr` trở thành 'Dangling Pointer' (Con trỏ lơ lửng/hoang dã). Để đảm bảo an toàn, ta nên làm gì ngay lập tức?

Lập trình nâng cao & OOP 60 phút
#bai-14

Bài 14: Kiểu cấu trúc (Struct) & Bước đệm sang Lập trình hướng đối tượng (OOP)

Mục tiêu bài học: Tự tạo kiểu dữ liệu phức hợp với struct trong C++ và class trong Python; giải quyết bài toán thực tế quản lý danh sách sinh viên; nắm vững 4 trụ cột cơ bản của Lập trình hướng đối tượng (OOP).

1. Kiểu dữ liệu tự định nghĩa (Struct)

Các kiểu dữ liệu nguyên thủy (int, float, string) không thể mô tả trọn vẹn một thực thể thực tế như Sinh viên, Xe hơi, Tọa độ điểm. struct cho phép nhóm nhiều thuộc tính có kiểu dữ liệu khác nhau thành một kiểu duy nhất.

Struct trong C++
struct SinhVien {
    string maSV;
    string hoTen;
    double gpa;
};

int main() {
    SinhVien sv1 = {"SV001", "Nguyen Van A", 3.85};
    
    // Truy xuất thành viên qua toán tử dấu chấm '.'
    cout << sv1.hoTen << " - GPA: " << sv1.gpa;
    
    // Mảng các sinh viên:
    vector<SinhVien> danhSach;
    danhSach.push_back(sv1);
}
Class / Dataclass trong Python
from dataclasses import dataclass

@dataclass
class SinhVien:
    ma_sv: str
    ho_ten: str
    gpa: float

sv1 = SinhVien("SV001", "Nguyen Van A", 3.85)
print(f"{sv1.ho_ten} - GPA: {sv1.gpa}")

# Danh sách sinh viên:
danh_sach = [sv1]

2. Bốn trụ cột của Lập trình hướng đối tượng (OOP)

1. Đóng gói (Encapsulation) Gói gọn dữ liệu (thuộc tính) và hành vi (phương thức) vào bên trong đối tượng, che giấu dữ liệu nhạy cảm bằng phạm vi truy cập private.
2. Kế thừa (Inheritance) Cho phép lớp con tái sử dụng toàn bộ thuộc tính và phương thức của lớp cha, mở rộng thêm các tính năng đặc thù mà không cần viết lại mã nguồn.
3. Đa hình (Polymorphism) Các đối tượng thuộc các lớp khác nhau có thể phản hồi cùng một thông điệp (hàm) theo các cách khác nhau (Ghi đè phương thức Override).
4. Trừu tượng hóa (Abstraction) Cung cấp giao diện tương tác đơn giản cho người dùng (ví dụ: chân ga ô tô), ẩn giấu toàn bộ sự phức tạp cơ khí bên dưới động cơ.

Câu hỏi củng cố kiến thức (4 câu)

1 Toán tử nào trong C++ dùng để truy xuất đến một trường thành viên của một biến kiểu cấu trúc struct thông thường?

2 Nếu `svPtr` là một con trỏ trỏ đến một đối tượng `SinhVien`, cú pháp nào sau đây dùng để truy cập trường `gpa`?

3 Trụ cột nào trong OOP đề cập đến việc che giấu trạng thái nội bộ của đối tượng và ngăn chặn sự can thiệp trực tiếp từ bên ngoài?

4 Lợi ích cốt lõi của tính Kế thừa (Inheritance) trong lập trình hướng đối tượng là gì?