DeMen Blog #4: Wavelet Tree - Khi Segment Tree được đảo nhãn
Tác giả: Võ Khắc Triệu (DeMen100ns)
Khi làm các bài quy hoạch động, ta sẽ các lưu các trạng thái với một số tính chất. Tuy nhiên, đôi khi ta cần thay đổi cách lưu các trạng thái sao cho số trạng thái cần duyệt qua được giảm xuống. Chúng ta gọi đó là quy hoạch động đảo nhãn (Các bạn có thể đọc thêm tại đây).
Trong bài viết này, chúng ta sẽ cùng tìm hiểu về một cấu trúc dữ liệu hay và hữu dụng: Wavelet Tree, hay mình thường gọi là Segment Tree đảo nhãn.
Kiến thức cần biết
Đặt vấn đề
Xét bài toán sau:
Xét mảng gồm phần tử nguyên dương, trả lời truy vấn thuộc hai loại sau:
- : Đếm số phần tử có giá trị trong đoạn của mảng .
- : Trả về phần tử lớn thứ trong đoạn của mảng .
Giới hạn:
Ta có thể thấy hai loại truy vấn trên đều là các truy vấn liên quan đến thứ tự của giá trị trong đoạn. Merge Sort Tree và Wavelet Tree chính là các cấu trúc dữ liệu được dùng để xử lý các dạng truy vấn như trên.
Tuy nhiên, với bài toán trên, Merge Sort Tree sẽ trả lời mỗi truy vấn và với độ phức tạp là và sẽ dễ bị chạy quá thời gian khi đủ lớn.
Wavelet Tree được tạo ra để giải quyết các truy vấn này chỉ trong với mỗi truy vấn.
Ý tưởng
Thông thường, khi chúng ta xây dựng cây Merge Sort Tree, mỗi nút trên cây sẽ là một vector hoặc multiset chứa thông tin giá trị của một đoạn vị trí trên mảng và được sắp xếp tăng dần.
Ví dụ: Với mảng , ta có cây Merge Sort Tree đại diện cho mảng như sau:

Tuy nhiên, với Wavelet Tree, chúng ta sẽ dùng tư duy đảo nhãn để giải quyết. Trong đó, mỗi nút sẽ quản lý thông tin vị trí của một đoạn giá trị trên mảng. Chúng ta sẽ đi qua hai cải tiến để hiểu cách Wavelet Tree hoạt động.
Cải tiến 1: P-Tree
Mình sẽ gọi cải tiến này là P-Tree.
Gọi mảng là mảng vị trí của mảng , trong đó, là một tập các index có giá trị là trên mảng .
Ví dụ: (Vì , nên )
Ta sẽ dựng một cây Merge Sort Tree của mảng , đây chính là P-Tree của mảng .
Với mảng và như trên, ta sẽ có cây P-Tree như sau:

Ta thấy được, mỗi nút của cây P-Tree là tập các vị trí tương ứng với giá trị trên mảng nằm trong giá trị mà nút đó quản lý.
Với P-Tree, ta có thể xử lý các truy vấn và trong với mỗi truy vấn (Cách xử lý xin nhường lại cho độc giả tự nghiệm).
Ta thấy thuật toán có độ phức tạp bằng với Merge Sort Tree nhưng dễ cài đặt hơn. Tuy nhiên, chúng ta vẫn có thể cải tiến hơn nữa.
Cải tiến 2: Wavelet Tree
Trong cải tiến này, ta sẽ xử lý lại thông tin có được từ cây P-Tree.
Giả sử nút quản lý đoạn giá trị . Gọi .
Gọi nút lần lượt là nút con bên trái và nút con bên bên phải của nút .
Ta biết rằng, sẽ quản lý đoạn giá trị và sẽ quản lý đoạn giá trị .
Ta sẽ dựng cây Wavelet Tree như sau: Mỗi nút của cây Wavelet Tree sẽ có một dãy bit. Trong đó, nếu một giá trị xuất hiện trên nút của cây P-Tree và cũng xuất hiện trên nút của cây P-Tree thì bit tương ứng với giá trị đó trên nút của cây Wavelet Tree sẽ là , ngược lại thì là .
Ví dụ: Với mảng , ta sẽ có Wavelet Tree như sau:

Lưu ý rằng các nút lá không có nút con nên ta không cần lưu thông tin gì trong đó.
Gọi là tổng tiền tố (Prefix sum) của dãy bit của nút trên Wavelet Tree.
Gọi là vị trí của giá trị lớn nhất trong nút của P-Tree. Ta sẽ gọi là vị trí tương đối của trên nút .
Thông thường, nếu ta cần tìm vị trí tương đối của giá trị trên một nút trên P-Tree, ta cần phải dùng chặt nhị phân. Như vậy ta sẽ phải tốn độ phức tạp cho với mỗi giá trị trên một nút. Với cách tổ chức dữ liệu trên cây Wavelet Tree, chúng ta sẽ làm được điều trên trong . Thật vậy, chúng ta sẽ thực hiện như sau:
Với nút gốc (root) của cây thì .
Khi duyệt cây Wavelet Tree, ta có thể đi từ xuống hoặc xuống nên ta sẽ chia ra trường hợp để xử lý:
TH1: ->
Như định nghĩa ở trên, chỉ có các giá trị trên sẽ xuất hiện trong . chính là số bit nằm trong đoạn hay .
TH2: ->
Tương tự, chỉ có các giá trị trên sẽ xuất hiện trong . chính là số bit nằm trong đoạn hay .
Từ các thông tin này, chúng ta sẽ dễ dàng trả lời các truy vấn của bài toán trên. Chúng ta sẽ đi cụ thể vào từng truy vấn.
Truy vấn : Đếm số phần tử có giá trị trong đoạn của mảng .
Xét một nút quản lý đoạn giá trị . Ta có, là số số có giá trị trong đoạn giá trị nằm trong đoạn vị trí của mảng .
Như vậy đáp án sẽ là với là tập hợp ít nút nhất sao cho tổng tất cả các phạm vi mà các nút đó quản lí đúng bằng đoạn giá trị .
Vì chỉ có nút được thăm và mỗi nút xử lý trong nên độ phức tạp cho truy vấn này là .
Truy vấn : Trả về phần tử lớn thứ trong đoạn của mảng .
Ta sẽ sử dụng kỹ thuật Segment Tree Walk cho bài này và chúng ta sẽ bắt đầu từ gốc của cây Wavelet Tree.
Giả sử chúng ta đang ở đỉnh (quản lý đoạn giá trị ) và cần tìm phần tử lớn thứ của nút , ta sẽ xét ba trường hợp sau:
TH1: (và )
Hiển nhiên, chính là đáp án cần tìm.
TH2: Số giá trị của nút nằm trong đoạn vị trí .
Lúc này, ta thấy được phần tử lớn thứ của nút cũng chính là phần tử lớn thứ của nút . Như vậy, ta đi xuống nút và tiếp tục giải quyết bài toán.
TH3: Số giá trị của nút nằm trong đoạn vị trí .
Lúc này, phần tử lớn thứ của nút sẽ là phần tử lớn thứ của nút , ta cũng đi xuống nút và tiếp tục giải quyết bài toán.
Vì chỉ có nút được thăm và mỗi nút xử lý trong nên độ phức tạp cho truy vấn này là .
