VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
Cấu trúc dữ liệu và giải thuậtTìm kiếm và sắp xếp
Bài 9/18
6 phút

Tìm kiếm nhị phân

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Tìm một giá trong list một triệu phần tử bằng cách duyệt từ đầu, xấu nhất phải so một triệu lần. Nếu list đã sắp xếp, chỉ cần khoảng 20 lần so. (Nếu đã học khoá SQL) index của database tìm nhanh cũng nhờ dữ liệu đã sắp xếp như vậy.

Khái niệm

🌓 Tìm kiếm nhị phân (binary search): trên dãy đã sắp xếp, so với phần tử ở giữa rồi bỏ đi nửa không thể chứa giá trị cần tìm, lặp lại tới khi thấy hoặc hết dãy.

Mỗi bước bỏ một nửa, nên 1.000.000 phần tử chỉ cần khoảng 20 bước, vì 2 mũ 20 là 1.048.576. Đó là O(log n): gấp đôi dữ liệu chỉ thêm một bước.

Ví dụ

int[] prices =
{
    3000, 5000, 7000, 12000, 25000, 350000, 450000
};
Console.WriteLine(Find(prices, 12000));   // 3
Console.WriteLine(Find(prices, 8000));    // -1

int Find(int[] items, int target)
{
    int low = 0;
    int high = items.Length - 1;
    while (low <= high)
    {
        int mid = (low + high) / 2;
        if (items[mid] == target)
        {
            return mid;
        }
        if (items[mid] < target)
        {
            low = mid + 1;
        }
        else
        {
            high = mid - 1;
        }
    }
    return -1;
}
  • low và high là hai đầu của đoạn còn phải tìm. Ban đầu là cả array.
  • mid là vị trí giữa. / giữa hai số nguyên bỏ phần lẻ, như bài Toán tử và ép kiểu của khoá C# Core.
  • Giá ở giữa nhỏ hơn giá cần tìm thì bỏ nửa trái, lớn hơn thì bỏ nửa phải.
  • Đoạn còn lại rỗng (low > high) mà vẫn chưa thấy thì trả -1.
Đoạn 0..6, giữa là 12000, nhỏ hơn: bỏ nửa trái Đoạn 4..6, giữa là 350000, lớn hơn: bỏ nửa phải Đoạn 4..4, giữa là 25000: thấy ở vị trí 4
Tìm 25000 trong 7 giá: ba bước

.NET có sẵn Array.BinarySearch(array, x) và list.BinarySearch(x). Không thấy thì chúng trả về một số âm.

Thử ngay

Chép Find thành CountSteps, thêm biến đếm số bước, rồi tìm trong một triệu số:

int[] big = new int[1000000];
for (int i = 0; i < big.Length; i++)
{
    big[i] = i + 1;
}
Console.WriteLine(CountSteps(big, 500000));
Console.WriteLine(CountSteps(big, 1000000));

int CountSteps(int[] items, int target)
{
    int steps = 0;
    int low = 0;
    int high = items.Length - 1;
    while (low <= high)
    {
        steps++;
        int mid = (low + high) / 2;
        if (items[mid] == target)
        {
            return steps;
        }
        if (items[mid] < target)
        {
            low = mid + 1;
        }
        else
        {
            high = mid - 1;
        }
    }
    return steps;
}

Đoán trước khi chạy: tìm 500000 và tìm 1000000 mất bao nhiêu bước?

Xem kết quả
1
20

500000 nằm đúng giữa nên thấy ngay ở bước đầu. 1000000 nằm cuối, gần như trường hợp xấu nhất, cũng chỉ mất 20 bước. Duyệt từ đầu thì mất một triệu bước.

Lỗi hay gặp

Tìm nhị phân trên dãy chưa sắp xếp. Chương trình không báo lỗi, chỉ trả về kết quả sai, vì bỏ nửa dãy chỉ đúng khi dãy có thứ tự. Array.Sort sắp xếp array tăng dần (bài Sắp xếp trong .NET).

// SAI — 3000 có trong dãy nhưng trả về -1
int[] prices = { 12000, 3000, 450000, 5000, 7000 };
Console.WriteLine(Array.BinarySearch(prices, 3000));
// ĐÚNG — sắp xếp trước rồi mới tìm
int[] prices = { 12000, 3000, 450000, 5000, 7000 };
Array.Sort(prices);
Console.WriteLine(Array.BinarySearch(prices, 3000));

Tóm tắt

  • Tìm nhị phân so với phần tử giữa rồi bỏ một nửa dãy mỗi bước.
  • O(log n): một triệu phần tử chỉ khoảng 20 bước.
  • Chỉ đúng trên dãy đã sắp xếp.
  • .NET có Array.BinarySearch và List<T>.BinarySearch, không thấy thì trả số âm.

Tự kiểm tra

0/3 câu
Câu 1

Dãy đã sắp xếp có khoảng 1.000 phần tử. Tìm nhị phân cần tối đa khoảng bao nhiêu bước?

Câu 2

Tìm 60000 trong dãy { 2000, 4000, 9000, 15000, 30000, 60000, 80000 }. Phần tử được so đầu tiên là gì?

Câu 3

Vì sao Array.BinarySearch trên dãy chưa sắp xếp có thể trả sai?

HashSet và bài toán đếmSắp xếp chèn

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt