You are on page 1of 32

Stack และ Queue เป็ นโครงสร ้างทีใ่ ช ้เก็บข ้อมูลในอีกรูปแบบหนึง่ ทีม ี ารนาไปใช ้อย่าง

่ ก
แพร่หลาย ซึง่ การจัดเก็บในรูปแบบทัง้ สองนี้ได ้เลียนแบบมาจากเหตุการณ์ทเี่ กิดขึน ้ ใน
ิ ประจาวันของเรา เช่น เราวางหนั งสือซ ้อนกันเป็ นชัน้ ๆ (Stack) หรือ การเข ้าแถวเพือ
ชีวต ่ รอรับ
การบริการต่าง ๆ หรือทีเ่ รียกจนติดปากว่า เข ้าคิว (Queue) เช่น เข ้ารอการนาเงินไปฝากกับ
ธนาคาร เข ้าคิวรอซือ ้ ตั๋วหนัง ในบทนีเ้ ราจะพูดถึง Stack และ Queue ทีใ่ ช ้ array และ Linked-
List เป็ นโครงสร ้างในการเก็บข ้อมูล

หลังจากจบบทเรียนนีแ
้ ล ้ว ผู ้อ่านจะได ้ทราบถึง

o การออกแบบโครงสร ้าง และการใช ้งานของ Stack


o การนาข ้อมูล เข ้าสู่ หรือ ออกจาก Stack
o การดูข ้อมูลทีม
่ อี ยูใ่ น Stack โดยไม่มก
ี ารนาออก
o การใช ้ Stack ในรูปแบบต่าง ๆ
o การออกแบบโครงสร ้าง และการใช ้งานของ Queue
o การนาข ้อมูล เข ้าสู่ หรือออกจาก Queue
o การดูข ้อมูลทีม่ อี ยูใ่ น Queue โดยไม่มกี ารนาออก
o การใช ้ Queue ในรูปแบบต่าง ๆ
o การสร ้าง Stack และ Queue ด ้วย Linked-List

3.1 Stack

Stack เป็ นการจัดเก็บข ้อมูลในแบบทีเ่ รียกว่า Last-In, First-Out (LIFO) ซึง่ เป็ นการจัดเก็บที่
สามารถนาไปใช ้กับการเขียนโปรแกรมด ้านต่าง ๆ เช่น การตรวจสอบ (parsing หรือ analyzing)
สมการทางคณิตศาสตร์ เช่น 3 * (5 + 89) ในการออกแบบทางสถาปั ตยกรรมของ computer
ส่วนใหญ่กใ็ ช ้ stack เป็ นตัวจัดเก็บ address หรือ instruction ต่าง ๆ

บนสุด (top) - เข ้าหลังสุด

ล่างสุด (bottom) - เข ้าก่อน

้ – stack of books
ภาพที่ 3.1 การวางหนั งสือเรียงกันเป็ นชัน

Stack เป็ นการจัดเก็บข ้อมูลในลักษณะของการนาข ้อมูลมาวางซ ้อนกัน เหมือนกับการวางถ ้วย


ชามทีเ่ ราคุ ้นเคยกันอยูป
่ ระจา (ถ ้าเราเคยอยูใ่ นครัวมาก่อน หรือไปกินก๋วยเตีย๋ วบ่อย ๆ) หรือการ
นาเอาหนังสือมาวางเรียงกันเป็ นชัน้ ๆ ดังทีแ ่ สดงในภาพที่ 3.1 เราสามารถทีจ ่ ะเอาคุณสมบัตน
ิ ี้มา
ใช ้ในการเก็บข ้อมูลต่าง ๆ ทีเ่ ราเห็นว่าเหมาะสม ดังตัวอย่างทีไ่ ด ้กล่าวมาแล ้ว การเขียน code
Stack และ Queue บทที่ 3

ของ stack ก็ไม่ยากเพียงแต่เรากาหนดให ้การนาข ้อมูลเข ้า (push) และ การดึงข ้อมูลออก


(pop) อยูใ่ นกฎเกณฑ์ของ stack คือ เข ้าหลังออกก่อน (Last-In, First-Out)

การใช ้ array เป็ นทีเ่ ก็บข ้อมูลของ stack นั น


้ ทาให ้การเขียน code ของเราทาได ้ง่ายขึน ้ แต่การ
นาเอา array มาใช ้นั น ้ ก็เป็ นทีท
่ ราบกันดีอยูแ
่ ล ้วว่า มีขดี จากัดในเรือ
่ งของขนาดของ array คือ
เราไม่สามารถทีจ ่ ะขยายขนาดได ้ ถ ้าเราเลือกทีจ ่ ะกาหนดขนาดของ array ก่อนการสร ้าง stack
(ยกเว ้นว่าเราจะใช ้เทคนิคของ dynamic array ทีไ่ ด ้พูดถึงก่อนหน ้านีใ้ นบทที่ 1) ตัวอย่างแรกจะ
เป็ นการออกแบบโครงสร ้างของ Stack ทีใ่ ช ้ array ทีม ่ คี วามจากัดในเรือ
่ งของขนาด

3.1.1 Stack ทีม


่ ข
ี อ
้ จำก ัดในเรือ
่ งของขนำด

เราเริม
่ ต ้นด ้วยการสร ้าง class ArrayStack ทีม
่ ี operation ต่าง ๆ ทีเ่ กีย
่ วข ้องกับ stack คือ การ
นาข ้อมูลเข ้า (push) การดึงข ้อมูลออก (pop) การตรวจสอบว่า stack มีข ้อมูลอยูเ่ ต็มหรือไม่ม ี
ข ้อมูลอยูเ่ ลย (full และ empty) หรือแม ้กระทั่งการแสดงข ้อมูลทีม ่ อ
ี ยูใ่ น stack

ใน class ArrayStack ของเรา เรากาหนดตัวแปรชือ ่ stack เป็ น array ทีใ่ ช ้ในการเก็บ object ที่
user ต ้องการใช ้ และ กาหนดให ้ top เป็ นตัวชีถ
้ งึ ข ้อมูลทีอ
่ ยูบ
่ นสุดของ stack ขนาดของ stack
จะมาจาก user เมือ ่ constructor ถูกเรียก เช่น

ArrayStack<Integer> stack = new ArrayStack<Integer>(10);

3.1.1.1 กำรนำข้อมูลเข้ำสู่ stack (push)

การนาข ้อมูลเข ้าสู่ stack ก็เหมือนกับการนาข ้อมูลเข ้าสู่ array โดยทั่วไป เราเลือกทีจ่ ะตรวจสอบ
ถึงความจุของ stack ก่อนทีเ่ ราจะนาข ้อมูลเข ้า ซึง่ ถ ้า stack ของเราเต็มแล ้ว เราก็จะฟ้ องไปยัง
user ทันที (และจะไม่ใส่ข ้อมูลให ้)

public void push(T obj) {


if(!full()) {
stack[++top] = obj;
numItems = top + 1;
}
else {
System.err.println("ERROR! Stack is full.");
System.exit(1);
}
}

Book 6

Book 6 Top

Top Book 5 Book 5

Book 4 Push Book 4

Book 3 Operation Book 3

Book 2 Book 2

Book 1 Book 1

Stack ก่อนการใส่ Book 6 Stack หลังจากการใส่ Book 6

ภาพที่ 3.2 การใส่ข ้อมู ลเข ้าสู่ stack (push operation)

70
บทที่ 3 Stack และ Queue

3.1.1.2 กำรดึงข้อมูลออกจำก stack (pop)

การดึงข ้อมูลออกจาก stack ก็เหมือนกับการลบข ้อมูลออกจาก array เราเพียงแต่ดงึ เอาค่าทีอ ่ ยู่


ในตาแหน่งของ top มาใช ้ พร ้อมกับลดค่าของ top ลงหนึง่ ค่า เพือ ่ ให ้ top ชีไ้ ปยังข ้อมูลตัว
ถัดไปทีอ่ ยูใ่ น array (ตัวใหม่ทอ ี่ ยูบ
่ นสุดใน stack) code ทีเ่ ราเขียนขึน้ นี้เรากาหนดให ้มีการส่ง
ค่า null ให ้กับผู ้เรียกถ ้า stack ของเราไม่มข ี ้อมูลอยูเ่ ลย

public T pop() {
if(empty())
return null;
return stack[top--];
}

Book 6

Top Book 6 Pop

Book 5 Operation Book 5 Top

Book 4 Book 4

Book 3 Book 3

Book 2 Book 2

Book 1 Book 1

Stack ก่อนการดึง Book 6 ออก Stack หลังจากดึง Book 6ออก

ภาพที่ 3.3 การดึงข ้อมูลออกจาก stack (pop operation)

สาหรับ method ตัวอืน ่ ๆ ก็ไม่ยากทีจ


่ ะทาความเข ้าใจ เรากาหนดให ้ค่าของ top เมือ ่ ไม่มข
ี ้อมูล
อยูเ่ ลยเป็ น -1 เพราะฉะนั น
้ การตรวจสอบว่า stack มีข ้อมูลหรือไม่ ก็ต ้องตรวจสอบว่าค่าของ
top เท่ากับ -1 หรือไม่ ส่วนการตรวจสอบว่า stack มีข ้อมูลอยูเ่ ต็มแล ้วนั น
้ เราก็ตรวจสอบ top
กับขนาดของ array – 1

เพือ
่ ให ้การจัดการกับข ้อมูลเป็ นไปได ้อย่างหลากหลาย เราจะกาหนดให ้ stack ของเราเก็บข ้อมูล
ในรูปแบบของข ้อมูลดิบ (Generic)1 ซึง่ วิธก ี ารดังกล่าวก็ทาได ้ง่าย ๆ ดังทีเ่ ห็นจากโปรแกรม
ArrayStack.java ทีเ่ ราเขียนขึน ้

Code ของ Stack ทีใ่ ช ้ array เป็ นตัวเก็บข ้อมูล

1: /**
2: Stack using array to store data
3: accommodate any-type from user (almost)
4: */
5:
6: class ArrayStack<T> {
7: private int size;
8: private T[] stack;
9: private int top, numItems;
10:
11: //set size and allocate space for stack
12: public ArrayStack(int max) {
13: size = max; //set size of stack
14: stack = (T[])new Object[size]; //allocate space
15: top = -1; //no object yet
16: numItems = 0;
17: }

1
เราไม่สามารถสร ้าง array สาหรับการเก็ บ generic ได ้ ดังนัน ้ เราจึงต ้องสร ้าง stack ทีใ่ ช ้เก็ บ Object ก่อน หลังจากนัน

จึง cast ให ้เป็ น generic ดังทีเ่ ห็ นในบรรทัดที่ 14 อย่างไรก็ตาม ถ ้าเรา compile ด ้วย –Xlint:unchecked เราจะพบว่า
Java จะเตือนเราถึงการใช ้ cast ทีอ ่ าจเกิดปั ญหา (stack เป็ น Object[] แต่เราบังคับให ้เป็ น T[])

71
Stack และ Queue บทที่ 3

18:
19: //insert object onto stack
20: public void push(T obj) {
21: if(!full()) {
22: stack[++top] = obj;
23: numItems = top + 1;
24: }
25: else {
26: System.err.println("ERROR! Stack is full.");
27: System.exit(1);
28: }
29: }
30:
31: //remove object from stack
32: public T pop() {
33: if(empty())
34: return null;
35: return stack[top--];
36: }
37:
38: //check if stack is full
39: public boolean full() {
40: return (top == size-1);
41: }
42:
43: //check if stack is empty
44: public boolean empty() {
45: return (top == -1);
46: }
47:
48: public String toString() {
49: StringBuffer buf = new StringBuffer();
50: buf.append("top-> " + stack[top]);
51: for(int i = top-1; i >= 0; i--)
52: buf.append(", " + stack[i]);
53: return new String(buf);
54: }
55: }

การออกแบบ stack นั น ้ หลากหลายวิธที เี่ ราสามารถทาได ้ แต่การออกแบบบางครัง้ ก็ต ้องคานึงถึง


ความเหมาะสม ว่าเราจะต ้องมี method รองรับการทางานทัง้ หมดหรือไม่ ส่วนไหนที่ user
จะต ้องรับผิดชอบเอง เช่น การตรวจสอบและดักจับข ้อผิดพลาดทีอ ่ าจเกิดขึน
้ ในการใส่ข ้อมูลเข ้า
สู่ stack หรือการดึงข ้อมูลออกจาก code ข ้างบนนี้ เรากาหนดให ้มี method full() และ method
empty() ทีท ่ งั ้ เราและ user สามารถเรียกใช ้ได ้ ดังจะเห็นได ้จาก method push() และ method
pop() อีกสาเหตุหนึง่ ทีเ่ ราต ้องตรวจสอบก็เนื่องจากว่า stack ของเราใช ้ array เป็ นตัวเก็บข ้อมูล
และ array มีการข ้อมูลแบบจากัด ทาให ้ไม่สามารถเพิม ่ ข ้อมูลได ้ถ ้า array เต็ม

ลองมาทดสอบ code ทีเ่ ราเขียนขึน


้ มาด ้วยโปรแกรมง่าย ๆ นี้

