- Published on
Divide and Conquer
- Authors
- Name
- Tien Minh Pham
- @TinMinhPhm1
Chia để trị (Divide and Conquer) là một kỹ thuật thiết kế thuật toán, trong đó bài toán lớn được chia thành các bài toán nhỏ hơn, giải quyết các bài toán nhỏ này, sau đó kết hợp kết quả để giải quyết bài toán ban đầu.
Cấu trúc của thuật toán chia để trị
Thuật toán chia để trị thường được xây dựng qua 3 bước:
- Chia (Divide): Chia bài toán lớn thành nhiều bài toán con nhỏ hơn, thường là các bài toán có cấu trúc tương tự bài toán ban đầu.
- Trị (Conquer): Giải quyết từng bài toán con. Nếu bài toán con đủ nhỏ, giải trực tiếp (base case); nếu không, tiếp tục chia nhỏ hơn nữa.
- Kết hợp (Combine): Kết hợp kết quả từ các bài toán con để có kết quả của bài toán ban đầu.
Ví dụ minh họa thuật toán chia để trị
Merge Sort (Sắp xếp trộn) là một ví dụ kinh điển của thuật toán chia để trị. Nó chia mảng thành các mảng nhỏ hơn, sắp xếp từng mảng con, sau đó trộn (merge) các mảng con đã sắp xếp để tạo thành mảng đã sắp xếp hoàn chỉnh.
def merge_sort(arr):
if len(arr) <= 1: # Base Case: Mảng có 1 phần tử hoặc rỗng thì đã được sắp xếp
return arr
mid = len(arr) // 2
left_half = merge_sort(arr[:mid]) # Đệ quy sắp xếp nửa bên trái
right_half = merge_sort(arr[mid:]) # Đệ quy sắp xếp nửa bên phải
# Kết hợp hai nửa đã sắp xếp
return merge(left_half, left_right)
def merge(left, right):
result = []
i = j = 0
# So sánh từng phần tử và trộn hai mảng
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
# Thêm các phần tử còn lại vào kết quả
result.extend(left[i:])
result.extend(right[j:])
return result
Giải thích:
- Chia (Divide): Mảng được chia thành hai nửa cho đến khi mỗi mảng con chỉ có một phần tử.
- Trị (Conquer): Sắp xếp từng mảng con (vì mảng một phần tử luôn được xem là đã sắp xếp).
- Kết hợp (Combine): Trộn hai mảng con đã sắp xếp lại với nhau để tạo thành mảng đã sắp xếp hoàn chỉnh.
Quick Sort là một thuật toán chia để trị khác. Thuật toán này chọn một phần tử gọi là pivot, sau đó chia mảng thành hai phần: các phần tử nhỏ hơn pivot và lớn hơn pivot. Tiếp tục sắp xếp hai phần này đệ quy.
def quick_sort(arr):
if len(arr) <= 1: # Base Case: Mảng có 1 phần tử hoặc rỗng
return arr
pivot = arr[len(arr) // 2] # Chọn pivot (ở đây chọn phần tử giữa)
left = [x for x in arr if x < pivot] # Các phần tử nhỏ hơn pivot
middle = [x for x in arr if x == pivot] # Các phần tử bằng pivot
right = [x for x in arr if x > pivot] # Các phần tử lớn hơn pivot
# Đệ quy sắp xếp các phần bên trái và bên phải
return quick_sort(left) + middle + quick_sort(right)
Giải thích:
- Chia (Divide): Chọn pivot và chia mảng thành hai phần (nhỏ hơn và lớn hơn pivot).
- Trị (Conquer): Sắp xếp hai phần này bằng cách gọi đệ quy quick_sort.
- Kết hợp (Combine): Nối kết quả của phần bên trái, pivot, và phần bên phải lại với nhau.
Ưu điểm và nhược điểm của chia để trị
- Ưu điểm:
- Giải quyết các bài toán phức tạp một cách hiệu quả.
- Tận dụng khả năng xử lý song song.
- Một số thuật toán như
Merge Sort
,Quick Sort
có độ phức tạp tốt.
- Nhược điểm:
- Tiêu tốn nhiều bộ nhớ do sử dụng đệ quy (stack call).
- Nếu không chia nhỏ đúng cách hoặc không có điểm dừng, có thể dẫn đến lỗi
stack overflow
.
Bài toán khác sử dụng chia để trị
- Exponentiation (Lũy thừa nhanh)
- Tìm phần tử lớn thứ k trong mảng (Median of Medians)
- Tìm kiếm trong ma trận 2D