วันนี้ผมนำเสนอแปลกๆ นะ บทความวันนี้เสนอเรื่อง Circular Queue โดยนำเสนอในรูปแบบ Q&A นะครับ ลองติดดามได้นะครับ ^__^
Q: ทำไมถึงต้องมี Circular Queue
A: เพราะ ว่างการใช้ queue แบบเดิม จะทำให้เสียเนื้อที่ใส่ส่วนด้านหน้าไป โดยเปล่าประโยชน์ หลังจากที่ enqueue ออกแล้ว ถึงแล้วว่าจะมีการสร้าง queue ใหม่ชึ้นมา ระบบก็จะจองเนื้อที่ในหน่วงความจำใหม่แทน ลองดูแผนภาพประกอบ

Q: แล้วใช้ Circular Queue จะแก้ปัญหาในข้อที่แล้วได้อย่างไร
A: ลองเปลี่ยนวิธีการเขียนจากการเขียนที่เรามองว่าคิว คือ แถวยาวๆ มามองให้เป็นวงกลม โดยมีการปรับเปลี่ยนในส่วนของโปรแกรม เพื่อสามารถที่จะทำ Circular Queue ลองดูแผนภาพประกอบ

Q: แล้วเราจะรู้ได้อย่างไร ว่า Queue มันเต็ม หรือยัง หรือตอนนี้ยังใส่ได้อีกเท่าไหร่
A: ขอตอบแยกเป็นข้อๆ ดังนี้ครับ
- ถ้าเป็น array ลองใช้ค่า index ให้เป็นประโยชน์ หรือจะสร้างตัวแปรอีกอันนึงขึ้นมานับก็ได้
- ถ้าเป็น linked list ก็ต้องสร้างตัวแปรขึ้นมานับ ถึงแม้ว่าlinked list จะมีโอกาสที่จะเต็มยาก แต่ถ้าเป็นงานใหญ่ๆ ก็มีโอกาสเต็มได้เหมือนกัน
ลอง Implement Code
- ผมมีการใช้ interface queueADT เพื่อกำหนดกรอบการทำงานว่า Circular Queue มีการทำงานอะไรบ้าง โดยตัว Interface queueADT สามารถนำไปใช้ต่อได้ ถ้านำไป implement queue แบบอื่นๆครับ
public interface queueADT {
public void enqueue(Object data)throws Exception;
public Object dequeue()throws Exception;
public boolean empty();
public boolean full();
public int length();
public void clear();
}- ต่อมาเป็นการ Implement ArrayCirQueue โดยมีการอธิบายรายละเอียดใน Session ถัดไปครับ
ArrayCirQueue implements queueADT
{
Object Obj[];
int total;
int head,tail,qsize;
ArrayCirQueue(int size)
{
Obj = new Object[size];
this.qsize = size;
head = -1;
tail = 0; //ชี้ข้อมูลตัวแรก เพื่อที่จะได้เอาไปเข้าคิว
total = 0;
}
public void enqueue(Object data)throws Exception
{
if(full())
{
throw new Exception("Queue is full");
}
else
{
Obj[tail] = data;
tail = (tail+1)%qsize;
total++;
//System.out.println("head: "+head+" tail: "+tail+" total: "+total);
}
}
public Object dequeue()throws Exception
{
Object data = null;
if(empty())
throw new Exception("Queue is empty");
else
{
if(head==-1)
{
head++;
}
data = Obj[head];
Obj[head] = null;
head = (head+1)%qsize;
total--;
//System.out.println("head: "+head+" tail: "+tail+" total: "+total);
}
return data;
}
public boolean empty()
{
if(total==0)
return true;
else
return false;
}
public boolean full()
{
if(total == qsize)
return true;
else
return false;
}
public int length()
{
return total;
}
public void clear()
{
total = 0;
head = -1;
tail = 0; //ชี้ข้อมูลตัวแรก เพื่อที่จะได้เอาไปเข้าคิว
}
}อธิบาย Code ทีละส่วน
- Data Member
Object Obj[]; //ประกาศให้มีข้อมูลเป็น Object ซึ่งถือว่าเป็น Class แม่ของ Object ทุกตัวใน java เพื่อที่จะรอรับข้อมูลได้ทุกรูปแบบ int total; //นับจำนวนข้อมูลที่มีอยู่ใน queue int head,tail,qsize; //ดูเพิ่มเติม
เพิ่มเติม: ใช้ head, tail และ qsize เป็นมือที่คอยจัดการภายใน queue ซึ่งจะมีชนิดข้อมูลแบบ integer เพื่อที่จะเก็บค่า index ของ array
- Constructor
ArrayCirQueue(int size)
{
Obj = new Object[size];
this.qsize = size;
head = -1;
tail = 0; //ชี้ข้อมูลตัวแรก เพื่อที่จะได้เอาไปเข้าคิว
total = 0;
}- Enqueue
public void enqueue(Object data)throws Exception
{
if(full())
{
throw new Exception("Queue is full");
}
else
{
Obj[tail] = data;
tail = (tail+1)%qsize; //ดูเพิ่มเติม
total++;
//System.out.println("head: "+head+" tail: "+tail+" total: "+total);
}
}เพิ่มเติม: เนื่องจากใน queue ข้อมูลจะเข้าทาง tail ทำให้ต้องมีการตรวจสอบว่า tail มันเกินขนาดของ array คือ ไม่ เพื่อไม่ให้เกิด ArrayOutofBound Exception และ ทำให้เป็นวงกลม โดยการ mod เพื่อที่จะเอาค่าเศษใช้ในการเลื่อน tail ไปในตำแหน่งเริ่มต้นของ Array ตามสูตร แต่ก็ต้องมีการตรวจสอบด้วยว่า queue เต็มหรือยังด้วย
tail = (tail+1) % qsize;
- Dequeue
public Object dequeue()throws Exception
{
Object data = null;
if(empty())
throw new Exception("Queue is empty");
else
{
if(head==-1)//ตรวจสอบในกรณีที่ยังไม่มีการdequeue ออกมาก่อนเลย เพื่อที่จะได้เลื่อน head ไปชี้ข้อมูลตัวแรกของqueue
{
head++;
}
data = Obj[head];
Obj[head] = null;
head = (head+1)%qsize;
total--;
//System.out.println("head: "+head+" tail: "+tail+" total: "+total);
}
return data;
}เพิ่มเติม: ลักษณะคล้ายกับ enqueue แต่จะเอาข้อมูลออกมาทาง head โดยเมื่อเอามาแล้วก็จะเพิ่มค่า head ไป โดย mod กับขนาดของ queue เพื่อไม่ให้มั่นใจว่าไม่เกิด exception และมีการตรวจสอบ queue ว่างด้วย

- Empty & Full
public boolean empty()
{
if(total==0)
return true;
else
return false;
}
public boolean full()
{
if(total == qsize)
return true;
else
return false;
}เพิ่มเติม: ใช้ค่าตัวแปร total มานับ และตรวจสอบ full
- Length
public int length()
{
return total;
}- Clear
public void clear()
{
total = 0;
head = -1;
tail = 0; //ชี้ข้อมูลตัวแรก เพื่อที่จะได้เอาไปเข้าคิว
}เพิ่มเติม: หลักการของมันก็ คือ ทำให้มือที่จัดการ คือ head และ tail มีค่า 0 และ -1 ตามลำดับ และไป set ค่า total = 0 โดยไม่ต้องสนใจข้อมูลที่อยู่ด้านในว่าจะต้องไปไล่ set ให้เป็น null
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.