1: /**
2: Testing stack that uses array as storage
3: */
4:
5: class ArrayStackTest {
6: public static void main(String []args) {
7: //create a stack holding 10 items
8: ArrayStack<Integer> stack = new ArrayStack<Integer>(10);
9:
10: //push 10 Integers onto stack
11: stack.push(45);
12: stack.push(5);
13: stack.push(32);
14: stack.push(78);
15: stack.push(12);
16: stack.push(99);
17: stack.push(27);
18: stack.push(19);
19: stack.push(66);
20: stack.push(7);
21:
22: System.out.println(stack); //display contents

72
บทที่ 3 Stack และ Queue

23:
24: //remove all Integers from stack
25: while(!stack.empty())
26: System.out.println("Pop " + stack.pop() +
" from Stack");
27: }
28: }

เรานาข ้อมูลทีเ่ ป็ น Object ชนิด Integer ใส่ไว ้ใน stack ของเราจนเต็มทัง้ 10 ตัว หลังจากนัน

เราก็ pop ข ้อมูลทัง้ หมดออกจาก stack ด ้วยการใช ้ while/loop (และ method empty() ในการ
ตรวจสอบว่า stack นั น ้ ยังมีข ้อมูลอยูอ
่ ก
ี หรือไม่)

ตัวอย่าง sample output

top-> 7, 66, 19, 27, 99, 12, 78, 32, 5, 45

Pop 7 from Stack


Pop 66 from Stack
Pop 19 from Stack
Pop 27 from Stack
Pop 99 from Stack
Pop 12 from Stack
Pop 78 from Stack
Pop 32 from Stack
Pop 5 from Stack
Pop 45 from Stack

ผู ้อ่านควรทดลองการนาข ้อมูลเข ้า และดึงข ้อมูลออกจาก Stack ด ้วยข ้อมูลต่าง ๆ เพือ


่ ให ้เกิด
ความเข ้าใจมากยิง่ ขึน ่ ามมาเป็ นโปรแกรมตัวอย่างของการใช ้ stack ในการ
้ โปรแกรมตัวอย่างทีต
จัดการกับข ้อมูลในรูปแบบต่าง ๆ

3.1.1.3 กำรใช ้ stack ในกำร reverse String

จากคุณสมบัตข ิ อง stack ทีว่ า่ last-in, first-out ทาให ้เราสามารถนา stack มาใช ้ในการ
reverse string ได ้ง่าย ๆ ดังตัวอย่างทีแ ่ สดงให ้ดูนี้

1: /**
2: Using stack to reverse a string
3: */
4:
5: import static java.lang.System.out;
6:
7: class ReverseTest {
8: public static void main(String[] args) {
9: //if no input detect, exit with a message
10: if(args.length == 0) {
11: out.println("Usage: java
ReverseTest \"Your String\"");
12: System.exit(1);
13: }
14:
15: //calling reverse() with args[0] and display its result
16: out.println("Reversed string: " + reverse(args[0]));
17: }
18:
19: //reversing input string
20: public static String reverse(String input) {
21: String output = ""; //output string
22: int size = input.length(); //size of input string
23: ArrayStack<Character> stack = new ArrayStack<Character>(size);
24:
25: //pushing each character onto stack
26: for(int i = 0; i < input.length(); i++) {
27: char c = input.charAt(i); //get a char from string
28: stack.push(c); //push it onto stack
29: }
30:
31: //pop character from stack and put it in output
32: while(!stack.empty()) {

73
Stack และ Queue บทที่ 3

33: char ch = stack.pop(); //pop character from stack


34: output += ch; //add character to output
35: }
36:
37: return output;
38: }
39: }

ขัน
้ ตอนการทาก็ไม่ยาก เมือ ่ เราได ้ string มาจาก keyboard แล ้วเราก็ทาการดึงเอา character
ออกจาก string ทีละตัว แล ้วนาไปใส่ไว ้ใน stack เมือ ่ ใส่ไว ้หมดแล ้วเราก็จะมี stack ทีม่ ข
ี ้อมูล
ด ้านบนเป็ น character ตัวสุดท ้ายของ string ทีอ ่ า่ นเข ้ามา ทีนี้เมือ
่ เรา pop ข ้อมูลออกจาก
stack เราก็จะได ้ข ้อมูลตัวสุดท ้ายของ string ก่อน และตัวอืน ่ ๆ ถัดไปจนกว่าเราจะได ้ข ้อมูล
ทัง้ หมด

ตัวอย่าง sample output

D:\Stack>java ReverseTest "Java is Great!"


Reversed string: !taerG si avaJ

่ นเลขฐำนสิบให้เป็นเลขฐำนสองด้วยกำรใช ้ stack
3.1.1.4 กำรเปลีย

stack สามารถทีจ ่ ะนามาใช ้ในการเปลีย่ นเลขฐาน 10 ให ้เป็ นเลขฐาน 2 ได ้เป็ นอย่างดี วิธก ี ารใน
การเปลีย ่ นเลขฐานสิบให ้เป็ นเลขฐานสองนัน ้ ให ้เรานาสองไปหารเลขนัน ้ แล ้วเก็บเศษทีไ่ ด ้จาก
การหารนั น ้ ใน stack หารไปเรือ่ ย ๆ จนหารไม่ได ้ หลังจากนัน ้ เราก็ pop ข ้อมูลทีอ
่ ยูใ่ น stack
ออก ซึง่ จะเป็ นเลขฐานสองของเลขฐานสิบนั น ้ ดังโปรแกรมตัวอย่างทีใ่ ห ้นี้

1: /**
2: converting positive number in base 10
3: to base 2 using stack
4: */
5:
6: import static java.lang.System.out;
7:
8: class Convert {
9: public static void main(String[] args) {
10: //if no input, exit with a message
11: if(args.length == 0) {
12: out.println("Usage: java Convert number");
13: System.exit(1);
14: }
15: ArrayStack<Integer> numStack = new ArrayStack<Integer>(20);
16: int remainder;
17: int number = Integer.parseInt(args[0]);
18:
19: //find remainder of number%2 and keep
20: //them on stack
21: while(number != 0) {
22: remainder = number % 2;
23: numStack.push(remainder);
24: number /= 2;
25: }
26:
27: //pop all digit from stack
28: out.print("Number in base two: ");
29: while(!numStack.empty()) {
30: out.print(numStack.pop());
31: }
32: }
33: }

ผลลัพธ์ของการ run โปรแกรมการเปลีย


่ นเลขฐานสิบให ้เป็ นเลขฐานสอง

D:\codes\Stack>java Convert 400


Number in base two: 110010000

74
บทที่ 3 Stack และ Queue

3.1.1.5 กำรใช ้ stack ก ับ Postfix-expression

ในการประมวลผลสมการทางคณิตศาสตร์ เช่น 3 * ((4 – 2) * (5 + 4) + 3) (เราเรียกรูปแบบ


การเขียนสมการนี้วา่ Infix notation) นั น้ เราสามารถทีจ ่ ะนาเอา stack มาช่วยได ้ แต่เราต ้องทา
ให ้สมการมีรูปแบบทีเ่ อือ
้ ต่อการประมวลผลทีว่ า่ เสียก่อน และรูปแบบทีช ่ ว่ ยให ้การนาเอา stack
มาใช ้ในการประมวลผลได ้นั น ้ ก็คอ
ื การเปลีย
่ นสมการให ้อยูใ่ นรูปของ Postfix notation

สมการทีอ
่ ยูใ่ นรูปแบบของ postfix จากตัวอย่างก่อนหน ้านี้คอ

342–54+*3+*

ขัน
้ ตอนในการประมวลผล postfix notation มีดังนีค
้ อ

1. ถ ้า input เป็ นตัวเลข (digit) ให ้เก็บไว ้ใน stack


2. ถ ้า input เป็ นเครือ
่ งหมายการประมวลผลให ้ pop ข ้อมูลออกจาก stack 2 ตัว แล ้วจึง
ทาการประมวลผลข ้อมูลทัง้ สอง เก็บผลลัพธ์ไว ้ใน stack
3. ทาขัน ้ ตอน 1 และ 2 จนกว่าจะหมดข ้อมูล

ในการเขียนโปรแกรมเพือ ่ ทาการประมวลผลสมการทีอ่ ยูใ่ นรูปแบบของ postfix นั น้ ก่อนอืน


่ เรา
่ นสมการดังกล่าวให ้อยูใ่ นรูปแบบของ postfix ก่อน ซึง่ สามารถทาได ้ด ้วยการใช ้ stack
ต ้องเปลีย
เช่นเดียวกัน ซึง่ มีขน
ั ้ ตอนการทางานดังนี้

1. ถ ้า input เป็ นตัวเลขให ้เก็บไว ้ใน buffer


2. ถ ้า input เป็ นเครือ
่ งหมายประมวลผล ให ้เก็บไว ้ใน stack
3. ถ ้า input เป็ นเครือ่ งหมายวงเล็บปิ ด ให ้ดึงเอาเครือ
่ งหมายประมวลผลออกจาก stack
ไปเก็บไว ้ใน buffer (เราจะไม่สนใจเครือ ่ งหมายวงเล็บเปิ ด)
4. ทาขัน ้ ตอนทัง้ สามจนกว่าจะหมดข ้อมูล

้ ตอนการประมวลผลสมการ postfix ทีใ่ ห ้นี้ เราจะเน ้นการประมวลผลของสมการทีใ่ ช ้วงเล็บ


ขัน
แบบเต็มที่ (fully parenthesized expression) เช่น

((( 3 + (2 + 5)) * ( 6 + 8 )) - ( 6 * 2 ))

่ ไปนีใ้ ช ้ได ้เฉพาะสมการทีม


ตัวอย่างทีเ่ ราได ้แสดงให ้ดูตอ ่ ต
ี ัวเลขเพียงแค่ตัวเดียว (one digit)
เท่านั น
้ แต่เราจะกลับมาพูดถึงการประมวลผล expression ทีม ่ ต
ี ัวเลขมากกว่าหนึง่ ตัวหลังจากนี้

//converts to postfix form


