Sắp xếp chèn
Nội dung bài · 5 mục
- 1.Khái niệm
- 2.Ví dụ
- 3.Thử ngay
- 4.Lỗi hay gặp
- 5.Tóm tắt
Tìm nhị phân cần dãy đã sắp xếp, vậy sắp xếp bằng cách nào? Sắp xếp chèn là cách dễ hiểu nhất: lấy từng phần tử và chèn vào đúng chỗ trong phần đã xếp. Nó cũng cho thấy rõ một thuật toán O(n²) trông như thế nào.
Khái niệm
🃏 Sắp xếp chèn (insertion sort): lấy lần lượt từng phần tử, dời các phần tử lớn hơn nó trong phần đã xếp sang phải một ô, rồi đặt nó vào ô trống.
Phần đầu dãy luôn có thứ tự, và mỗi vòng nó dài thêm một phần tử. Xấu nhất, mỗi phần tử phải so với mọi phần tử đứng trước, nên là O(n²).
Ví dụ
int[] prices = { 12000, 3000, 7000, 5000 };
InsertionSort(prices);
Console.WriteLine(string.Join(", ", prices));
// 3000, 5000, 7000, 12000
void InsertionSort(int[] items)
{
for (int i = 1; i < items.Length; i++)
{
int current = items[i];
int j = i - 1;
while (j >= 0 && items[j] > current)
{
items[j + 1] = items[j];
j--;
}
items[j + 1] = current;
}
}ibắt đầu từ 1, vì một phần tử đứng một mình đã là dãy có thứ tự.currentlà giá đang cần chèn. Vòngwhiledời các giá lớn hơn nó sang phải.- Gặp giá không lớn hơn hoặc hết dãy thì dừng, đặt
currentvào ô trốngj + 1. - Sắp xếp ngay trong array, không tạo array mới.
▶ Mô phỏng: Sắp xếp chèn từng bước
int[] prices = { 12000, 3000, 7000, 5000 };
InsertionSort(prices);
Console.WriteLine(string.Join(", ", prices));
void InsertionSort(int[] items)
{
for (int i = 1; i < items.Length; i++)
{
int current = items[i];
int j = i - 1;
while (j >= 0 && items[j] > current)
{
items[j + 1] = items[j];
j--;
}
items[j + 1] = current;
}
}Xem từng bước
- i = 1: nhấc current = 3000 ra, ô 1 trống. Phần 0..0 đã có thứ tự.
- So lần 1: 12000 > 3000, phải dời 12000 sang phải.
- items[1] = items[0]: 12000 dời sang ô 1, ô trống lùi về 0.
- j = -1: hết dãy bên trái, dừng.
- items[j + 1] = current: đặt 3000 vào ô 0. Phần 0..1 đã có thứ tự.
- i = 2: nhấc current = 7000 ra, ô 2 trống. Phần 0..1 đã có thứ tự.
- So lần 2: 12000 > 7000, phải dời 12000 sang phải.
- items[2] = items[1]: 12000 dời sang ô 2, ô trống lùi về 1.
- So lần 3: 3000 không lớn hơn 7000, dừng: ô trống 1 là chỗ của 7000.
- items[j + 1] = current: đặt 7000 vào ô 1. Phần 0..2 đã có thứ tự.
- i = 3: nhấc current = 5000 ra, ô 3 trống. Phần 0..2 đã có thứ tự.
- So lần 4: 12000 > 5000, phải dời 12000 sang phải.
- items[3] = items[2]: 12000 dời sang ô 3, ô trống lùi về 2.
- So lần 5: 7000 > 5000, phải dời 7000 sang phải.
- items[2] = items[1]: 7000 dời sang ô 2, ô trống lùi về 1.
- So lần 6: 3000 không lớn hơn 5000, dừng: ô trống 1 là chỗ của 5000.
- items[j + 1] = current: đặt 5000 vào ô 1. Phần 0..3 đã có thứ tự.
- Xếp xong sau 6 lần so. Xấu nhất (dãy xếp ngược) mỗi phần tử so với mọi phần tử trước nó: O(n²).
Thử ngay
Đếm số lần so sánh khi dãy đầu vào đã có thứ tự và khi dãy đầu vào xếp ngược. Chép
InsertionSort thành CountComparisons, thêm biến comparisons và trả nó về:
int[] sorted = { 3000, 5000, 7000, 12000, 25000 };
int[] reversed = { 25000, 12000, 7000, 5000, 3000 };
Console.WriteLine(CountComparisons(sorted));
Console.WriteLine(CountComparisons(reversed));
int CountComparisons(int[] items)
{
int comparisons = 0;
for (int i = 1; i < items.Length; i++)
{
int current = items[i];
int j = i - 1;
while (j >= 0)
{
comparisons++;
if (items[j] <= current)
{
break;
}
items[j + 1] = items[j];
j--;
}
items[j + 1] = current;
}
return comparisons;
}Đoán trước khi chạy: với 5 phần tử, mỗi trường hợp tốn bao nhiêu lần so?
Xem kết quả
4
10Dãy đã có thứ tự: mỗi phần tử so một lần với phần tử liền trước là dừng, tổng 4 lần, tức O(n). Dãy ngược: phần tử thứ hai so 1 lần, thứ ba 2 lần... tổng 1 + 2 + 3 + 4 = 10 lần, tức O(n²).
Lỗi hay gặp
Dùng sắp xếp chèn cho dữ liệu lớn. Một trăm nghìn đơn hàng xếp ngược thì tốn khoảng 5 tỉ lần so. Dữ liệu lớn nên dùng thuật toán O(n log n) như merge sort, hoặc method có sẵn của .NET.
// SAI — tự viết O(n²) cho dữ liệu lớn
int[] prices = new int[100000];
for (int i = 0; i < prices.Length; i++)
{
prices[i] = prices.Length - i; // xếp ngược
}
InsertionSort(prices);// ĐÚNG — Array.Sort của .NET là O(n log n)
int[] prices = new int[100000];
for (int i = 0; i < prices.Length; i++)
{
prices[i] = prices.Length - i;
}
Array.Sort(prices);Tóm tắt
- Sắp xếp chèn lấy từng phần tử, dời phần tử lớn hơn sang phải, rồi chèn vào.
- Xấu nhất O(n²), khi dãy đầu vào xếp ngược.
- Tốt nhất O(n), khi dãy đầu vào đã có thứ tự, nên hợp với dãy nhỏ hoặc gần như đã xếp.
- Dữ liệu lớn dùng thuật toán O(n log n) như merge sort hoặc
Array.Sort.
Bình luận
Đang tải bình luận…