- • เรียนรู้หลักการสร้าง และการใช้งานคิวแบบมีเงื่อนไข
- • เรียนรู้การวิเคราะห์อัลกอริทึมจากการใช้งานคิวแบบมีเงื่อนไข
ในบางครั้งการใช้งานคิวตามปกติ ถึงแม้จะเป็นธรรม ให้บริการเท่าเทียมกัน แต่บางครั้งก็ขาดประสิทธิภาพ เช่นการเข้าคิวจ่ายเงินตามซุปเปอร์มาร์เก็ต หรือคิวรับฝากเงินของธนาคาร หรือคิวการจ่ายเงินค่าใช้ทางด่วนพิเศษ หรือคิวพิมพ์งานของเครื่องพิมพ์ ถ้าคนแรกๆของคิวมีการทำงานที่นานหลายๆรายการ แต่คนหลังมีพียงรายการเดียว การให้บริการจะขาดประสิทธิภาพ เพราะคนที่มีการทำงานสั้นต้องคอยคนที่ทำงานยาวๆเป็นเวลานาน ปริมาณงานที่สำเร็จต่อเวลาจะน้อยลง ดังนั้นการปรับปรุงระบบคิวตามเงื่อนไข เช่นการมีช่องทางด่วนไม่เกินสองรายการในการรับฝากเงินที่ธนาคาร หรือช่องทางด่วนคิดเงินไม่เกินสิบรายการตามซุปเปอร์มาร์เก็ต หรือช่องทางพิเศษสำหรับรถที่จ่ายเงินด้วยบัตรอัตโนมัติในการใช้ทางด่วนพิเศษ จะช่วยเพิ่มประสิทธิภาพในการทำงานของระบบโดยรวมได้ ซึ่งการสร้างคิวแบบมีเงื่อนไขนี้เราสามารถสร้างได้ด้วยโครงสร้างข้อมูลแบบไบนารีฮีป (Binary Heap) และไบโนเมียลคิว (Binomial Queue)
สำหรับเงื่อนไขในการสร้างคิวแบบนี้ เราจะใช้ค่าต่สุดหรือสูงสุดเป็นตัวกำหนดเงื่อนๆไข ขึ้นอยู่กับปัญหานั้นๆ แต่ส่วนใหญ่จะใช้ค่าต่ำสุดเป็นหลักตามตัวอย่างที่กล่าวมาด้านบน โดยข้อมูลที่มีค่าต่ำสุด จะถูกนำออกมาจากคิวเป็นลำดับแรกเสมอ ด้วยคำสั่ง deleteMins ซึ่งมีค่า Big O = O(1) หรือ O(log N) โดยใช้ Binary Heap หรือ Binomial Queue
ตัวอย่างไบนารีฮีป
ไบนารีฮีป คือต้นไม้คู่สมบูรณ์ แต่โหนดใบในระดับล่างสุดอาจจะไม่เต็ม เพราะเราต้องทำการสร้างต้นไม้คู่สมบูรณ์ทีละโหนดตามลำดับจากบนลงล่าง จากซ้ายไปขวา และมีเงื่อนไขว่า โหนดด้านบนต้องมีค่าคีย์น้อยกว่าโหนดด้านล่างเสมอ และโหนดรากจะมีค่าคีย์ต่ำสุด ซึ่งจะเป็นโหนดแรกที่ถูกนำมาใช้งานก่อนเสมอ ดังนี้
1. เริ่มต้นฮีปจะว่าง โหนดแรกของต้นไม้ที่สร้างจะเป็นราก
2. โหนดที่สองจะเป็นลูกทางซ้ายของราก และโหนดที่สามเป็นลูกทางขวาของราก
3. โหนดต่อไปก็เติมให้เต็มในแต่ละชั้นจากซ้ายไปขวา
4. แต่ละโหนดในฮีป จะมีคีย์สำหรับเปปรียบเทียบกับโหนดอื่นเพื่อการสลับตำแหน่งและคงคุณสมบัติของไบนารีฮีป
คุณสมบัติลำดับของฮีปคือคีย์ของโหนดด้านบนต้องมีค่าน้อยกว่าโหนดด้านล่างเสมอ
เพื่อความสะดวกในการสลับลำดับโหนดด้านบนและโหนดด้านล่างให้คงคุณสมบัติลำดับของฮีป เราจำเป็นต้องรู้ตำแหน่งดัชนีของโหนดด้านบน และด้านล่าง จากตัวอย่างฮีปด้านบน การใช้อาร์เรย์สร้างฮีปทำให้เรารู้ตำแหน่งดัชนีของแต่ละโหนดในฮีป
ให้ดูรูปโครงสร้างต้นไม้ของฮีปและอาร์เรย์ของฮีปประกอบการคำนวณหาตำแหน่งขงแต่ละโหนด ดังนี้
1. รากจะเก็บอยู่ที่ดัชนี [1] เสมอ (โหนด 13) สังเกตว่าเราจะไม่ใช้ดัชนี [0] ของอาร์เรย์ในการสร้างฮีป ทั้งนี้เพื่อให้การคำนวณตำแหน่งดัชนีของแต่ละโหนดในฮีปทำได้รวดเร็วและถูกต้อง
2. โหนดด้านล่างทางซ้ายจะเก็บอยู่ที่ดัชนี 2i เมื่อ i คือดัชนีของโหนดด้านบน เช่นโหนดทางซ้ายของโหนดดัชนี [4] (โหนด 19) จะอยู่ที่ดัชนี 2*4=[8] (โหนด 65)
3. โหนดด้านล่างทางขวา จะเก็บอยู่ที่ดัชนี 2i+1 เมื่อ i คือดัชนีของโหนดด้านบน เช่นโหนดทางขวาของโหนดดัชนี [4] (โหนด 19) จะอยู่ที่ดัชนี (2*4)+1=[9] (โหนด 26)
4. โหนดด้านบนจะอยู่ที่ดัชนี i/2 เมื่อ i คือดัชนีของโหนดด้านล่าง เช่น โหนดดัชนี [9] (โหนด 26)จะมีโหนดด้านบนอยู่ที่ดัชนี 9/2=[4] (โหนด 19) เป็นต้น
5. การสร้างฮีปด้วยอาร์เรย์ ทำให้เราไม่จำเป็นต้องใช้ตัวชี้ด้านซ้ายและด้านขวาของต้นไม้ และการค้นหาในฮีปสำหรับการเพิ่มข้อมูลและการนำข้อมูลไปใช้ ถึงแม้ว่าจะมีค่า Big O = O(log N) เหมือนกัน แต่ทำได้ง่ายและรวดเร็วกว่า
หลักๆจะมีสองคำสั่งคือ Insert() และ DeleteMin() ดังนี้
Insert()
1. ในการเพิ่มข้อมูลใหม่เข้าไปในฮีป คือการสร้างโหนดใหม่ในต้นไม้คู้สมบูรณ์ โดยการวางโหนดใหม่ในลำดับที่ต้อจากโหนดสุดท้ายในฮีป จากนั้นก็ให้ให้ปรียบเทียบคีย์กับโหนดด้านบน ถ้าคีย์ของโหนดใหม่มีค่ามากกว่าโหนดด้านบน ก็จบการเพิ่มข้อมูล Big O = O(1)
2. แต่ถ้าคีย์ของโหนดใหม่น้อยกว่าโหนดด้านบนก็ให้สลับตำแหน่งของโหนดทั้งสอง แล้วทำการเปรียบเทียบคีย์ของโหนดใหม่กับโหนดด้านบนไปเรื่อยๆ จนกว่าจะเจอตำแหน่งที่ถูกต้อง ในกรณีนี้ Big O = O (log N) และวิธีในการขยับโหนดใหม่ขึ้นข้างบนจนกว่าจะได้ตำแหน่งที่ถูกต้อง เราเรียกว่า percolate up
ตัวอย่างการ Insert 9
ก่อน Insert 9
หลัง Insert 9
ในการเอาโหนดออกไปใช้งาน โหนดรากจะถูกนำออกไปก่อนเสมอ ทำให้ต้นไม้แตกเป็นสองส่วน ฮีปถูกทำลาย ดังนั้นเราต้องหาวิธีการในการนำรากออกไปแต่ต้นไม้ยังคงสภาพคุณสบัติเดิมของฮีป ซึ่งมีวิธีการดังนี้
1. เอาโหนดรากออกไปใช้งาน แล้วเอาข้อมูลของโหนดสุดท้ายมาแทนที่ราก และลบโหนดสุดท้ายออกไป
2. จากนั้นก็เปรียบเทียบคีย์ของรากอันใหม่ว่าน้อยกว่าคีย์ของโหนดด้านล่างหรือไม่ ถ้าน้อยกว่าก็จบ ฮีปยังคงคุณสมบัติเดิม และ Big O = O(1)
3. แต่ถ้าคีย์ของรากอันใหม่มากกว่าคีย์ของโหนดด้านล่าง ให้สลับข้อมูลของโหนดด้านบนกับโหนดด้านล่างที่มีค่าคีย์น้อยกว่า จากนั้นก็ให้เปรียบเทียบคีย์กับโหนดด้านล่างไปเรื่อยๆจนกว่าจะได้ตำแหน่งที่ถูกต้อง ในกรณีนี้ Big O = O (log N) และวิธีในการขยับโหนดสุดท้ายลงข้างล่างเรื่อยๆจนกว่าจะได้ตำแหน่งที่ถูกต้อง เราเรียกว่า percolate down
ตัวอย่างการ DeleteMin
ก่อน DeleteMin
หลัง DeleteMin
ไบโนเมียลคิว (Binimial Queues)
การเพิ่มข้อมูลในไบนารีฮีบ ต้องทำ ครั้ง ดังนั้นค่า Big O คือ O(1)
การลบข้อมูลในไบนารีฮีบ ทำเพียงครั้งเดียว ดังนั้นค่า Big O คือ O(1) แต่ในกรณีที่มีการชนกันของค่าแฮช ก็ต้องเลื่อนไปยังตำแหน่งที่ว่าง จำนวน c ครั้ง ดังนั้นค่า Big O คือ O(c)
- คิวแบบมีเงื่อนไข ใช้ในงานทีมีความสำคัญไม่เท่ากัน เช่นงานสั้น หรืองานที่มีจำนวนน้อย ควรต้องทำ
ก่อน หรืองานที่เร่งด่วนเป็นต้น
- คิวแบบมีเงื่อนไข (Priority Queue) สร้างจากไบนารีฮีป (Binary Heap) หรือไบโนเมียลคิว
(Binomial Queue) โดยโหนดที่มีความสำคัญสูงสุด หรือมีค่าต่ำสุด จะถูกนำออกมาใช้ก่อนเสมอ
- ไบนารีฮีปคือต้นไม้คู่สมบูรณ ์ ที่โหนดใบในชั้นล่างสุดอาจจะไม่เต็ม ส่วนไบโนเมียลคิว คือป่าไม้ของ
ต้นไบโนเมียล ที่ความสูงไม่ซ้ำกัน
- การเพิ่มข้อมูล (Insert , Percolate Up) และลบข้อมูล (DeleteMin , Percolate Down) ออก
จากไบนารีฮีป โดยเฉลี่ย มีค่า Big O = O(log N) และมี Best case = O(1)
- การเพิ่มข้อมูล (Insert) และลบข้อมูล (DeleteMin) ออกจากไบนารีฮีป โดยเฉลี่ย มีค่า Big O = O(c)
หรือ O(log N) เมื่อ c มีขนาดเข้าใกล้ log N และมี Best case = O(1)