|  หน้าแรก สารบัญ เกี่ยวกับบล็อกนี้ เกี่ยวกับผู้เขียน คิวน่ารักเพราะสร้างจากลิงค์ลิสต์ [ตอน 3] สร้างคิวที่เพิ่ม-ลดขนาดได้อย่างมีพลวัต และเป็นเจนเนอริกสไตล์ OOP บทความโดย : ลาภลอย วานิชอังกูร (laploy.com) ในบทความตอนที่แล้วผู้เขียนนำเสนอวิธีสร้างคิวด้วยอาร์เรย์ไปแล้ว แต่คิวที่สร้างจากอาร์เรย์มีปัญหาอยู่อย่างหนึ่ง คือเมื่ออาร์เรย์เต็มแล้วจะขยายไม่ได้ ทำให้ขาดความยืดหยุ่น คงจะดีไม่น้อยหากเราสร้างคิวจากลิงค์ลิสต์ เพราะลิงค์ลิสต์เป็นโครงสร้างข้อมูลที่เรายืด-หดได้ตามใจชอบ บทความนี้มีจุดมุ่งหมายเพื่อเสนอวิธีเขียนโปรแกรมแบบ OOP แสดงการทำงานของโครงสร้างข้อมูล แสดงตัวอย่างอัลกอริทึม แสดงวิธีเขียนโค้ดด้วยภาษา C# แสดงการเขียนโปรแกรมแบบเมนเนจโค้ด และแสดงวิธีสร้างคิวด้วยลิงค์ลิสต์แบบเจนเนอริค ฉบับที่แล้วผู้เขียนได้ทบทวนความจำเรื่องคิว และอธิบายแนวคิดในการสร้างคิวจากลิงค์ลิสต์ไปแล้ว นอกจากนั้นยังแสดงโครงสร้างของโปรเจ็กต์ QueueLinkedList แสดงวิธีนิยามคลาส Node เพื่อนำไปสร้างเป็นออพเจ็กต์สำหรับเก็บข้อมูลภายในลิงค์ลิสต์ โค้ดบางส่วนของคลาส LinkedList และวิธีเขียนโค้ดเพื่อทำหน้าที่เข้าคิว ในบทความตอนนี้ผู้เขียนจะแสดงโค้ดและหลักการทำงานส่วนที่เหลือ คือส่วนออกคิว และแสดงโปรแกรมทดสอบ ซึ่งจะนำคลาส QueueLinkedList มาสร้างเป็นออพเจ็กต์เพื่อดูว่าคลาสที่เรานิยามไว้ทำงานได้ถูกต้องหรือไม่ และตอนท้ายของบทความผู้เขียนจะแสดงวิธีนิยามคลาสสร้างคิวจากลิงค์ลิสต์ที่เป็นเจนเนอริก ที่สามารถทำงานกับข้อมูลได้ทุกชนิด การออกคิว การออกคิวหรือ dequeue คือการลบโหนดหน้าสุดออกจากคิว แย่หน่อยที่ในคลาส LinkedList ไม่มีโค้ดอะไรใกล้เคียงการออกคิวเลย เราจึงต้องเขียนโค้ดขึ้นใหม่ทั้งหมด รูป 009 : เมธอด Dequeue และ IsEmpty เมธอด Dequeue เริ่มทำงานโดยตรวจสอบว่าคิวว่างเปล่าหรือไม่ (บรรทัดที่ 19) เราเขียนเมธอดตรวจสอบชื่อ IsEmpty (บรรทัดที่ 35-41) เตรียมไว้แล้ว เมธอดนี้มีหน้าที่ตรวจสอบว่า front เป็น null หรือไม่ หากใช่ แสดงว่าในคิวไม่มีโหนดเหลืออยู่เลย เพื่อให้ง่ายแก่การทำความเข้าใจตอนไล่โปรแกรม ผู้เขียนจะสมมุติว่าขณะนี้ในคิวมีโหนดอยู่สามโหนดดังนี้ รูป 020 : สภาพของคิวเมื่อมีข้อมูลสามโหนด โปรดสังเกตว่าตัวชี้ front ตอนนี้ชี้โหนด 1 และ back ชี้โหนด 3 เนื่องจากขณะนี้ในคิวมีโหนดอยู่ ตัวชี้ front จึงไม่เป็น null แต่จะมีค่าเท่ากับโหนด 1 ผลลัพธ์คือเมธอด IsEmpty จะคืนค่ากลับมาเป็น false โปรแกรมจะไปทำงานบรรทัดที่ 21 แต่ถ้าสมมุติว่าตอนนี้ในลิสต์ไม่มีโหนดเหลืออยู่เลย คือเราดึงข้อมูลออกคิวไปหมดแล้ว เมื่อโปรแกรมทำงานบรรทัดที่ 19 เมธอด IsEmpty พบว่าไม่มีโหนดแล้ว ค่า front จะกลายเป็น null มันจะคืนค่ากลับมาเป็น true ทำให้โค้ดบรรทัดที่ 20 ทำงานเพื่อส่งค่า -1 ไปให้โค้ดที่เรียกใช้มัน (ค่า -1 เป็นเครื่องหมายบอกว่าไม่มีข้อมูลในคิว) และจบการทำงานของเมธอด Dequeue เพียงเท่านั้น ในกรณีที่ยังมีข้อมูลบรรทัดที่ 21 จะสร้างตัวแปรชื่อ retVal เพื่อเก็บค่าส่งกลับ (return value) ค่าที่เราจะส่งกลับไปคือข้อมูลในโหนดหน้าสุดของคิว (เพราะการออกคิวต้องออกทางด้านหน้าเสมอ) ซึ่งก็คือข้อมูลโหนดที่เก็บอยู่ใน front.Data นั่นเอง ดังนั้นคำสั่งบรรทัดที่ 21 จึงนำค่านี้มาไว้ใช้ส่งกลับ คำสั่งบรรทัดที่ 22 ผู้เขียนใส่ไว้ทดสอบการทำงานตอนไล่หาความผิดพลาด (ดีบัก) ไม่ได้ใช้ประโยชน์ในเมธอดนี้ จะลบทิ้งไปเสียก็ได้ เมื่อได้ข้อมูลในการออกคิวแล้ว ขั้นตอนต่อไปเราต้องปรับเลื่อนตัวชี้หน้าคิว (front) ให้ชี้ไปยังโหนดถัดไป แต่ก่อนที่จะทำเช่นนั้นได้ เราจำเป็นต้องตรวจสอบว่าขณะนี้ภายในลิสต์ มีโหนดอยู่เพียงโหนดเดียวหรือไม่ หากเป็นเช่นนั้นเราจะต้องไม่เลื่อน front เพราะถ้าในลิสต์มีโหนดเพียงโหนดเดียว เมื่อออกคิวแล้วโหนดเดียวที่เหลืออยู่จะหายไป ในลิสต์จะไม่มีโหนดเหลืออยู่เลย จึงไม่รู้ว่าจะเลื่อน front ไปไหนได้อีก เราจึงต้องเซตค่า front ให้เป็น null เพื่อเป็นเครื่องแสดงว่าไม่มีโหนดเหลือแล้ว คำสั่ง if บรรทัดที่ 23 ตรวจสอบว่าภายในคิว มีโหนดเหลือโหนดอยู่เพียงเดียวหรือไม่ ซึ่งทำได้โดยดูที่ตัวชี้ Next ของ front หาก Next มีค่าเป็น null แสดงว่าไม่มีโหนดถัดไปอีกแล้ว โปรแกรมจะไปทำงานบรรทัดที่ 25-26 เพื่อเซตให้ทั่ง back และ front ให้มีค่าเป็น null เพื่อเป็นเครื่องแสดงว่าขณะนี้ลิงค์ลิสต์ว่างเปล่า นั่นคือไม่มีข้อมูลเหลืออยู่ในคิวแล้ว แต่ในตัวอย่างนี้ยังมีโหนดอยู่อีกสองโหนด คือนอกจากข้อมูลตัวที่กำลังออกคิวแล้ว ภายในคิวยังมีข้อมูลเหลืออยู่อีกสองตัว โปรแกรมจะไปทำงานบรรทัดที่ 30 ซึ่งทำหน้าที่ปรับเลื่อน front ให้ชี้ไปยังโหนดถัดไป เนื่องจากตอนนี้ภายในลิสต์มีโหนดอยู่สามโหนด และค่าของ front.Next เป็นโหนด 2 (ดูภาพคิวที่มีข้อมูลสามโหนด) เมื่อทำคำสั่งบรรทัดที่ 30 front จะกลายเป็นโหนด 2 เมื่อเป็นเช่นนั้น front.Next อันใหม่จึงกลายเป็นโหนด 3 บรรทัด 31 เซต front.Previous เป็น null ซึ่งมีผลให้โหนด 2 กลายเป็นโหนดแรกของคิว เมื่อเมธอด Dequeue ทำงานจบคิวจะมีสภาพดังนี้ รูป 021 : สภาพของคิวเมื่อนำข้อมูลออกไปหนึ่งโหนด โปรดสังเกตว่าในภาษา C# การออกคิวไม่จำเป็นต้องเขียนโค้ดเพื่อลบโหนดไปจริงๆ เพราะเมื่อเราเลื่อนตัวชี้ previous และ next แล้ว โหนดที่ไม่ได้รับการอ้างถึงจะถูกมองว่าเป็นขยะ และกาเบจคอลเลคเตอร์จะลบโหนดนั้นให้โดยอัตโนมัติ ไม่เหมือนภาษา C++ (ในโหมด unmanaged) ที่เราต้องคอยดูให้แน่ใจด้วยตนเองว่าได้ทำลายออพเจ็กต์ที่ไม่ต้องการแล้ว
 |