public static char[] infixToPostfix(char[] exp) {
ArrayStack<Character> s = new ArrayStack<Character>(exp.length);
StringBuffer buf = new StringBuffer();
for(int i = 0; i < exp.length; i++) {
if(exp[i] == ')')
buf.append(s.pop() + " ");
if(exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/')
s.push(exp[i]);
if(exp[i] >= '0' && exp[i] <= '9')
buf.append(exp[i] + " ");
}
return new String(buf).toCharArray();
}

วิธก
ี ารในการเปลีย ่ นก็ไม่ยาก เราเพียงแค่ตรวจสอบดูวา่ ค่าของ exp[i] มีคา่ เป็ นอะไร ถ ้าเป็ น
เครือ่ งหมายของการประมวลผล (+, -, *, /) เราก็จัดเก็บเครือ ่ งหมายเหล่านี้ไว ้ใน stack ถ ้าเป็ น
ตัวเลขเราก็จะเก็บตัวเลขนี้ไว ้ใน buffer ถ ้าเป็ นวงเล็บเปิ ดเราก็จะไม่ทาอะไร แต่ถ ้าเป็ นวงเล็บปิ ด
เราก็จะย ้ายข ้อมูลทีอ ่ ้านบนของ stack (ซึง่ ต ้องเป็ นเครือ
่ ยูด ่ งหมายสาหรับการประมวลผลอย่าง
แน่นอน) ไปเก็บไว ้ใน buffer หลังจากนั น ้ เราก็สง่ postfix นี้ไปทาการประมวลผลต่อไป

//evaluates postfix form


public static Double evaluate(char[] exp) {
ArrayStack<Double> s = new ArrayStack<Double>(exp.length);
Double v1, v2;
for(int i = 0; i < exp.length; i++) {

75
Stack และ Queue บทที่ 3

switch(exp[i]) {
case '+' : s.push(s.pop() + s.pop());
break;
case '-' : v1 = s.pop();
v2 = s.pop();
s.push(v2 - v1);
break;
case '*' : s.push(s.pop() * s.pop());
break;
case '/' : v1 = s.pop();
v2 = s.pop();
s.push(v2 / v1);
break;
}
//convert digit to int
while(exp[i] >= '0' && exp[i] <= '9')
s.push((double)(exp[i++] - '0'));

return s.pop();
}

ในการประมวลสมการทีอ ่ ยูใ่ นรูปของ postfix นั น


้ เมือ
่ เราเจอข ้อมูลทีเ่ ป็ นตัวเลขเราจะเก็บไว ้ใน
stack และเมือ ่ เราเจอเครือ่ งหมายของการประมวลผล เราจะทาการดึงข ้อมูลออกจาก stack 2
ตัว แล ้วจึงทาการประมวลผลข ้อมูลสองตัวนั น ้ พร ้อมทัง้ เก็บผลลัพธ์ทไี่ ด ้ไว ้ใน stack สาหรับการ
ประมวลทีอ ่ าจตามมาต่อไป โปรแกรม ArithmeticEvaluation.java รวบรวม code สาหรับ
กระบวนการทัง้ หมดทีเ่ กีย ่ วข ้องกับการประมวลผลสมการในรูปของ postfix

หลังจากทีเ่ ราได ้ทาการทดลองการประมวลผลด ้วยสมการ (2 * (((8 - 4) * (4 / 2)) + 2))


ข ้อมูลทีม
่ อ
ี ยูใ่ น stack เวลาต่าง ๆ มีดงั นี้

java ArithmeticEvaluation "(2 * (((8 - 4) * (4 / 2)) + 2))

Infix form: (2 * (((8 - 4) * (4 / 2)) + 2))


Postfix form: 2 8 4 - 4 2 / * 2 + *
top-> 2.0
top-> 8.0, 2.0
top-> 4.0, 8.0, 2.0
top-> 4.0, 2.0
top-> 4.0, 2.0
top-> 4.0, 4.0, 2.0
top-> 2.0, 4.0, 4.0, 2.0
top-> 2.0, 4.0, 2.0 ข ้อมูลใน stack ณ เวลา
top-> 2.0, 4.0, 2.0 ต่าง ๆ ของการ
top-> 8.0, 2.0 ประมวลผล
top-> 8.0, 2.0
top-> 2.0, 8.0, 2.0
top-> 10.0, 2.0
top-> 10.0, 2.0
top-> 20.0
top-> 20.0
Result: 20.00

และโปรแกรมการทางานทัง้ หมดของการประมวลผลสมการทีอ
่ ยูใ่ นรูปแบบของ postfix มีดังนี้

1: /**
2: Evaluating arithmetic expressions using stack
3: work only for one digit input with no error detecting!
4: */
5:
6: import static java.lang.System.out;
7:
8: class ArithmeticEvaluation {
9: public static void main(String[] args) {
10: if(args.length == 0) {
11: out.println("Usage: ArithmeticEvaluation \"expression\"");
12: out.println("e.g. \"(3 * (2 - 4 ) * (4 / (5 - 2)))\"");
13: System.exit(1);
14: }
15: char []exp = args[0].toCharArray();
16: exp = infixToPostfix(exp);

76
บทที่ 3 Stack และ Queue

17: out.printf("Result: %.2f%n", evaluate(exp));


18: }
19:
20: //converts to postfix form
21: public static char[] infixToPostfix(char[] exp) {
22: System.out.println("Infix form: " + new String(exp));
23: ArrayStack<Character> s = new ArrayStack<Character>(exp.length);
24: StringBuffer buf = new StringBuffer();
25: for(int i = 0; i < exp.length; i++) {
26: if(exp[i] == ')')
27: buf.append(s.pop() + " ");
28: if(exp[i] == '+' || exp[i] == '-' ||
29: exp[i] == '*' || exp[i] == '/')
30: s.push(exp[i]);
31: if(exp[i] >= '0' && exp[i] <= '9')
32: buf.append(exp[i] + " ");
33: }
34: return new String(buf).toCharArray();
35: }
36:
37: //evaluates postfix form
38: public static Double evaluate(char[] exp) {
39: System.out.println("Postfix form: " + new String(exp));
40: ArrayStack<Double> s = New ArrayStack<Double>(exp.length);
41: Double v1, v2;
42: for(int i = 0; i < exp.length; i++) {
43: switch(exp[i]) {
44: case '+' : s.push(s.pop() + s.pop());
45: break;
46: case '-' : v1 = s.pop();
47: v2 = s.pop();
48: s.push(v2 - v1);
49: break;
50: case '*' : s.push(s.pop() * s.pop());
51: break;
52: case '/' : v1 = s.pop();
53: v2 = s.pop();
54: s.push(v2 / v1);
55: break;
56: }
57:
58: //convert digit to int
59: while(exp[i] >= '0' && exp[i] <= '9')
60: s.push((double)(exp[i++] - '0'));
61: }
62: return s.pop();
63: }
64: }

ตัวอย่างของผลลัพธ์ทไี่ ด ้จากการ run ด ้วยสมการ (5 * (((9 + 8) * (4 * 6)) + 7)) คือ

Infix form: (5 * (((9 + 8) * (4 * 6)) + 7))


Postfix form: 5 9 8 + 4 6 * * 7 + *
Result: 2075.00

กระบวนการในการประมวลผลสมการทีม ่ ต
ี ัวเลขมากกว่า 1 digit นั น
้ เราเพียงแค่เปลีย่ นการอ่าน
สมการจากทีเ่ คยอ่านเป็ นทีละตัวอักษร เราก็อา่ นเป็ นทีละหน่วย (หรือทีเ่ รียกง่าย ๆ ว่าทีละคา –
token) ซึง่ ทาได ้ง่าย ๆ ด ้วยการใช ้ class StringTokenizer ดังตัวอย่างทีแ ่ สดงให ้ดูนี้

public static Double evaluate(String exp) {


System.out.println("Postfix form: " + new String(exp));
StringTokenizer str = new StringTokenizer(exp);
ArrayStack<Double> st = new ArrayStack<Double>(str.countTokens());
String token;
Double v1, v2, result = 0.0D;

//until all tokens have been processed


while(str.hasMoreTokens()) {
token = str.nextToken();
//token is an operator, we pop 2 operands and perform
//operation on them - push result back onto stack

77
Stack และ Queue บทที่ 3

if(isOperator(token)) {
v2 = st.pop();
v1 = st.pop();
result = operate(token.charAt(0), v1, v2);
st.push(result);
}
//token is an operand, we push it onto stack
else
st.push(new Double(Double.parseDouble(token)));

//print content of stack during the operation


System.out.println(st);
}
return result;
}

เราได ้ดัดแปลง method evaluate() ของเรา ให ้รับ String แทน char[] ซึง่ เราได ้ส่ง String ตัว
นี้ (exp) ไปให ้ StringTokenizer() ทาการอ่านพร ้อมกับแปลงรูปแบบของ exp ให ้อยูใ่ นรูปแบบ
ที่ StringTokenizer ต ้องการ ซึง่ เราได ้เก็บไว ้ในตัวแปร str หลังจากนัน
้ เราก็สร ้าง stack สาหรับ
เก็บ token ทีเ่ ราจะดึงออกมาจาก str

ขัน
้ ตอนต่าง ๆ ทีเ่ หลืออยูก ่ ็เหมือนกับโปรแกรม ArithmeticEvaluation.java ทีเ่ ราเขียนไว ้ก่อน
หน ้านี้ เพียงแต่เราได ้จัดกระบวนการประมวลผลให ้อยูใ่ นรูปแบบของ method เพือ ่ ให ้ดูง่ายขึน

และ method ทีเ่ ราเขียนขึน ้ ใหม่คอ

private static boolean isOperator(String token) {


return (token.equals("+") || token.equals("-") ||
token.equals("*") || token.equals("/"));
}

private static Double operate(char op, Double v1, Double v2) {


Double result = 0.0D;
switch(op) {
case '+' : result = v1 + v2;
break;
case '-' : result = v1 - v2;
break;
case '*' : result = v1 * v2;
break;
case '/' : result = v1 / v2;
break;
}
return result;
}

Method ทีส ่ าคัญอีกตัวหนึง่ ก็คอื infixToPostfix() ซึง่ เราต ้องดัดแปลงให ้รองรับการประมวลผล


ของเรา นั่ นก็คอื ตัวเลขทีใ่ ส่ตามกันมาจะต ้องถูกเก็บให ้เป็ นหนึง่ token เช่น สมมติวา่ เราใส่ 2 *
34 เราต ้องมั่นใจว่า 34 มีคา่ เป็ น 1 token (ไม่ใช่ 2 token คือ 3 กับ 4)

public static String infixToPostfix(char[] exp) {


System.out.println("Infix form: " + new String(exp));
ArrayStack<Character> s = new
ArrayStack<Character>(exp.length);
StringBuffer buf = new StringBuffer();
for(int i = 0; i < exp.length; i++) {
if(exp[i] == ')')
buf.append(" " + s.pop() + " ");
if(exp[i] == '+' || exp[i] == '-' ||
exp[i] == '*' || exp[i] == '/')
s.push(exp[i]);
if(exp[i] >= '0' && exp[i] <= '9')
buf.append(exp[i]);
if(exp[i] == ' ') buf.append(" ");
}
return new String(buf);
}

78
บทที่ 3 Stack และ Queue

Code ทีอ ่ ยูใ่ นกรอบและตัวหนาคือส่วนทีเ่ ราได ้ทาการเปลีย ่ นเพือ่ รองรับตัวเลขทีม ่ ม


ี ากกว่าหนึง่
digit สาเหตุทเี่ ราทาแบบนี้ก็เพราะว่า StringTokenizer จะอ่านข ้อมูลทีอ ่ ยูใ่ น string ทีละคา
(ขึน
้ อยูก ้ ๆ ซึง่ ในทีน
่ ับ delimiter ใน string นั น ่ ี้ delimiter ทีใ่ ช ้เป็ นค่า default คือ ช่องว่าง)
ดังนัน
้ เราจึงต ้องใส่ชอ่ งว่างหลังจากทีเ่ ราดึง operator ออกจาก stack และเมือ ่ เราเจอช่องว่างใน
string

code ทัง้ หมดก็มด


ี ังนี้

1: /**
2: Evaluating arithmetic expressions using stack
3: Expression must be fully parenthesized e.g.
4: ((( 3 + (2 + 5)) * ( 6 + 8 )) - ( 6 * 2 ))
5: This version allows more than one digit & no error checking
6: */
7:
8: import static java.lang.System.out;
9: import java.util.StringTokenizer;
10: import java.util.Scanner;
11:
12: class ArithmeticEvaluation2 {
13: public static void main(String[] args) {
14: out.println("Enter infix expression: ");
15: out.print("e.g. \"(((3 + (2 + 5)) * (6 + 8)) - (6 * 2))\" : ");
16: Scanner in = new Scanner(System.in);
17: String exp = in.nextLine();
18:
19: exp = infixToPostfix(exp.toCharArray());
20: out.printf("Result: %.2f%n", evaluate(exp));
21: }
22:
23: //converts to postfix form
24: public static String infixToPostfix(char[] exp) {
25: System.out.println("Infix form: " + new String(exp));
26: ArrayStack<Character> s = new ArrayStack<Character>(exp.length);
27: StringBuffer buf = new StringBuffer();
28: for(int i = 0; i < exp.length; i++) {
29: if(exp[i] == ')')
30: buf.append(" " + s.pop() + " ");
31: if(exp[i] == '+' || exp[i] == '-' ||
exp[i] == '*' || exp[i] == '/')
32: s.push(exp[i]);
33: if(exp[i] >= '0' && exp[i] <= '9')
34: buf.append(exp[i]);
35: if(exp[i] == ' ') buf.append(" ");
36: }
37: return new String(buf);
38: }
39:
40: //evaluates postfix form
41: public static Double evaluate(String exp) {
42: System.out.println("Postfix form: " + new String(exp));
43: StringTokenizer str = new StringTokenizer(exp);
44: ArrayStack<Double> st = new ArrayStack<Double>(str.countTokens());
45: String token;
46: Double v1, v2, result = 0.0D;
47:
48: //until all tokens have been processed
49: while(str.hasMoreTokens()) {
50: token = str.nextToken();
51: //token is an operator, we pop 2 operands and
52: //perform operation on them - push result onto stack
53: if(isOperator(token)) {
54: v2 = st.pop();
55: v1 = st.pop();
56: result = operate(token.charAt(0), v1, v2);
57: st.push(result);
58: }
59: //token is an operand, we push it onto stack
60: else
61: st.push(new Double(Double.parseDouble(token)));
62:

79
Stack และ Queue บทที่ 3

63: //print content of stack during the operation


64: System.out.println(st);
65: }
66: return result;
67: }
68:
69: //determines if the token is an operator
70: private static boolean isOperator(String token) {
71: return (token.equals("+") || token.equals("-") ||
72: token.equals("*") || token.equals("/"));
73: }
74:
75: //evaluates two oeprands with a given operator
76: private static Double operate(char op, Double v1, Double v2) {
77: Double result = 0.0D;
78: switch(op) {
79: case '+' : result = v1 + v2;
80: break;
81: case '-' : result = v1 - v2;
82: break;
83: case '*' : result = v1 * v2;
84: break;
85: case '/' : result = v1 / v2;
86: break;
87: }
88: return result;
89: }
90: }

เมือ
่ ทดสอบด ้วยสมการ (((30 + (20 + 50)) * (60 + 80)) - (60 * 20)) ผลลัพธ์ทเี่ ราได ้คือ

Infix form: (((30 + (20 + 50)) * (60 + 80)) - (60 * 20))


Postfix form: 30 20 50 + + 60 80 + * 60 20 * -
top-> 30.0
top-> 20.0, 30.0
top-> 50.0, 20.0, 30.0
top-> 70.0, 30.0
top-> 100.0
top-> 60.0, 100.0
top-> 80.0, 60.0, 100.0
top-> 140.0, 100.0
top-> 14000.0
top-> 60.0, 14000.0
top-> 20.0, 60.0, 14000.0
top-> 1200.0, 14000.0
top-> 12800.0
Result: 12800.00

3.1.2 Stack ทีไ่ ม่มข


ี อ
้ จำก ัดในเรือ
่ งของขนำด

เราได ้ดูตัวอย่างการใช ้ array ทีม


่ ค
ี วามจากัดในเรือ
่ งของขนาดเป็ นโครงสร ้างหลักของ Stack มา
พอสมควร ต่อไปเราจะมาดูตัวอย่างการออกแบบ Stack ทีใ่ ช ้ Dynamic array เป็ นโครงสร ้าง
หลักในการเก็บข ้อมูล

เนื่องจากว่าเราไม่มขี ้อจากัดในเรือ
่ งของจานวนของข ้อมูลทีส ่ ามารถเก็บได ้ใน Stack ดังนัน้ ใน
การนาข ้อมูลเข ้าเราจึงไม่ต ้องตรวจสอบว่า Stack ของเราจะเต็มหรือไม่ (ในมุมมองของผู ้ใช ้
Stack ของเรา) เพียงแต่เราต ้องมาเพิม ่ ขนาดความจุให ้กับ array ของเราถ ้า array นั น ้ ไม่
สามารถรองรับการนาเข ้าข ้อมูลใหม่ได ้ เรามาเริม
่ ด ้วยการดู class DynamicArrayStack ก่อน
หลังจากนัน้ เราจะมาดูกน ั ถึง method ต่าง ๆ ทีม
่ อ
ี ยูใ่ น class นี้

1: /**
2: Use dynamic array to store objects
3: */
4:
5: import java.lang.reflect.Array;
6:
7: class DynamicArrayStack<T> {
8: private T[] stack;
9: private int top, numItems;

80
บทที่ 3 Stack และ Queue

10: static final int INITIAL_CAPACITY = 10;


11:
12: //creating a stack with initial capacity of 10 objects
13: DynamicArrayStack() {
14: stack = (T[])new Object[INITIAL_CAPACITY];
15: top = -1;
16: numItems = 0;
17: }
18:
19: //insert object onto stack and
20: //increment number of objects in the stack
21: public void push(T item) {
22: //if exceeds the initial capacity, double the size
23: if(top == stack.length - 1) {
24: T[] newStack = (T[])doubleCapacity(stack);
25: stack = newStack;
26: }
27: stack[++top] = item; //pushing object
28: numItems = top + 1; //number of items so far
29: }
30:
31: //remove currently inserted object from the stack
32: public T pop() {
33: //if our stack is empty, returns null
34: if(empty())
35: return null;
36: //otherwise, decrements size and returns the object
37: --numItems;
38: return stack[top--];
39: }
40:
41: //internal method to verify if stack is empty
42: private boolean empty() {
43: return (top == -1);
44: }
45:
46: //internal method to double the size of stack
47: private T[] doubleCapacity(T[] source) {
48: int sourceLength = Array.getLength(source);
49: Class arrayClass = source.getClass();
50: Class componentClass = arrayClass.getComponentType();
51: T[] result = (T[])Array.newInstance(componentClass,
sourceLength * 2);
52: System.arraycopy(source, 0, result, 0, sourceLength);
53: return result;
54: }
55:
56: //display items in stack via System.out.println()
57: public String toString() {
58: StringBuffer buf = new StringBuffer();
59: buf.append("top -> " + stack[numItems-1]);
60: for(int i = numItems-2; i >= 0; i--)
61: buf.append(", " + stack[i]);
62: return new String(buf);
63: }
64: }

Method ทีส ่ าคัญสาหรับการออกแบบโครงสร ้างของ Stack ทีข ่ ยายขนาดของความจุเองเมือ ่ มี


ความจาเป็ นนั น้ มีอยูห ่ ลัก ๆ คือ ตัว constructor ทีใ่ ช ้ในการสร ้าง Stack ครัง้ แรก, method
push(), และ method doubleCapacity() พร ้อมกันนี้เรายังได ้สร ้าง toString() ขึน ้ มาใชส้ าหรับ
การส่งข ้อมูลทีอ ่ ยูใ่ น Stack ไปยังช่องทางการส่งข ้อมูลออกมาตรฐาน (standard output) ก่อน
อืน
่ เรามาดูถงึ ตัว constructor

DynamicArrayStack() {
stack = (T[])new Object[INITIAL_CAPACITY];
top = -1;
numItems = 0;
}

81
Stack และ Queue บทที่ 3

เรากาหนดให ้ขนาดของความจุเบือ ้ งต ้นของ Stack มีคา่ เป็ น 10 พร ้อมทัง้ กาหนดให ้ตัวแปร


numItems เป็ นทีเ่ ก็บจานวนของ object ทีม
่ อ
ี ยูจ ้ (ซึง่ เราใช ้ตัวแปรนี้ในการ
่ ริง ณ เวลานัน
แสดงผลผ่านทางช่องทางการส่งข ้อมูลออกมาตรฐาน)

ใน push() นั น้ ก่อนทีเ่ ราจะนาข ้อมูลเข ้า เราตรวจสอบดูกอ่ นว่าเราต ้องขยายขนาดความจุหรือไม่


ถ ้าต ้องขยายเราก็เรียกใช ้ doubleCapacity() ในการเพิม ่ ขนาดให ้เป็ นสองเท่า (ผู ้อ่านสามารถ
กาหนดให ้เป็ นเท่าไรก็ได ้ ถ ้าต ้องการ)

public void push(T item) {


//if exceeds the initial capacity, double the size
if(top == stack.length - 1) {
T[] newStack = (T[])doubleCapacity(stack);
stack = newStack;
}
stack[++top] = item; //pushing object
numItems = top + 1; //number of items so far
}

ในส่วนของ doubleCapacity() นั น้ เราได ้นามาจากโปรแกรม DynamicArray.java ในบททีห ่ นึง่


หากผู ้อ่านจาไม่ได ้ควรกลับไปทบทวนดูใหม่ ส่วน toString() นั น
้ ก็เขียนขึน
้ ง่าย ๆ โดยกาหนดให ้
มีการบอกตาแหน่งของ top ว่าอยูส ่ ว่ นไหนของการแสดงผล

public String toString() {


StringBuffer buf = new StringBuffer();
buf.append("top -> " + stack[numItems-1]);
for(int i = numItems-2; i >= 0; i--)
buf.append(", " + stack[i]);
return new String(buf);
}

หลังจากทีท
่ ดลองด ้วยโปรแกรม TestDynamicArrayStack.java ดังทีเ่ ห็นด ้านล่างนี้

1: /**
2: Testing dynamic stack
3: */
4:
5: class TestDynamicArrayStack {
6: public static void main(String[] args) {
7: //create stack
8: DynamicArrayStack<Integer> stack = new DynamicArrayStack<Integer>();
9:
10: //create 15 random Integer objects
11: //and insert them onto stack
12: for(int i = 0; i < 15; i++) {
13: int data = (int)(Math.random() * 100 + 1);
14: stack.push(data);
15: }
16: System.out.println(stack); //display contents of stack
17:
18: //remove 8 objects from stack
19: for(int i = 0; i < 8; i++) {
20: stack.pop();
21: }
22: System.out.println(stack); //display contents again
23: }
24: }

ผลลัพธ์ทไี่ ด ้คือ

top -> 30, 92, 51, 35, 94, 85, 76, 55, 18, 33, 25, 2, 42, 70, 47
top -> 18, 33, 25, 2, 42, 70, 47

เรานาข ้อมูลทีเ่ ป็ น Integer ใส่เข ้าสู่ Stack จานวน 15 ตัวเพือ่ แสดงให ้เห็นว่า Stack ของเรา
ขยายความจุให ้เราโดยอัตโนมัต ิ (เรารู ้ว่าความจุเริม ่ ต ้นของเรามีคา่ เป็ น 10) หลังจากนัน
้ เราก็
แสดงผลผ่านทาง standard output เราทาการลบข ้อมูลออก 8 ตัวพร ้อมกับแสดงผลของข ้อมูล
ทีเ่ หลืออยูใ่ น Stack เพือ
่ ยืนยันว่าการทางานของ Stack นั น ้ ถูกต ้อง

82
บทที่ 3 Stack และ Queue

ผู ้อ่านจะเห็นว่าโครงสร ้างของ Stack ทีเ่ ราสร ้างขึน้ ใหม่นัน ้ ทางานรองรับการนาเข ้าได ้ดีกว่าทีเ่ รา
ได ้ออกแบบไว ้ก่อนหน ้านี้ เราไม่ต ้องกังวลถึงความจุทจ ี่ ากัดของ Stack อีกต่อไป อย่างไรก็ตาม
โครงสร ้างของ Stack ทีเ่ ราได ้ออกแบบไว ้ยังมีอก ี หลายส่วนทีส ่ ามารถนาไปปรับปรุงเพือ ่ ให ้การ
ใช ้งานทาได ้ง่ายขึน้ เช่น ในการ pop ข ้อมูลออกจาก Stack นั น ้ เรายังต ้องทาการ cast ให ้อยูใ่ น
รูปของ object ทีเ่ ราได ้กาหนดไว ้ คงจะเป็ นสิง่ ทีด
่ ถ ี ้าเราสามารถทีจ ่ ะรองรับการนาข ้อมูลเข ้า และ
ออกจาก Stack โดยไม่ต ้องทาการ cast หรือกังวลกับชนิดของข ้อมูลทีเ่ รากาลังทาอยู่

3.2 Queue

Queue เป็ นโครงสร ้างทีเ่ ก็บข ้อมูลในรูปแบบทีเ่ รียกว่า First In, First Out (เข ้าก่อน – ออก
ก่อน) หรือ FIFO queue มาจากภาษาอังกฤษทีแ ่ ปลว่า การจัดอันดับ หรือ การจัดแถว ดังที่
แสดงในภาพที่ 3.4

หน ้า (front) หลัง (rear)

Queue of Java cups

ภาพที่ 3.4 การเข ้าแถวเพือ


่ รอรับการบริการ

queue ทีเ่ ราจะเขียนขึน ้ ก็จะยังคงใช ้ array เป็ นตัวเก็บข ้อมูล แต่เราจะใช ้ array ทีน ่ าเอาเนื้อทีท
่ ี่
ว่างลงเมือ ่ มีการดึงข ้อมูลออกไปกลับมาใชใหม่ ้ วิธน
ี ี้จะทาให ้เรามีท ี่ ทีส
่ ามารถจะจัดเก็บข ้อมูลได ้
ดีกว่า array แบบปกติ เราเรียก array แบบนี้วา่ circular array ผู ้อ่านอาจใช ้ array ธรรมดาใน
การเก็บข ้อมูลก็ได ้ แต่การบริหารและจัดการกับเนื้อทีม ่ ป
ี ั ญหามากกว่าการจัดเก็บด ้วย circular
array กล่าวคือเมือ ่ มีการนาข ้อมูลเข ้า เราจะนาไปใส่ไว ้ที่ rear และเลือ ่ นไปอีกหนึง่ ตาแหน่ง และ
เมือ่ ดึงออกเราก็ดงึ ออกจากทาง front และก็เลือ ่ นไปอีกหนึง่ ตาแหน่ง วิธก ี ารแบบนี้ทาให ้เราเสีย
เนื้อทีท่ างด ้านหน ้าของ array เมือ่ เราดึงข ้อมูลออก ดังทีแ ่ สดงในภาพที่ 3.5 นี้

front rear

55 78 12 7 85 23 77 235 …

เนือ
้ ทีส
่ ญ
ู เสียหลังจากการดึงข ้อมู ลออก

ภาพที่ 3.5 การสูญเสียเนือ


้ ทีจ
่ ากการดึงข ้อมูลออก

เนื่องจาก queue เป็ นโครงสร ้างทีน ่ าข ้อมูลเข ้าสู่ queue ทางด ้านหลังและดึงข ้อมูลออกทาง
ด ้านหน ้า ดังนัน
้ การเขียน code จะต ้องจาตาแหน่งของข ้อมูลทัง้ ทีอ ่ ยูด
่ ้านหน ้าและด ้านหลังของ
queue อีกสิง่ หนึง่ ทีเ่ ราจะต ้องคานึงถึงก็คอื การตรวจสอบว่า queue เต็ม (full) หรือว่าง
(empty) เนื่องจาก circular queue นาเอาช่องว่างทีเ่ หลืออยูม ่ าใช ้ ดังนั น
้ การตรวจสอบจึงต ้อง
ใช ้ index 2 ตัวเป็ นตัวช่วย

เราใช ้ front และ rear เป็ น index ทีอ


่ ยูด
่ ้านหน ้าและด ้านหลังของ queue ตามลาดับ เรา
ตรวจสอบว่า queue ว่าง (empty) ด ้วยประโยค

(front == rear)

83
Stack และ Queue บทที่ 3

และ queue เต็มด ้วยประโยค

(rear + 1) % size == front

เรามาลองดูภาพตัวอย่างสถานการณ์ทงั ้ สองทีไ่ ด ้กล่าวมาแล ้ว ภาพแรกเป็ นสถานะของ queue


ในขณะทีไ่ ม่มข
ี ้อมูลอยู่

0 1 2 3 4

ในการสร ้าง queue ครัง้ แรก เรากาหนดให ้ front และ


front rear ชีไ้ ปทีต ่ าแหน่ งเดียวกันคือ 0 และเราจะเลือ
่ น rear
ทุกครัง้ ทีม ี ารนาข ้อมูลเข ้าสู่ queue
่ ก

rear

ภาพที่ 3.6 สถานะภาพขณะที่ queue ยังไม่ม ีข ้อมูล


เนื่องจากว่าเราใชการค านวณด ้วย (rear + 1) % size == front เป็ นตัวตรวจสอบความจุเต็ม
ของ queue ดังนัน ้ เราจะเสียช่องว่างไปหนึง่ ที่ ดังทีแ
่ สดงในภาพที่ 3.7

0 1 2 3 4

หลังจากทีเ่ ราใส่ข ้อมูลเข ้าสู่ queue 4 ตัวเราก็ จะ


45 89 23 10 ได ้ภาพดังทีเ่ ห็ น ซึง่ เป็ นสถานะภาพของ queue ที่
เต็ม คือ (rear + 1) % size == front [(4 + 1)
% 5 == 0] จะเห็ นว่าเราเสียช่องว่างไปหนึง่
ช่องว่าง ซึง่ เป็ นหนึง่ ในวิธที เี่ ราเลือกใช ้ในการ
ออกแบบโครงสร ้าง queue ของเรา
front rear

ภาพที่ 3.7 ช่องว่างทีเ่ สียไปในขณะที่ queue เต็ม

สมมติวา่ มีการดึงข ้อมูลออกจาก queue 2 ตัวหลังจากที่ queue เต็ม เราก็นาช่องว่างที่ rear ชี้
่ าใช ้ พร ้อมทัง้ ทาการกาหนดตาแหน่งของ rear ใหม่ให ้รองรับการใส่ข ้อมูลด ้วยการวนกลับ
อยูม
ของ rear ดังทีแ ่ สดงให ้ดูในภาพที่ 3.8

84
บทที่ 3 Stack และ Queue

0 1 2 3 4

45 89 23 10

หลังจากทีเ่ ราดึงข ้อมูลออก 2 ตัว front ก็ จะมาชี้


ในตาแหน่ง index = 2 ซึง่ ทาให ้การตรวจสอบ
ว่า queue เต็มเป็ น false [(4 + 1) % 5 == 2]
ว่าง 2 ช่อง front rear
 false ดังนัน ้ เราจึงนาข ้อมูล 99 ใส่ ณ
ตาแหน่งที่ rear = 4 ชีอ ้ ยู่พร ้อมทัง้ ทาการเลือ
่ น
rear ด ้วยประโยค rear = (rear + 1) % size
0 1 2 3 4 ดังนัน
้ rear จึงไปชีท ้ ี่ 0 ซึง่ เป็ นตาแหน่ งแรกของ
array ทีเ่ รานากลับมาใช ้ใหม่
45 89 23 10 99
อย่างไรก็ ตามถ ้าเราใส่ข ้อมูลอีก ตัว rear ก็ จะมาชี้
ที่ index = 1 ซึง่ จะทาให ้ประโยคของการ
ตรวจสอบว่า queue เต็มเป็ นจริง เราก็ ยังคงเสีย
ช่องว่างอีกหนึง่ ช่องเหมือนเดิม
rear front

ภาพที่ 3.8 การนาช่อ งว่างกลับมาใช ้ใหม่ และการกาหนด rear ด ้วยการวนกลับ

อย่างไรก็ตามถ ้าสังเกตให ้ดีจะเห็นว่า ถ ้าเราให ้ผู ้ใช ้กาหนดขนาดของ queue ให ้เป็ น n โดยที่ n
เป็ นค่า integer ทีเ่ ป็ นบวก queue ของเราจะสามารถเก็บข ้อมูลได ้สูงสุดได ้เพียง n – 1 ตัว (เช่น
ทีแ
่ สดงให ้ดูในภาพก่อนหน ้านี)้ เพราะฉะนัน ้ ถ ้าเราอยากให ้การกาหนดค่าของผู ้ใช ้เป็ นจริงดังที่
เขากาหนดเราก็เพียงแต่เพิม ่ ขนาด (size) ของ queue ให ้มีคา่ เกินไปหนึง่ ค่า ดังทีแ
่ สดงไว ้ใน
constructor ของ QueueArray (size = max + 1)

ในบางครัง้ เราอาจเพิม
่ ตัวแปรอีกตัวหนึง่ (count) เพือ
่ เป็ นตัวบอกว่ามีข ้อมูลอยูใ่ น queue กีต
่ ัว
และการเพิม ่ ตัวแปรตัวนี้อาจช่วยให ้การเขียน code ของการทางานของ queue เขียนได ้ง่ายขึน ้
โปรแกรมตัวอย่างของเราใชตั้ วแปร count ในการแสดงผลมากกว่าทีจ ่ ะใช ้ทาอย่างอืน ่ ดังที่
แสดงไว ้ใน constructor ทีเ่ ห็นนี้

public QueueArray(int max) {


size = max + 1; //making size one size bigger
queue = new Object[size]; //to deal with wrap around
front = rear = 0; //initial queue: empty
count = 0; //no object in queue
}

ในทางปฏิบัตน ิ ัน
้ เมือ
่ front และ rear มาอยูใ่ นตาแหน่งทีอ ่ ยูถ
่ ัดกัน การตรวจสอบว่า queue เต็ม
นั น
้ จะทาให ้การ insert ข ้อมูลตัวสุดท ้ายเป็ นไปไม่ได ้ เพราะว่าประโยค

if((rear + 1) % size == front)

จะทาให ้เราเสียช่องว่างใน queue ไปหนึง่ ช่องดังทีไ่ ด ้กล่าวมาแล ้ว แต่เพือ ่ ให ้ผู ้ใช ้มีความรู ้สึกว่า
ได ้ใช ้ queue อย่างทีเ่ ขากาหนดไว ้ (โดยไม่รู ้ว่าการใช ้เนื้อทีใ่ นหน่วยความจานั น้ มีการใช ้เกิน)
่ ขนาดของการใช ้ภายใน queue มากกว่าการจองใช ้จริงจากผู ้ใช ้
เราจึงต ้องเพิม

3.2.1 กำรนำข้อมูลเข้ำสู่ queue

การ insert ข ้อมูลเข ้าสู่ queue ของเราก็ทาได ้ด ้วยการตรวจสอบว่า queue ของเรานั น ้ เต็มหรือ
ยังด ้วยการเรียก method full() ถ ้ายังไม่เต็มก็ใส่เข ้าไปและเลือ
่ น rear pointer ไปอีกหนึง่ ช่อง
ด ้วยการใช ้คาสัง่

rear = (rear + 1) % size; //move rear pointer

ซึง่ คาสั่งนี้จะทาการหา index ทีว่ า่ งอยูถ


่ ัดไปจาก index ตัวเดิมด ้วยการใช ้การหารของ integer
หรือทีเ่ ราเรียกว่า modular (หรือ modulo) operation

85
Stack และ Queue บทที่ 3

public void insert(T obj) {


//exit routine if queue is full
if(full()) {
System.err.println("Error: queue is FULL!");
System.exit(1);
}
queue[rear] = obj; //insert object
rear = (rear + 1) % size; //move rear pointer
count++; //one more object added
}

3.2.2 กำรดึงข้อมูลออกจำก queue

การ remove ข ้อมูลออกจาก queue ก็ทาคล ้าย ๆ กับการ insert เพียงแต่แทนทีเ่ ราจะทากับ
rear pointer เราก็มาทากับ front pointer เมือ ่ เราดึงข ้อมูลออกแล ้ว (เก็บไว ้ในทีเ่ ก็บชัว่ คราว)
เราก็ต ้องเลือ
่ น front pointer ของเราให ้อยูใ่ นทีท่ เี่ หมาะสม ด ้วยการใช ้คาสัง่

front = (front + 1) % size; //move front pointer

ซึง่ ถ ้าดูแล ้วมันก็คอ


ื คาสัง่ เดียวกันกับการเลือ
่ น rear pointer เราเพียงแต่มาทากับ front pointer
เท่านั น ้

public T remove() {
//exit routine if queue is empty
if(empty()) {
System.err.println("Error: queue is EMPTY!");
System.exit(2);
}
T temp = queue[front]; //remove object
queue[front] = null; //set queue[front] to null
front = (front + 1) % size; //move front pointer
count--; //one object less
return temp; //return removed object
}

Method ตัวอืน ่ ๆ ก็ไม่ยากทีจ


่ ะทาความเข ้าใจ เราเพียงแต่ต ้องรู ้ว่า queue ของเราจะ empty ถ ้า
front และ rear pointer มาอยูใ่ นตาแหน่งเดียวกัน และ queue จะเต็มเมือ ่ ทัง้ สอง pointer อยู่
ถัดกันหนึง่ ตาแหน่ง

Code ของ QueueArray

1: /**
2: Queue using array to store data
3: */
4:
5: class QueueArray<T> {
6: private int size; //size of queue
7: private int count; //# of objects in queue
8: private T[] queue; //space for objects
9: private int front, rear; //front and rear pointers
10:
11: public QueueArray(int max) {
12: size = max + 1; //one size bigger
13: queue = (T[])new Object[size];
14: front = rear = 0; //initial queue: empty
15: count = 0; //no object in queue
16: }
17:
18: //insert object into queue
19: public void insert(T obj) {
20: //exit routine if queue is full
21: if(full()) {
22: System.err.println("Error: queue is FULL!");
23: System.exit(1);
24: }
25: queue[rear] = obj; //insert object
26: rear = (rear + 1) % size; //move rear pointer
27: count++; //one more object added
28: }

86
บทที่ 3 Stack และ Queue

29:
30: //remove object from array
31: public T remove() {
32: //exit routine if queue is empty
33: if(empty()) {
34: System.err.println("Error: queue is EMPTY!");
35: System.exit(2);
36: }
37: T temp = queue[front]; //remove object
38: queue[front] = null; //set queue[front] to null
39: front = (front + 1) % size; //move front pointer
40: count--; //one object less
41: return temp; //return removed object
42: }
43:
44: //when rear pointer is right next to front pointer
45: private boolean full() {
46: if((rear + 1) % size == front)
47: return true;
48: else
49: return false;
50: }
51:
52: //when rear is at the same position as front
53: private boolean empty() {
54: if(rear == front)
55: return true;
56: else
57: return false;
58: }
59:
60: //display queue's content
61: public void printQ() {
62: System.out.print("(" + queue[0]);
63: for(int i = 1; i < queue.length; i++)
64: System.out.print(", " + queue[i]);
65: System.out.println(")");
66: }
67:
68: //count empty spaces
69: public int emptySpaces() {
70: return size - count - 1;
71: }
72: }

หลังจากทีท
่ ดลอง run ดูด ้วยโปรแกรมทีเ่ ห็นนี้

1: /**
2: Testing module for queue
3: */
4:
5: import static java.lang.System.out;
6:
7: class TestQueueArray {
8: public static void main(String[] args) {
9: QueueArray<Integer> queue = new QueueArray<Integer>(10);
10:
11: //insert random Integers into queue
12: out.println("Insert 10 items into queue");
13: for(int i = 0; i < 10; i++) {
14: int object = (int)(Math.random() * 10);
15: queue.insert(object);
16: queue.printQ(); //for viewing purpose only
17: }
18: out.print("Queue after insertion: ");
19: queue.printQ();
20: out.println("Spaces left = " + queue.emptySpaces());
21:
22: //remove 5 objects from queue
23: out.println("\nRemove 5 items from queue");
24: for(int j = 0; j < 5; j++) {
25: Integer object = (Integer)queue.remove();
26: queue.printQ(); //for viewing purpose only

87
Stack และ Queue บทที่ 3

27: }
28: out.print("Queue after removal: ");
29: queue.printQ();
30: out.println("Spaces left = " + queue.emptySpaces());
31:
32: //insert 4 more random Integers into queue
33: out.println("\nInsert 4 items into queue");
34: for(int i = 0; i < 4; i++) {
35: Integer object = new Integer((int)(Math.random() * 10));
36: queue.insert(object);
37: queue.printQ(); //for viewing purpose only
38: }
39: out.print("Queue after insertion: ");
40: queue.printQ();
41: out.println("Spaces left = " + queue.emptySpaces());
42: }
43: }

่ ริง - มากกว่าการใช ้งานจริง


ผลลัพธ์ดังทีไ่ ด ้คือ (เราแสดงผลจานวนความจุของ array ทีเ่ ป็ นอยูจ
หนึง่ ตาแหน่ง)

Insert 10 items into queue


(8, null, null, null, null, null, null, null, null, null, null)
(8, 9, null, null, null, null, null, null, null, null, null)
(8, 9, 4, null, null, null, null, null, null, null, null)
(8, 9, 4, 0, null, null, null, null, null, null, null)
(8, 9, 4, 0, 1, null, null, null, null, null, null)
(8, 9, 4, 0, 1, 0, null, null, null, null, null)
(8, 9, 4, 0, 1, 0, 5, null, null, null, null)
(8, 9, 4, 0, 1, 0, 5, 6, null, null, null)
(8, 9, 4, 0, 1, 0, 5, 6, 5, null, null)
(8, 9, 4, 0, 1, 0, 5, 6, 5, 0, null)
Queue after insertion: (8, 9, 4, 0, 1, 0, 5, 6, 5, 0, null)
Spaces left = 0

Remove 5 items from queue


(null, 9, 4, 0, 1, 0, 5, 6, 5, 0, null)
(null, null, 4, 0, 1, 0, 5, 6, 5, 0, null)
(null, null, null, 0, 1, 0, 5, 6, 5, 0, null)
(null, null, null, null, 1, 0, 5, 6, 5, 0, null)
(null, null, null, null, null, 0, 5, 6, 5, 0, null)
Queue after removal: (null, null, null, null, null, 0, 5, 6, 5, 0, null)
Spaces left = 5

Insert 4 items into queue


(null, null, null, null, null, 0, 5, 6, 5, 0, 0)
(7, null, null, null, null, 0, 5, 6, 5, 0, 0)
(7, 3, null, null, null, 0, 5, 6, 5, 0, 0)
(7, 3, 3, null, null, 0, 5, 6, 5, 0, 0)
Queue after insertion: (7, 3, 3, null, null, 0, 5, 6, 5, 0, 0)
Spaces left = 1

เราใช ้ printQ() เป็ นตัวแสดงผลข ้อมูลทุกตัวทีอ ่ ยูใ่ น queue เพือ


่ ให ้ผู ้อ่านมองเห็นถึงตาแหน่ง
ช่องว่างทีเ่ กิดขึน
้ เมือ
่ มีการนาข ้อมูลเข ้า และการดึงข ้อมูลออก แต่ถ ้าต ้องการแสดงผลเฉพาะ
ข ้อมูลตามทีก ่ าหนดไว ้ด ้วยขนาดความจุของ queue เราต ้องเขียน method ขึน ้ มาใหม่ (ดู
method toString() ในเรือ ่ งของ Priority queue)

3.2.3 Priority Queue

Queue ทีเ่ ราเขียนขึน ้ ยังเป็ น queue ทีใ่ ช ้กันอยูโ่ ดยทั่วไป ยังมี queue อยูอ่ ก
ี หลายแบบทีม่ ก
ี าร
จัดเก็บข ้อมูลแตกต่างออกไป เช่น priority queue ซึง่ เป็ น queue ทีใ่ ช ้ในระบบปฏิบัตก ิ ารใน
แบบ multitasking ในการเก็บข ้อมูลของ queue แบบนี้จะต ้องมีการจัดเก็บตาม priority ทีไ่ ด ้
กาหนดไว ้ ซึง่ อาจเป็ นเงือ ่ นไขใด ๆ ก็ได ้ทีท ่ าให ้ข ้อมูลใน queue มีการจัดเก็บแบบมี order เช่น
ให ้ข ้อมูลทีม
่ ค
ี า่ น ้อยทีส
่ ด
ุ เป็ นข ้อมูลทีม
่ ี priority สูงสุดและการจัดเก็บก็จัดเก็บแบบมากไปหาน ้อย

เรากาหนดให ้การจัดเก็บข ้อมูลมีการเรียงจากมากไปหาน ้อย เพราะฉะนั น ้ เราก็ไม่จาเป็ นทีจ


่ ะต ้อง
กาหนดให ้มี front และ rear สาหรับการบอกตาแหน่งทีต ่ ้องนาข ้อมูลเข ้า และดึงข ้อมูลออก เรารู ้
ว่าข ้อมูล ณ ตาแหน่ง index = 0 คือข ้อมูลทีม
่ ี priority ตา่ ทีส
่ ด
ุ และข ้อมูล ณ ตาแหน่งท ้ายสุด

88
บทที่ 3 Stack และ Queue

คือ ข ้อมูลทีม ้ การดึงออกก็เพียงแต่สง่ ค่า ณ ตาแหน่งท ้ายสุด ส่วนการนา


่ ี priority สูงสุด ดังนั น
ข ้อมูลเข ้านัน
้ จะต ้องหาตาแหน่งทีเ่ หมาะสมให ้เจอ ก่อนทีจ ่ ะมีการใส่ข ้อมูล ณ ตาแหน่งทีห
่ าเจอ
ดังทีแ่ สดงในภาพที่ 3.8

0 1 2 3 4
ข ้อมูลทีม ่ ี
55 50 44 35 33 Priority
สูงทีส่ ด

ขยับข ้อมูล 2 ตัวนีอ


้ อก

ข ้อมูลทีม่ ี
Priority น ้อย
ทีส
่ ด
ุ ตาแหน่งของ
42 42

ภาพที่ 3.8 ตาแหน่งของข ้อมูลทีม


่ ี Priority ตา่ สุด / สูงสุด และการนาข ้อมูลเข ้า

ตัวอย่างของ class PriorityQ ทีเ่ ราได ้สร ้างขึน


1: /**
2: Priority Queue using array + smallest key has high priority
3: */
4:
5: class PriorityQ<T> {
6: private int size, elems;
7: private T[] que;
8:
9: PriorityQ(int max) {
10: size = max;
11: elems = 0;
12: que = (T[])new Object[size];
13: }
14:
15: //insert object into que in descending order
16: public void insert(T value) {
17: int i;
18: for(i = 0; i < elems; i++) {
19: if(((Comparable)que[i]).compareTo(value) < 0)
20: break;
21: }
22: //shift positions
23: for(int j = elems; j > i; j--)
24: que[j] = que[j - 1];
25: //insert element
26: que[i] = value;
27: elems++;
28: }
29:
30: //remove item from queue
31: public T remove() {
32: return que[--elems];
33: }
34:
35: //display contents via System.out.println()
36: public String toString() {
37: StringBuffer buf = new StringBuffer();
38: buf.append("rear->(" + que[0]);
39: for(int i = 1; i < elems; i++)
40: buf.append(", " + que[i]);
41: return new String(buf + ")<-front");
42: }
43: }

89
Stack และ Queue บทที่ 3

เราคงไม่ต ้องอธิบายมากมายนั กในเรือ ่ งของ code ทีใ่ ห ้ด ้านบนนี้ ผู ้อ่านได ้สัมผัสกับการนาข ้อมูล
เข ้าแบบจัดเรียงมาก่อนหน ้านี้แล ้ว ส่วนการนาข ้อมูลออกนั น ้ เราใช ้คาสัง่

return que[--elems];

ซึง่ ทาหน ้าทีล


่ ดค่าของ index ของ array ลงก่อนหนึง่ ค่า ก่อนทีจ
่ ะส่งกลับออกไปให ้ผู ้เรียก

หลังจากที่ run ด ้วยโปรแกรม TestPriorityQ.java ทีใ่ ห ้นี้

1: /**
2: Testing priority queue
3: */
4:
5: import static java.lang.System.out;
6:
7: class TestPriorityQ {
8: public static void main(String[] args) {
9: //create 10 items priority queue
10: PriorityQ<Integer> q = new PriorityQ<Integer>(10);
11: int data;
12:
13: //populate queue with 10 random Integers
14: for(int i = 0; i < 10; i++) {
15: data = (int)(Math.random() * 100) + 1;
16: q.insert(data);
17: }
18: out.println("Queue after inserted 10 items");
19: out.println(q); //display contents of queue
20:
21: //remove 5 items from queue
22: for(int i = 0; i < 5; i++) {
23: data = q.remove();
24: out.println("Removed: " + data);
25: }
26: out.println("Queue after removed 5 items");
27: out.println(q); //display contents of queue
28:
29: //insert 2 more items
30: q.insert(99);
31: q.insert(37);
32: out.println("Queue after inserted 2 more items");
33: out.println(q); //display contents of queue
34: }
35: }

ผลลัพธ์ทไี่ ด ้คือ

Queue after inserted 10 items


rear->(97, 86, 81, 80, 71, 71, 60, 42, 34, 23)<-front
Removed: 23
Removed: 34
Removed: 42
Removed: 60
Removed: 71
Queue after removed 5 items
rear->(97, 86, 81, 80, 71)<-front
Queue after inserted 2 more items
rear->(99, 97, 86, 81, 80, 71, 37)<-front

ผู ้อ่านควรทบทวนถึง code ของ PriorityQ โดยเฉพาะการนาข ้อมูลเข ้า ส่วนการดึงข ้อมูลออกนัน ้


แทบจะไม่มอ ี ะไรเลย เพราะเราดึงข ้อมูลออกทางท ้ายสุด ดังทีไ่ ด ้กล่าวไว ้แล ้วก่อนหน ้านี้

ถ ้าจะพูดถึงประสิทธิภาพของการทางานของ Priority queue สิง่ ทีเ่ ราน่าจะกังวลมากทีส ่ ด ุ คือ


การนาข ้อมูลเข ้า เนื่องจากว่าเราต ้องค ้นหาตาแหน่งทีเ่ หมาะสมของข ้อมูล ดังนั น ้ เวลาของการ
ทางานส่วนใหญ่จงึ ใช ้ไปกับการนาข ้อมูลเข ้า แต่ถ ้าวิเคราะห์ตามหลักการของ Big O แล ้ว
ประสิทธิภาพการทางานของของ Priority queue นั น ้ เป็ นเพียงแค่ O(n) เพราะว่าขึน
้ อยูก่ ับ
จานวนของข ้อมูลทีม ่ อี ยูใ่ น queue เท่านั น
้ (มี loop เพียงหนึง่ loop)

90
บทที่ 3 Stack และ Queue

3.3 กำรใช ้ class ArrayList สร้ำง Stack และ Queue

ArrayList เป็ นโครงสร ้างที่ Java มีให ้สาหรับการทางานกับข ้อมูลทีถ ่ ูกเก็บในลักษณะของ list
หรือ array (ดูบททีห่ นึง่ ) เราจะใช ้ method ต่าง ๆ จาก ArrayList มาช่วยในการจัดการกับข ้อมูล
ของ Stack และ Queue โดยก่อนอืน ่ เราจะมาดูกน
ั ถึงการสร ้าง Stack จาก ArrayList ทีว่ า่

1: /**
2: Stack using ArrayList to manipulate data
3: */
4:
5: import java.util.ArrayList;
6:
7: public class ArrayListStack<T> {
8: private ArrayList<T> list;
9: private int top;
10:
11: //default constructor
12: public ArrayListStack() {
13: list = new ArrayList<T>();
14: top = -1;
15: }
16:
17: //insert item onto stack
18: public void push(T item) {
19: list.add(++top, item);
20: }
21:
22: //remove item from stack
23: public T pop() {
24: return list.remove(top--);
25: }
26:
27: //display content of stack
28: public String toString() {
29: StringBuffer buf = new StringBuffer();
30: buf.append("(" + list.get(0));
31: for(int i = 1; i < list.size(); i++)
32: buf.append(", " + list.get(i));
33: return new String(buf + ")<-top");
34: }
35:
36: //testing module
37: public static void main(String[] args) {
38: //create new stack
39: ArrayListStack<String> stk = new ArrayListStack<String>();
40:
41: //insert 4 items and display content
42: stk.push("Java");
43: stk.push("C++");
44: stk.push("Visual Basic");
45: stk.push("C#");
46: System.out.println(stk);
47:
48: //remove an item and display content afterward
49: System.out.println("Pop " + stk.pop());
50: System.out.println(stk);
51: }
52: }

เราใช ้ method add() สาหรับการ push ใช ้ method remove() สาหรับการ pop ส่วนการ
แสดงผลเราใช ้ get() เป็ นตัวดึงข ้อมูลออกจาก ArrayList ตาม index ทีก
่ าหนดให ้ ผลลัพธ์ทเี่ รา
ได ้จากการ run คือ

(Java, C++, Visual Basic, C#)<-top


Pop C#
(Java, C++, Visual Basic)<-top

ต่อไปเราจะมาดูกันถึงการนาเอา ArrayList มาเป็ นโครงสร ้างของ Queue

1: /**

91
Stack และ Queue บทที่ 3

2: Queue using ArrayList to manipulate data


3: */
4:
5: import java.util.ArrayList;
6:
7: public class ArrayListQueue<T> {
8: private ArrayList<T> list;
9: private int front, rear;
10:
11: //default constructor
12: public ArrayListQueue() {
13: list = new ArrayList<T>();
14: front = rear = 0;
15: }
16:
17: //empty queue
18: public boolean empty() {
19: if(list.isEmpty())
20: return true;
21: return false;
22: }
23:
24: //insert item into queue
25: public void put(T item) {
26: list.add(rear++, item);
27: }
28:
29: //remove item from queue
30: public T get() {
31: if(list.isEmpty()) {
32: System.out.println("ERROR -Queue is empty!");
33: System.exit(1);
34: }
35: return list.remove(front);
36: }
37:
38: //display content of queue
39: public String toString() {
40: if(list.isEmpty())
41: return new String("Queue is empty.");
42:
43: StringBuffer buf = new StringBuffer();
44: buf.append("(" + list.get(0));
45: for(int i = 1; i < list.size(); i++)
46: buf.append(", " + list.get(i));
47: return new String(buf + ")<-rear");
48: }
49:
50: public static void main(String[] args) {
51: //create new queue
52: ArrayListQueue<String> que = new ArrayListQueue<String>();
53: //insert 4 items and display content
54: que.put("Java");
55: que.put("C++");
56: que.put("Visual Basic");
57: que.put("C#");
58: System.out.println(que);
59: //remove item and display content afterward
60: System.out.println("Get " + que.get());
61: System.out.println(que);
62: System.out.println("Get " + que.get());
63: System.out.println(que);
64: System.out.println("Get " + que.get());
65: System.out.println(que);
66: System.out.println("Get " + que.get());
67: System.out.println(que);
68: }
69: }

เรายังคงใช ้ method add() สาหรับการนาข ้อมูลเข ้า และ method remove() สาหรับการนา


ข ้อมูลออก แต่เราใช ้ตัวแปร front และ rear เป็ นตัวกาหนดการนาข ้อมูล เข ้า/ออก ผลลัพธ์ทเี่ รา
ได ้จากการ run คือ

92
บทที่ 3 Stack และ Queue

(Java, C++, Visual Basic, C#)<-rear


Get Java
(C++, Visual Basic, C#)<-rear
Get C++
(Visual Basic, C#)<-rear
Get Visual Basic
(C#)<-rear
Get C#
Queue is empty.

ผู ้อ่านควรพิจารณาถึงข ้อดี (หรือข ้อเสีย) ของการนาสิง่ ทีม ่ อ ่ ล ้วมาประยุกต์ใช ้ใหม่ สาหรับงาน


ี ยูแ
ต่าง ๆ บางครัง้ การออกแบบโครงสร ้างใหม่จากศูนย์อาจเสียเวลาโดยใช่เหตุ หากมีสงิ่ ทีส ่ ามารถ
นามาใช ้ได ้ หลาย ๆ ครัง้ การนาเอาโครงสร ้างทีม่ อ
ี ยูแ
่ ล ้วมาปรับปรุงเพิม ่ เติม เพือ
่ รองรับงานใหม่
ๆ ทาให ้การออกแบบใช ้เวลาน ้อยลง

3.4 กำรใช ้ class Stack ของ java

Java มี class Stack ทีเ่ ราสามารถเรียกใช ้ได ้เหมือนกับ class ตัวอืน่ ๆ โปรแกรมตัวอย่างต่อไปนี้
เป็ นการแสดงการใช ้ class Stack พร ้อมกับการใช ้ for/in loop ทีม ่ อ
ี ยูใ่ น Java 1.5

1: /**
2: Stack from Java (1.5)
3: */
4:
5: import java.util.Stack;
6:
7: class JavaStack {
8: public static void main(String[] args) {
9: Stack<String> stk = new Stack<String>();
10:
11: //push items onto stack
12: stk.push("Chiang Mai");
13: stk.push("Chiang Rai");
14: stk.push("Chiang Kong");
15:
16: printStack(stk);
17:
18: //pop items from stack
19: System.out.println("Pop: " + stk.pop());
20: printStack(stk);
21: }
22:
23: //iterate over stack's content
24: public static void printStack(Stack<String> stack) {
25: System.out.println("Content:");
26: for(String s : stack) {
27: System.out.println(s);
28: }
29: }
30: }

ี ารเรียกใช ้ Stack ก็เหมือนกับการเรียกใช ้ class อืน


วิธก ่ ๆ ในโปรแกรมของเรา เราได ้ประกาศตัว
แปร stk ให ้เป็ น stack ทีม่ ี String เป็ นข ้อมูลด ้วยคาสัง่

Stack<String> stk = new Stack<String>();

ค่าทีอ ่ งหมาย <> จะเป็ นตัวบอกชนิดของข ้อมูลทีเ่ ราต ้องการเก็บใน stack ซึง่ ถ ้าเรา
่ ยูใ่ นเครือ
ต ้องการเก็บ Integer เราก็เพียงแค่ประกาศ

Stack<Integer> s = new Stack<Integer>

การนาข ้อมูลเข ้าก็เช่นเดียวกันกับทีเ่ ราเคยทาคือ เรียกใช ้ method push() ได ้ทันที เช่น

stk.push("Chiang Mai");

93
Stack และ Queue บทที่ 3

สาหรับการแสดงข ้อมูลทัง้ หมดทีม ี ยูใ่ น stack เราเรียกใช ้ for/in loop ซึง่ เป็ น for-loop ตัวใหม่
่ อ
ที่ Java 1.5 มีให ้ วิธกี ารเข ้าหาข ้อมูลทีเ่ ราใช ้ก็คอ ื บอกชนิดของข ้อมูลทีเ่ ราเก็บไว ้ใน stack
ให ้กับ for/in loop ด ้วยการประกาศตัวแปร s ให ้เป็ น String พร ้อมกับบอกทีอ ่ ยูท
่ ี่ string เหล่านี้
ถูกเก็บไว ้ ซึง่ ก็คอ
ื stk (หรือตัวแปรทีช ่ อ ี้ ยูท
่ เี่ ดียวกัน เช่น stack)

for(String s : stack) {
System.out.println(s);
}

เมือ
่ เรา run โปรแกรม JavaStack.java ผลลัพธ์ทเี่ ราได ้คือ

Content:
Chiang Mai
Chiang Rai
Chiang Kong
Pop: Chiang Kong
Content:
Chiang Mai
Chiang Rai

ผู ้อ่านควรใช ้เวลาสักเล็กน ้อยในการศึกษาถึงการใช ้ Stack (หรือแม ้แต่ Queue) ที่ Java มีให ้
จากคูม ่ อ
ื ทีม
่ ใี ห ้จาก sun เพือ
่ ให ้เกิดความเข ้าใจมากยิง่ ขึน
้ ตัวอย่างต่อไปจะเป็ นตัวอย่างสุดท ้าย
ในบทนี้ทเี่ รียกใช ้ class PriorityQueue ของ Java

3.5 กำรใช ้ class PriorityQueue

การเรียกใช ้ class PriorityQueue ก็เหมือนกับการเรียกใช ้ class ArrayList ทีเ่ ราได ้แสดงให ้


ดูกอ
่ นหน ้านี้ ตัวอย่างของเราจะเป็ นตัวอย่างแบบง่าย ๆ ทีแ
่ สดงให ้เห็นการนาข ้อมูลเข ้า และการ
ดึงข ้อมูลออกด ้วยการใช ้ method offer() และ method poll()

1: /**
2: Java's PrioirtyQueue
3: */
4:
5: import java.util.PriorityQueue;
6:
7: class JavaPriorityQueue {
8: public static void main(String[] args) {
9: PriorityQueue<String> q = new PriorityQueue<String>();
10:
11: //insert 4 items
12: q.offer("Chiang Mai");
13: q.offer("Bangkok");
14: q.offer("Ayudhaya");
15: q.offer("Nakhon Nayok");
16:
17: //display items in queue
18: for(String s : q)
19: System.out.println(s);
20:
21: //remove 1 item
22: System.out.println("Item removed: " + q.poll());
23: }
24: }

เรานาข ้อมูลชนิด String เข ้าสู่ queue ด ้วยการเรียกใช ้ method offer() และดึงข ้อมูลออกด ้วย
การเรียกใช ้ poll() ทัง้ นี้ผู ้อ่านสามารถเรียกใช ้ method add() และ method remove() ทีม ่ ใี ห ้ก็
ได ้ การจัดวางข ้อมูลใน priority queue ของ Java นั น ้ จัดวางตาม natural order ของข ้อมูลที่
นาเข ้า ด ้วยการใช ้ comparable interface เป็ นตัวกาหนด

ผลลัพธ์ทเี่ ราได ้จากการ run โปรแกรม JavaPriorityQueue.java คือ

Ayudhaya
Chiang Mai
Bangkok
Nakhon Nayok

94
บทที่ 3 Stack และ Queue

Item removed: Ayudhaya

ผู ้อ่านอาจสงสัยว่าทาไมผลลัพธ์ทไี่ ด ้จึงไม่จัดเรียงตามลาดับก่อนหลังของตัวอักษรทีอ ่ ยูใ่ น


string คาตอบก็คอ ื การแสดงผลด ้วย iterator ใน for/loop นั น ้ ไม่การันตีการจัดเรียงของข ้อมูล
เมือ ่ ส่งออกมา แต่จริง ๆ แล ้วข ้อมูลมีการจัดเรียงซึง่ สามารถตรวจสอบได ้ด ้วยการ poll ข ้อมูลใน
queue ออกให ้หมด

3.6 กำรใช ้ Linked-List สำหร ับกำรสร้ำง Stack และ Queue

เราได ้ออกแบบ Stack ทีใ่ ช ้ array เป็ นตัวจัดเก็บข ้อมูลมาก่อนหน ้านี้ ณ เวลานี้เราจะทดลองใช ้
Linked-List เป็ นตัวเก็บข ้อมูลของ Stack ซึง่ เป็ นสิง่ ทีท
่ าได ้ไม่ยากดังทีจ
่ ะแสดงให ้เห็นด ้วย
code ข ้างล่างนี้ (ดู class MyLinkedList ในบทที่ 2)

1: /**
2: Stack using Linked-List as storage
3: */
4:
5: public class LinkedStack<T extends Comparable<? super T>> {
6: protected MyLinkedList<T> list;
7:
8: //default constructor
9: //calling LinkedList's constructor
10: public LinkedStack() {
11: list = new MyLinkedList<T>();
12: }
13:
14: //insert item onto Stack using LinkedList's insert()
15: public void push(T item) {
16: list.insert(item);
17: }
18:
19: //remove item from Stack using LinkedList's remove()
20: public T pop() {
21: //cannot remove if empty
22: if(empty())
23: return null;
24: //return item removed from list
25: return list.remove();
26: }
27:
28: //calling LinkedList's empty()
29: public boolean empty() {
30: return list.size() == 0;
31: }
32:
33: //display contents of list via System.out.println()
34: public String toString() {
35: //if empty, display top->()
36: //otherwise return new String to avoid null
37: //pointer exception in System.out.println()
38: if(empty()) {
39: System.out.println("top->()");
40: return new String();
41: }
42: //items are in list - display them
43: else {
44: StringBuffer buf = new StringBuffer();
45: buf.append("top->(" + list.peek(0));
46: for(int i = 1; i < list.size(); i++)
47: buf.append(", " + list.peek(i));
48: buf.append(")");
49: return new String(buf);
50: }
51: }
52: }

จาก code ทีใ่ ห ้จะเห็นว่าเราแทบไม่ต ้องทาอะไรเลย นอกจากเรียกใช ้ method ของ class


MyLinkedList ทาหน ้าทีใ่ ห ้เรา ตัง้ แต่การสร ้าง Stack การนาข ้อมูลเข ้า และการแสดงผลข ้อมูล
ส่วนทีเ่ ราได ้ปรับปรุงก็คอ
ื การสร ้าง method toString() ให ้รองรับ null pointer ทีเ่ กิดขึน
้ จาก

95
Stack และ Queue บทที่ 3

การที่ list ไม่มข ี ้อมูลอยู่ (toString() ทีเ่ ราเขียนขึน


้ ก่อนหน ้านีไ้ ม่รองรับข ้อมูลทีเ่ ป็ น null ดังนั น

ผู ้อ่านควรเปลีย่ นให ้เป็ นแบบเดียวกันกับ toString() ทีเ่ ราเขียนขึน ้ ใหม่นี้)

หลังจากทีท
่ ดสอบด ้วยโปรแกรมทีเ่ ห็นนี้

1: /**
2: Testing stack that uses LinkedList stores data
3: */
4:
5: class LinkedStackTest {
6: public static void main(String []args) {
7: LinkedStack<Character> stack = new LinkedStack<Character>();
8:
9: //populate stack with chars
10: for(char i = 'A'; i <= 'K'; i++) {
11: stack.push(i);
12: System.out.println(stack);
13: }
14:
15: //remove 5 items form Stack
16: for(int i = 0; i < 5; i++) {
17: stack.pop();
18: System.out.println(stack);
19: }
20:
21: //insert 2 more chars
22: stack.push('Z');
23: stack.push('Q');
24: System.out.println(stack);
25: }
26: }

ผลลัพธ์ทไี่ ด ้คือ

top->(A)
top->(B, A)
top->(C, B, A)
top->(D, C, B, A)
top->(E, D, C, B, A)
top->(F, E, D, C, B, A)
top->(G, F, E, D, C, B, A)
top->(H, G, F, E, D, C, B, A)
top->(I, H, G, F, E, D, C, B, A)
top->(J, I, H, G, F, E, D, C, B, A)
top->(K, J, I, H, G, F, E, D, C, B, A)
top->(J, I, H, G, F, E, D, C, B, A)
top->(I, H, G, F, E, D, C, B, A)
top->(H, G, F, E, D, C, B, A)
top->(G, F, E, D, C, B, A)
top->(F, E, D, C, B, A)
top->(Q, Z, F, E, D, C, B, A)

จะเห็นได ้ว่าการนาเอา code กลับมาใชใหม่ ้ ลดเวลาในการออกแบบและเขียน code ได ้ดี


พอสมควร สิง่ ทีจ่ ะต ้องคานึงถึงก็คอ
ื กระบวนการต่าง ๆ ทีอ
่ าจต ้องเพิม
่ ให ้กับ code เพือ
่ ให ้การ
ทางานมีประสิทธิภาพมากขึน ้

เช่นเดียวกันกับ Stack เราสามารถทีจ ่ ะเขียน code รองรับการทางานในรูปแบบของ Queue โดย


ใช ้ Linked-List เป็ นตัวจัดการกับข ้อมูลให ้ เราสามารถทีจ ่ ะใช ้ Linked-List แบบใดก็ได ้ทีไ่ ด ้
กล่าวไว ้แล ้วเป็ นตัวจัดการกับข ้อมูลให ้กับ Queue ไม่วา่ จะเป็ น Linked-List ทีเ่ ข ้าและออกทาง
ด ้านหน ้า หรือว่า Linked-List ทีส ่ ามารถทีจ ่ ะนาข ้อมูลเข ้าได ้ทัง้ ทางด ้านหน ้าและด ้านหลัง ทัง้ นี้ก็
ต ้องขึน ่ ับผู ้อ่านว่าต ้องการใช ้ตัวไหน ตัวอย่างทีจ
้ อยูก ่ ะให ้ต่อไปเป็ นการนาเอา Linked-List ทีใ่ ช ้
การใส่ข ้อมูลทางด ้านหลัง และดึงข ้อมูลออกทางด ้านหน ้าเป็ นหลัก เพือ ่ ให ้เหมาะสมกับ
ลักษณะเฉพาะของ Queue (FIFO)

1: /**
2: Queue using LinkedList as storage
3: */
4:

96
บทที่ 3 Stack และ Queue

5: public class LinkedQueue<T extends Comparable<? super T>> {


6: //create list to hold data
7: protected MyLinkedList<T> list;
8:
9: //default constructor
10: //calling LinkedList's constructor
11: public LinkedQueue() {
12: list = new MyLinkedList<T>();
13: }
14:
15: //insert item via LinkeList's insertEnd()
16: public void insert(T item) {
17: list.insertEnd(item);
18: }
19:
20: //remove item via LinkedList's remove()
21: public T remove() {
22: //cannot remove if empty
23: if(empty())
24: return null;
25: //return item removed
26: return list.remove();
27: }
28:
29: //call LinkedList's empty()
30: public boolean empty() {
31: return list.empty();
32: }
33:
34: //display contents of list via System.out.println()
35: public String toString() {
36: //if empty, display front->()
37: //otherwise return new String to avoid null
38: //pointer exception in System.out.println()
39: if(empty()) {
40: System.out.println("front->()<-rear");
41: return new String();
42: }
43: //items are in list - display them
44: else {
45: StringBuffer buf = new StringBuffer();
46: buf.append("front->(" + list.peek(0));
47: for(int i = 1; i < list.size(); i++)
48: buf.append(", " + list.peek(i));
49: buf.append(")<-rear");
50: return new String(buf);
51: }
52: }
53: }

เช่นเดียวกันกับการใช ้ Linked-List ในการเก็บข ้อมูลให ้ Stack การใช ้ Linked-List สาหรับการ


เก็บข ้อมูลก็เช่นเดียวกัน เราเพียงแต่เรียก method ทีเ่ กีย
่ วข ้องของ Linked-List มาทางานให ้เรา
โดยเราจะเน ้นการเรียกใช ้ insertEnd() สาหรับการนาข ้อมูลเข ้า และ remove() สาหรับการดึง
ข ้อมูลออก และเมือ ่ เราทดสอบด ้วยโปรแกรม LinkedQueueTest.java นี้

1: class LinkedQueueTest {
2: public static void main(String []args) {
3: LinkedQueue<Character> queue = new
LinkedQueue<Character>();
4:
5: //populate Stack with 10 random Integers
6: for(char c = 'Z'; c > 'K'; c--) {
7: queue.insert(c);
8: System.out.println(queue);
9: }
10:
11: //remove 5 items form queue
12: for(int i = 0; i < 5; i++) {
13: queue.remove();
14: System.out.println(queue);
15: }
16:

97
Stack และ Queue บทที่ 3

17: //insert 2 more Integers


18: queue.insert('A');
19: queue.insert('B');
20: System.out.println(queue);
21: }
22: }

ผลลัพธ์ทไี่ ด ้คือ

front->(Z)<-rear
front->(Z, Y)<-rear
front->(Z, Y, X)<-rear
front->(Z, Y, X, W)<-rear
front->(Z, Y, X, W, V)<-rear
front->(Z, Y, X, W, V, U)<-rear
front->(Z, Y, X, W, V, U, T)<-rear
front->(Z, Y, X, W, V, U, T, S)<-rear
front->(Z, Y, X, W, V, U, T, S, R)<-rear
front->(Z, Y, X, W, V, U, T, S, R, Q)<-rear
front->(Z, Y, X, W, V, U, T, S, R, Q, P)<-rear
front->(Z, Y, X, W, V, U, T, S, R, Q, P, O)<-rear
front->(Z, Y, X, W, V, U, T, S, R, Q, P, O, N)<-rear
front->(Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M)<-rear
front->(Z, Y, X, W, V, U, T, S, R, Q, P, O, N, M, L)<-rear
front->(Y, X, W, V, U, T, S, R, Q, P, O, N, M, L)<-rear
front->(X, W, V, U, T, S, R, Q, P, O, N, M, L)<-rear
front->(W, V, U, T, S, R, Q, P, O, N, M, L)<-rear
front->(V, U, T, S, R, Q, P, O, N, M, L)<-rear
front->(U, T, S, R, Q, P, O, N, M, L)<-rear
front->(U, T, S, R, Q, P, O, N, M, L, A, B)<-rear

เช่นเดียวกันกับ Stack ทีใ่ ช ้ Linked-List เป็ นตัวจัดเก็บข ้อมูล method อืน


่ ๆ ทีจ
่ ะทาให ้งานต่าง
้ ก็ต ้องมีการออกแบบและนามาใช ้กับ Queue ต่อไป
ๆ ดียงิ่ ขึน

สรุป

ในบทนี้เราได ้พูดถึงการออกแบบ Stack ทัง้ ทีใ่ ช ้ array ทีม


่ ขี ้อจากัดในเรือ
่ งของขนาด และ
array ทีม่ ค
ี วามสามารถทีจ ่ ะขยายขนาดเพือ ่ รองรับการนาข ้อมูลเข ้า รวมไปถึงการออกแบบและ
การใช ้ Queue ทีม ี ารใช ้โครงสร ้างของ circular array เป็ นตัวเก็บข ้อมูล การออกแบบและการ
่ ก
ใช ้งาน Priority queue โดยรวมแล ้วเราได ้พูดถึง

 การใช ้ array ทีม่ ข


ี ้อจากัดในเรือ
่ งของขนาดเป็ นตัวเก็บข ้อมูลของ Stack
 ้
การใช Dynamic array ในการจัดการกับขนาดของ Stack
 ตัวอย่างการใช ้ Stack กับการ reverse String
 ตัวอย่างการใช ้ Stack กับการเปลีย ่ นเลขฐานสิบให ้เป็ นเลขฐานสอง
 การใช ้ circular array ในการเก็บข ้อมูลของ Queue
 การนาข ้อมูลเข ้า และดึงข ้อมูลออกจาก Queue
 การทางานกับ Priority queue
 การออกแบบ Stack และ Queue ด ้วย ArrayList
 การใช ้ class Stack และ PriorityQueue ของ Java
 การใช ้ Linked-List ในการสร ้าง Stack และ Queue

แบบฝึ กห ัด

่ ง่ ค่าของข ้อมูลทีอ
1. จงเขียน method ทีส ่ ยูด
่ ้านล่างสุด (bottom) ของ stack ให ้กับผู ้เรียก ให ้
เขียนโปรแกรมทดสอบ method ทีเ่ ขียนขึน ้

2. จงเขียน method ทีส ่ ง่ ค่าของข ้อมูล ณ ตาแหน่งทีก ่ าหนดให ้ (nth element) ของ stack
(นั บจากด ้านบน หรือ top) ให ้กับผู ้เรียก ให ้เขียนโปรแกรมทดสอบ

3. จงเขียนโปรแกรมทีท
่ าหน ้าทีส ่ ้านล่าง (top to bottom)
่ ลับข ้อมูลของ stack จากด ้านบนสูด

98
บทที่ 3 Stack และ Queue

4. จงเขียนโปรแกรมทีม
่ ี menu สาหรับการทางานต่าง ๆ ทีเ่ กีย ่ วข ้องกับ stack เช่น push,
pop, peek, จานวนข ้อมูลทีม
่ อ
ี ยูใ่ น stack และ display ข ้อมูลทีม ่ อ
ี ยูใ่ น stack ทัง้ หมด

่ นการใช ้ stack ให ้เป็ นการใช ้ queue


5. จากข ้อมูลในข ้อ 4 เปลีย

6. จงเขียนโปรแกรมทีส
่ ลับตาแหน่งข ้อมูลใน queue (front to rear)

7. จากข ้อมูลในข ้อ 2 ให ้ใช ้ queue แทน stack

่ าหน ้าที่ insert ข ้อมูลเข ้าสู่ queue จากทางด ้านหน ้า ให ้เขียน


8. จงเขียน method ทีท
โปรแกรมทดสอบ

9. จงเขียน method ทีด


่ งึ ข ้อมูลออกจากทางด ้านหลังของ queue ให ้เขียนโปรแกรมทดสอบ

10. จงเขียนโปรแกรมทีน ่ าข ้อมูลจาก stack เข ้าสู่ queue โดยทีข


่ ้อมูลทีอ
่ ยูด
่ ้านหน ้าสุดของ
stack (top) ก็ยังอยูด
่ ้านหน ้า ของ queue (front)

11. กาหนดให ้ queue เป็ น queue ทีเ่ ก็บข ้อมูลชนิด Integer จงเขียน method ที่ delete
ข ้อมูลทีเ่ ป็ น negative ทัง้ หมดออกจาก queue โดยทีข่ ้อมูลทีเ่ หลืออยูใ่ น queue ยังคงอยู่
ในตาแหน่งทีเ่ รียงกันอยูแ ่ บบเดิม (same order)

12. จงเขียน method ทีส ่ ง่ ค่า true ถ ้า queue 2 ตัวมีข ้อมูลเหมือนกัน และส่งค่า false ถ ้ามี
ข ้อมูลทีแ
่ ตกต่างกัน

13. จงเขียนโปรแกรมทีน
่ าเอา stack และ queue มาอยูใ่ น menu เดียวกัน (ข ้อ 4 รวมกับข ้อ 5)

14. จงอธิบายถึงการทางานของ Priority queue ทีใ่ ช ้ array ในการจัดเก็บข ้อมูลทีม ่ ก


ี าร
เรียงลาดับ จาก น ้อยไปหามากว่าจะมีผลแตกต่างจากการใช ้ array ทีเ่ รียงจากมากไปหา
น ้อยอย่างไร การดึงข ้อมูลออก และการนาข ้อมูลเข ้าต ้องทาอย่างไร ให ้เขียนโปรแกรม
ทดสอบ

15. จงเขียนโปรแกรมทีใ่ ช ้ circular array ในการเก็บข ้อมูลของ Priority queue

16. จงเขียนโปรแกรมทีใ่ ช ้ class Vector เป็ นโครงสร ้างหลักในการเก็บข ้อมูลของ Queue และ
Stack ให ้เขียนโปรแกรมทดสอบ method ต่าง ๆ ทีม ่ อ
ี ยูใ่ น Queue และ Stack ทีส
่ ร ้างขึน

17. จงสร ้าง class Stack ทีม ่ อมให ้ผู ้ใชน้ าข ้อมูลเข ้า หรือดึงข ้อมูลออก ณ
่ ี method ทีย
ตาแหน่งทีก ่ าหนดให ้

18. เช่นเดียวกับข ้อ 17 แต่เปลีย


่ นเป็ น Queue แทน

่ Queue ทีเ่ ราสามารถเรียกใช ้ในการทางานต่าง ๆ ทีเ่ กีย


19. Java 1.5 มี interface ชือ ่ วข ้องกับ
queue ได ้ โดยทีเ่ ราสามารถจะกาหนดให ้โครงสร ้างของการเก็บข ้อมูลเป็ นอะไรก็ได ้ทีไ่ ด ้มี
การ implement Queue interface เช่น เราอาจประกาศโครงสร ้าง queue ของเราเป็ น

Queue<String> myQueue = new LinkedList<String>();

จงเขียนโปรแกรมทีแ ้
่ สดงการใชโครงสร ้างดังกล่าว โดยมีกระบวนการทีเ่ กีย
่ วข ้องกับ queue
ทัง้ หมด เช่น การนาข ้อมูลเข ้า การดึงข ้อมูลออก เป็ นต ้น

20. จงสร ้าง class PriorityQueue ทีใ่ ช ้ class ArrayList เป็ นโครงสร ้างหลักในการเก็บข ้อมูล
โดยมีเงือ
่ นไขว่า ข ้อมูลทีม
่ ี priority สูงสุดจะต ้องอยูด
่ ้านหลังสุดของ queue เท่านั น
้ และให ้
เขียนโปรแกรมทดสอบโครงสร ้างดังกล่าว

99
Stack และ Queue บทที่ 3

21. กาหนดให ้มี queue อยูส ่ องตัว โดยทีต ่ ัวแรกเป็ น queue ทีม ่ ก
ี ารจัดเรียงข ้อมูลจากน ้อยไป
หามาก และตัวทีส ่ องเป็ น queue ทีม ่ ก
ี ารจัดเรียงจากมากไปหาน ้อย จงเขียนโปรแกรมทีท ่ า
การรวมเอาข ้อมูลทัง้ สองทีอ ่ ยูใ่ น queue เข ้าด ้วยกันโดยกาหนดให ้ ข ้อมูลหลังจากการรวม
แล ้วให ้อยูใ่ นรูปแบบของการจัดเรียงจาก มากไปหาน ้อย หรือน ้อยไปหามาก ทัง้ นี้จะขึน ้ อยู่
กับข ้อมูลทีอ ่ ยูด
่ ้านหน ้าสุดของ queue ทัง้ สองตัว ถ ้าข ้อมูลตัวแรกสุดของ queue ตัวแรก
น ้อยกว่า ข ้อมูลตัวแรกสุดของ queue ตัวทีส ่ องให ้จัดเรียงจากมากไปหาน ้อย ให ้ทาการ
ตรงกันข ้ามถ ้าข ้อมูลตัวแรกไม่เป็ นไปตามทีก ่ ล่าวไว ้

22. จงออกแบบ stack ทีใ่ ช ้ linked list เป็ นโครงสร ้างหลักในการจัดการข ้อมูล

23. จงออกแบบ queue ทีใ่ ช ้ linked list เป็ นโครงสร ้างหลักในการจัดการข ้อมูล

่ นมาใช ้ circular linked list แทน


24. จากโจทย์ในข ้อ 22 ให ้เปลีย

่ นมาใช ้ circular linked list แทน


25. จากโจทย์ในข ้อ 23 ให ้เปลีย

100

You might also like