Stack, Queue và Priority Queue trong Java (Phần 3)


Stack, Queue và Priority Queue trong Java
Phần 3: Priority Queue
1.     Định nghĩa
Priority Queue là gì?
Priority Queue là một danh sách dạng biến thể của queue với thứ tự sắp xếp dựa trên mức độ ưu tiên của phần tử. Tức danh sách này có cấu trúc đưa vào và đưa ra giống queue, chỉ có một điểm khác là sau khi đưa vào thì nó sẽ đẩy phần tử mới vào vị trí phù hợp theo thứ tự giảm dần từ rear – có giá trị cao nhất, đến top – có giá trị thấp nhất.
Nói một chút về giá trị ưu tiên (priority). Về mặt tổng quan, đó là một cách để sắp xếp công việc nào sẽ được thực hiện trước, công việc nào sẽ được thực hiện sau. Về mặt cụ thể, đây là chỉ số giúp cho ta biết được phần tử nào sẽ được đưa ra ngoài trước, phần tử nào sẽ được đưa ra ngoài sau. Theo quy tắc trong đây, giá trị nào được xem như là nhỏ nhất thì sẽ được thực hiện trước. (Lưu ý: Việc định giá trị của 1 phần tử lớn hay nhỏ không theo 1 công thức cụ thể, mà phụ thuộc vào từng bài toán khác nhau).
     Note: Mức độ ưu tiên trong Linux đối với các chương trình là chỉ số NI, chỉ số này càng cao thì độ ưu tiên càng tăng. Trong các trình soạn thảo hoặc trình chiếu như PowerPoint cũng có thể sử dụng mức độ ưu tiên để xác định các ảnh chồng chất lên nhau, ảnh nào có độ ưu tiên cao hơn thì sẽ được chồng lên trên cùng (việc quy định số cao hay thấp sẽ ưu tiên trước phụ thuộc vào nhà phát triển).



Một bức hình đơn giản cho các bạn thấy được cách thức vận hành của priority queue như thế nào. (Nguồn: https://www.callicoder.com/java-priority-queue/)
Khi bạn muốn lấy một phần tử ra ngoài, thì phần tử tại vị trí front sẽ lấy ra trước. Khi bạn thêm phần tử, nó sẽ thêm tại rear và chọn vị trí phù hợp để đứng
2.     Sử dụng Priority Queue trong Java
Giống với Stack và khác với Queue, Priority Queue đều khai báo và khởi tạo thông qua cùng một lớp là PriorityQueue. Cú pháp chắc các bạn có thể đoán được rồi
PriorityQueue<Object> pQueue = new PriorityQueue<Object>();
Vì Priority Queue được sắp xếp thông qua độ ưu tiên, mà độ ưu tiên do chúng ta quy định, nên sẽ có thêm một công đoạn quy định độ ưu tiên cho phần tử. Đúng vậy, đó là xây dựng một Comparator. Bạn hình dung comparator với hai phần tử obj1 và obj2, obj1 đại diện cho phần tử chúng ta vừa chèn vào, và obj2 đại diện cho các phần tử đã có trên danh sách. Và ta có một quy định như sau
obj1 > obj2 <=> +
obj1 < obj2 <=> -
obj1 = obj2 <=> 0
Khi đó, class PriorityQueue của chúng ta sẽ ngầm hiểu ý nghĩa của các con số dương là gì, âm là gì, và 0 là gì thông qua quy tắc trên. Với xu hướng đẩy con số nhỏ nhất lên trên đầu, nó sẽ đẩy obj1 đi lên khi gặp số -, và sẽ dừng lại khi obj1 > obj2 hoặc obj1 = obj2. Nên nhớ, đây là quy tắc, tức chúng ta thực ra đang truyền thông tin thực sự rằng obj1 > obj2, hay obj1 < obj2, hay obj1 = obj2. Giả sử như sau

Giá trị ưu tiên đầu tiên sẽ là số 5
Vì sao? Vì chúng ta quy định obj1 sẽ nhỏ hơn obj2 (-1) khi value1 < value2, tức là phần tử có giá trị nhỏ hơn thì độ ưu tiên sẽ nhỏ hơn. Còn nếu chúng ta lật ngược lại trạng thái

Kết quả trả về là 10, với 10 là phần tử có độ ưu tiên nhỏ nhất
Vì sao? (Chú ý dòng phát sáng) Chúng ta quy định giá trị phần tử đầu tiên nếu lớn hơn phần tử thứ 2, thì obj1 sẽ có độ ưu tiên bé hơn obj2 (-1).
Chú ý: Mức độ ưu tiên sẽ không phụ thuộc vào value1 hoặc value2 trên, mà phụ thuộc vào cách chúng ta quy định dựa vào một điều kiện nào đó. Ở đây là với điều kiện số càng lớn thì độ ưu tiên càng bé
Một trò vui nho nhỏ áp dụng kiến thức chúng ta vừa học xem nào. Nếu chúng ta quy định obj1 luôn nhỏ hơn obj2 thì sao nào? Hãy xem nhé

Kết quả: 6 10 5
Ôi dào, thế phải chăng một obj mới khi đưa vào luôn ưu tiên lên đầu tiên sao, tức là mình quy định mọi số mới khi đưa vào luôn có độ ưu tiên nhỏ nhất, cho nên số nào vào trước thì sẽ ra trước. First In First Out, hừm nghe có vẻ như một Stack nhỉ, chỉ là cấu trúc và cách hoạt động của nó không giống Stack, nhưng chức năng của nó thì y chang.
Nếu đảo ngược lại obj1 luôn lớn hơn obj2 thì sao, câu trả lời rõ rồi nhỉ. Nó sẽ là 1 Queue thuần túy, với cấu trúc và cách vào ra giống y hệt Queue. Haha khi hiểu được cách thức hoạt động của comparator thì chiều xử lý của Priority Queue này các bạn có thể nắm dễ như trở bàn tay.
Với các hàm lấy vào và lấy ra, các bạn có thể thấy rõ đó là hàm add() và hàm remove(), vì chức năng giống với queue nên mình không cần phải giải thích lại nhỉ. Còn một số hàm khác các bạn có thể tham khảo thêm, trên mạng có khá nhiều rồi. Mình chỉ nói chi tiết những cái mà trên mạng ít hoặc không nói đến thôi.
 
Nội dung bài viết thuộc về Lê Công Diễn.

Người viết: Lê Công Diễn
Mang đi nhớ ghi nguồn


Nhận xét

Bài đăng phổ biến từ blog này

Deploy project Springboot MIỄN PHÍ sử dụng Render

Ứng dụng Mã hóa bất đối xứng (Asymmetric cryptography) vào Chữ ký số (Digital Signature)

API và HTTP - Một số khái niệm cơ bản cần biết về Web (Phần 2)