Trường ĐH Công nghệ – Đại học Quốc gia Hà Nội
Từ lúc thức dậy đến khi vào lớp, bạn đã “tìm kiếm” bao nhiêu lần?
Gần đây bạn đã sắp xếp những gì?
| Loại | Mô tả | Ví dụ |
|---|---|---|
| Tuần tự | Duyệt lần lượt từng phần tử. | Linear Search |
| Theo khoảng | Chia dãy thành các khoảng để thu hẹp vùng tìm kiếm. | Binary Search (trên dãy đã sắp xếp) |
Mảng chưa sắp xếp, cần tìm V = 40.
Mảng ban đầu:
Bước 1: So sánh V với phần tử đầu tiên.
Nếu V không trùng phần tử đầu tiên, ta chuyển sang phần tử tiếp theo và lặp lại quá trình cho đến khi tìm thấy hoặc duyệt hết mảng.
Khi tìm thấy phần tử bằng V, thuật toán trả về chỉ số của phần tử đó (ở đây là 3).
Tìm vị trí của V = 50.
Nếu phần tử cần tìm không xuất hiện trong danh sách, thuật toán thường trả về một giá trị đặc biệt để báo thất bại. Trong ví dụ này, giá trị trả về là -1 khi V không có trong mảng.
Trả về chỉ số của lần xuất hiện đầu tiên, hoặc -1 nếu không có.
# Tìm kiếm tuyến tính – Độ phức tạp O(n)
def linear_search(arr, value):
for i in range(len(arr)):
if arr[i] == value: # tìm thấy phần tử
return i
return -1 # không tìm thấy
⚠️ Tệ nhất phải kiểm tra n phần tử.
Trả về danh sách các chỉ số của các phần tử phù hợp.
# Tìm kiếm tuyến tính cho nhiều lần xuất hiện – Độ phức tạp O(n)
def linear_search_all(arr, value):
indices = []
for i in range(len(arr)):
if arr[i] == value:
indices.append(i)
return indices
Cho một mảng đã được sắp xếp. Tìm: V = 57.
Bước 1: Khởi tạo left = 0, right = 8.
Xét vị trí mid = (left + right) / 2
= 4.
Hiện tại: left = 5, right = 8, V = 57.
Hiện tại: left = 7, right = 8.
Bây giờ phần tử cần tìm đã được tìm thấy, thuật toán trả về chỉ số tương ứng (7).
# Tìm kiếm nhị phân cho một vị trí xuất hiện
# Độ phức tạp O(log n)
def binary_search(arr, value):
left, right = 0, len(arr) - 1
while left <= right: # đảm bảo khoảng tìm kiếm hợp lệ
mid = (left + right) // 2
if arr[mid] == value:
return mid
elif arr[mid] < value:
left = mid + 1
else:
right = mid - 1
return -1
| Linear Search | Binary Search | |
|---|---|---|
| Dữ liệu phải sắp xếp? | ❌ Không cần | ✅ Bắt buộc |
| Ý tưởng | Duyệt từng phần tử từ đầu đến cuối. | Liên tục chia đôi khoảng tìm kiếm. |
| Độ phức tạp | O(n) | O(log n) |
| Ưu điểm | Đơn giản, dễ cài đặt. | Rất nhanh với dữ liệu lớn đã sắp xếp. |
| Nhược điểm | Chậm với danh sách dài. | Phải trả giá cho bước sorting ban đầu. |
→ Đơn giản nhưng kém hiệu quả cho dữ liệu lớn.
# Bubble sort – Complexity O(n^2)
def bubble_sort(arr):
n = len(arr)
for i in range(n):
swapped = False
for j in range(0, n - i - 1):
if arr[j] > arr[j + 1]:
arr[j], arr[j + 1] = arr[j + 1], arr[j]
swapped = True
if not swapped:
break
Không có hoán đổi trong một lượt → mảng đã được sắp xếp.
# Selection sort – Complexity O(n^2)
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_idx = i
for j in range(i + 1, n):
if arr[j] < arr[min_idx]:
min_idx = j # Update min_idx
arr[i], arr[min_idx] = arr[min_idx], arr[i] # Swap
Mỗi vòng lặp i đặt thêm một phần tử vào đúng vị trí cuối cùng của nó.
# Insertion sort – Complexity O(n^2)
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i] # Current element to be sorted
j = i - 1
while j >= 0 and arr[j] > key:
arr[j + 1] = arr[j] # Shift element > key to the right
j -= 1
arr[j + 1] = key # Insert key at the correct position
Hiệu quả hơn Bubble/Selection khi mảng gần như đã sắp xếp.
# Merge sort – Complexity O(n log n)
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = arr[:mid]
right = arr[mid:]
sorted_left = merge_sort(left)
sorted_right = merge_sort(right)
return merge(sorted_left, sorted_right)
def merge(left, right):
res = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
res.append(left[i])
i += 1
else:
res.append(right[j])
j += 1
res.extend(left[i:])
res.extend(right[j:])
return res
# Quick sort
def partition(arr, low, high):
pivot = arr[high] # pivot
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
def quickSort(arr, low, high):
if low < high:
pi = partition(arr, low, high)
quickSort(arr, low, pi - 1)
quickSort(arr, pi + 1, high)
| Thuật toán | Độ phức tạp T(n) | Ổn định? | Đặc điểm chính | Khi nên dùng |
|---|---|---|---|---|
| Bubble |
Best: O(n) Avg/Worst: O(n²) |
✅ | Đơn giản, dễ hiểu; rất nhiều phép so sánh & hoán đổi. | Chỉ dùng cho ví dụ học thuật, dữ liệu rất nhỏ. |
| Selection | Best/Avg/Worst: O(n²) | ❌ | Ít hoán đổi hơn Bubble; luôn quét hết phần chưa sắp xếp. | Khi chi phí hoán đổi cao nhưng n vẫn nhỏ. |
| Insertion |
Best: O(n) Avg/Worst: O(n²) |
✅ | Hiệu quả khi dữ liệu gần như đã sắp xếp. | Danh sách nhỏ / gần sắp xếp; bước con trong thuật toán lai. |
| Merge | Best/Avg/Worst: O(n log n) | ✅ | Chia để trị, cần bộ nhớ phụ cho bước trộn. | Khi cần hiệu năng ổn định, dữ liệu rất lớn. |
| Quick |
Best/Avg: O(n log n) Worst: O(n²) |
⚠️ Tuỳ cài đặt | Rất nhanh trong thực tế; phụ thuộc chọn pivot. | Sắp xếp trong bộ nhớ, hiệu năng cao cho đa số trường hợp. |