stack queue...
TRANSCRIPT
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 ตาง ๆ
ภาพท 3.1 การวางหนงสอเรยงกนเปนชน – stack of books
Stack เปนการจดเกบขอมลในลกษณะของการน าขอมลมาวางซอนกน เหมอนกบการวางถวยชามทเราคนเคยกนอยประจ า (ถาเราเคยอยในครวมากอน หรอไปกนกวยเตยวบอย ๆ) หรอการน าเอาหนงสอมาวางเรยงกนเปนชน ๆ ดงทแสดงในภาพท 3.1 เราสามารถทจะเอาคณสมบตนมาใชในการเกบขอมลตาง ๆ ทเราเหนวาเหมาะสม ดงตวอยางทไดกลาวมาแลว การเขยน code
บนสด (top) - เขาหลงสด
ลางสด (bottom) - เขากอน
Stack และ Queue บทท 3
70
ของ 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);
}
}
ภาพท 3.2 การใสขอมลเขาส stack (push operation)
Operation
Book 5
Book 4
Book 3
Book 2
Book 1
Push
Book 5
Book 4
Book 3
Book 2
Book 1
Book 6
Book 6
Top
Top
Stack กอนการใส Book 6 Stack หลงจากการใส Book 6
บทท 3 Stack และ Queue
71
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--];
}
ภาพท 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[])
Top Operation Book 5
Book 4
Book 3
Book 2
Book 1
Pop
Book 5
Book 4
Book 3
Book 2
Book 1
Book 6 Top
Stack กอนการดง Book 6 ออก Stack หลงจากดง Book 6ออก
Book 6
Stack และ Queue บทท 3
72
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
บทท 3 Stack และ Queue
73
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()) {
Stack และ Queue บทท 3
74
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
3.1.1.4 กำรเปลยนเลขฐำนสบใหเปนเลขฐำนสองดวยกำรใช stack 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
บทท 3 Stack และ Queue
75
3.1.1.5 กำรใช stack กบ Postfix-expression ในการประมวลผลสมการทางคณตศาสตร เชน 3 * ((4 – 2) * (5 + 4) + 3) (เราเรยกรปแบบการเขยนสมการนวา Infix notation) นน เราสามารถทจะน าเอา stack มาชวยได แตเราตองท าใหสมการมรปแบบทเออตอการประมวลผลทวาเสยกอน และรปแบบทชวยใหการน าเอา stack มาใชในการประมวลผลไดนนกคอ การเปลยนสมการใหอยในรปของ Postfix notation สมการทอยในรปแบบของ postfix จากตวอยางกอนหนานคอ 3 4 2 – 5 4 + * 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++) {
Stack และ Queue บทท 3
76
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
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);
ขอมลใน stack ณ เวลาตาง ๆ ของการประมวลผล
บทท 3 Stack และ Queue
77
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
Stack และ Queue บทท 3
78
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);
}
บทท 3 Stack และ Queue
79
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:
Stack และ Queue บทท 3
80
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;
บทท 3 Stack และ Queue
81
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;
}
Stack และ Queue บทท 3
82
เราก าหนดใหขนาดของความจเบองตนของ 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 นนถกตอง
บทท 3 Stack และ Queue
83
ผอานจะเหนวาโครงสรางของ Stack ทเราสรางขนใหมนนท างานรองรบการน าเขาไดดกวาทเราไดออกแบบไวกอนหนาน เราไมตองกงวลถงความจทจ ากดของ Stack อกตอไป อยางไรกตามโครงสรางของ Stack ทเราไดออกแบบไวยงมอกหลายสวนทสามารถน าไปปรบปรงเพอใหการใชงานท าไดงายขน เชน ในการ pop ขอมลออกจาก Stack นนเรายงตองท าการ cast ใหอยในรปของ object ทเราไดก าหนดไว คงจะเปนส งทดถาเราสามารถทจะรองรบการน าขอมลเขา และออกจาก Stack โดยไมตองท าการ cast หรอกงวลกบชนดของขอมลทเราก าลงท าอย 3.2 Queue Queue เปนโครงสรางทเกบขอมลในรปแบบทเรยกวา First In, First Out (เขากอน – ออกกอน) หรอ FIFO queue มาจากภาษาองกฤษทแปลวา การจดอนดบ หรอ การจดแถว ดงทแสดงในภาพท 3.4
ภาพท 3.4 การเขาแถวเพอรอรบการบรการ
queue ทเราจะเขยนขนกจะยงคงใช array เปนตวเกบขอมล แตเราจะใช array ทน าเอาเนอททวางลงเมอมการดงขอมลออกไปกลบมาใชใหม วธนจะท าใหเรามท ทสามารถจะจดเกบขอมลได ดกวา array แบบปกต เราเรยก array แบบนวา circular array ผอานอาจใช array ธรรมดาใน
การเกบขอมลกได แตการบรหารและจดการกบเนอทมปญหามากกวาการจดเกบดวย circular array กลาวคอเมอมการน าขอมลเขา เราจะน าไปใสไวท rear และเลอนไปอกหนงต าแหนง และเมอดงออกเรากดงออกจากทาง front และกเลอนไปอกหนงต าแหนง วธการแบบนท าใหเราเสยเนอททางดานหนาของ array เมอเราดงขอมลออก ดงทแสดงในภาพท 3.5 น
ภาพท 3.5 การสญเสยเนอทจากการดงขอมลออก
เนองจาก queue เปนโครงสรางทน าขอมลเขาส queue ทางดานหลงและดงขอมลออกทาง
ดานหนา ดงนนการเขยน code จะตองจ าต าแหนงของขอมลทงทอยดานหนาและดานหลงของ queue อกส งหนงทเราจะตองค านงถงกคอ การตรวจสอบวา queue เตม (full) หรอวาง (empty) เนองจาก circular queue น าเอาชองวางทเหลออยมาใช ดงนนการตรวจสอบจงตองใช index 2 ตวเปนตวชวย เราใช front และ rear เปน index ทอยดานหนาและดานหลงของ queue ตามล าดบ เราตรวจสอบวา queue วาง (empty) ดวยประโยค
(front == rear)
หนา (front) หลง (rear)
Queue of Java cups
55 78 12 7 85 23 77 235
front rear
เนอทสญเสยหลงจากการดงขอมลออก
…
Stack และ Queue บทท 3
84
และ queue เตมดวยประโยค (rear + 1) % size == front
เรามาลองดภาพตวอยางสถานการณทงสองทไดกลาวมาแลว ภาพแรกเปนสถานะของ queue ในขณะทไมมขอมลอย
ภาพท 3.6 สถานะภาพขณะท queue ยงไมมขอมล
เนองจากวาเราใชการค านวณดวย (rear + 1) % size == front เปนตวตรวจสอบความจเตมของ queue ดงนนเราจะเสยชองวางไปหนงท ดงทแสดงในภาพท 3.7
ภาพท 3.7 ชองวางทเสยไปในขณะท queue เตม
สมมตวามการดงขอมลออกจาก queue 2 ตวหลงจากท queue เตม เรากน าชองวางท rear ช
อยมาใช พรอมทงท าการก าหนดต าแหนงของ rear ใหมใหรองรบการใสขอมลดวยการวนกลบของ rear ดงทแสดงใหดในภาพท 3.8
0 1 2 3 4
front
rear
ในการสราง queue ครงแรก เราก าหนดให front และ rear ชไปทต าแหนงเดยวกนคอ 0 และเราจะเลอน rear ทกครงทมการน าขอมลเขาส queue
หลงจากทเราใสขอมลเขาส queue 4 ตวเรากจะ
ไดภาพดงทเหน ซงเปนสถานะภาพของ queue ทเตม คอ (rear + 1) % size == front [(4 + 1) % 5 == 0] จะเหนวาเราเสยชองวางไปหนงชองวาง ซงเปนหนงในวธทเราเลอกใชในการ
ออกแบบโครงสราง queue ของเรา
45 89 23 10
0 1 2 3 4
front rear
บทท 3 Stack และ Queue
85
ภาพท 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
1 0 2 4 3
45 89 23 10
0 1 2 3 4
front rear
45 89 99 23 10
front rear
หลงจากทเราดงขอมลออก 2 ตว front กจะมาช
ในต าแหนง index = 2 ซงท าใหการตรวจสอบวา queue เตมเปน false [(4 + 1) % 5 == 2] false ดงนนเราจงน าขอมล 99 ใส ณ
ต าแหนงท rear = 4 ชอยพรอมทงท าการเลอน rear ดวยประโยค rear = (rear + 1) % size ดงนน rear จงไปชท 0 ซงเปนต าแหนงแรกของ array ทเราน ากลบมาใชใหม
อยางไรกตามถาเราใสขอมลอกตว rear กจะมาช
ท index = 1 ซงจะท าใหประโยคของการตรวจสอบวา queue เตมเปนจรง เรากยงคงเสยชองวางอกหนงชองเหมอนเดม
วาง 2 ชอง
Stack และ Queue บทท 3
86
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: }
บทท 3 Stack และ Queue
87
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
Stack และ Queue บทท 3
88
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 ต าทสด และขอมล ณ ต าแหนงทายสด
บทท 3 Stack และ Queue
89
คอ ขอมลทม priority สงสด ดงนนการดงออกกเพยงแตสงคา ณ ต าแหนงทายสด สวนการน าขอมลเขานนจะตองหาต าแหนงทเหมาะสมใหเจอ กอนทจะมการใสขอมล ณ ต าแหนงทหาเจอ ดงทแสดงในภาพท 3.8
ภาพท 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: }
4 3 2 1 0
55
ขอมลทม Priority นอย
ทสด 42
ต าแหนงของ 42
ขยบขอมล 2 ตวนออก
ขอมลทม Priority
สงทสด
50 44 35 33
Stack และ Queue บทท 3
90
เราคงไมตองอธบายมากมายนกในเรองของ 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)
บทท 3 Stack และ Queue
91
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: /**
Stack และ Queue บทท 3
92
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 คอ
บทท 3 Stack และ Queue
93
(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");
Stack และ Queue บทท 3
94
ส าหรบการแสดงขอมลทงหมดทมอยใน 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
บทท 3 Stack และ Queue
95
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 ทเกดข นจาก
Stack และ Queue บทท 3
96
การท 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:
บทท 3 Stack และ Queue
97
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:
Stack และ Queue บทท 3
98
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. จงเขยนโปรแกรมทท าหนาทสลบขอมลของ stack จากดานบนสดานลาง (top to bottom)
บทท 3 Stack และ Queue
99
4. จงเขยนโปรแกรมทม menu ส าหรบการท างานตาง ๆ ทเกยวของกบ stack เชน push, pop, peek, จ านวนขอมลทมอยใน stack และ display ขอมลทมอยใน stack ทงหมด
5. จากขอมลในขอ 4 เปลยนการใช stack ใหเปนการใช queue 6. จงเขยนโปรแกรมทสลบต าแหนงขอมลใน queue (front to rear) 7. จากขอมลในขอ 2 ใหใช queue แทน stack 8. จงเขยน method ทท าหนาท insert ขอมลเขาส queue จากทางดานหนา ใหเขยน
โปรแกรมทดสอบ
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 แทน 19. Java 1.5 ม interface ชอ Queue ทเราสามารถเรยกใชในการท างานตาง ๆ ทเกยวของกบ
queue ได โดยทเราสามารถจะก าหนดใหโครงสรางของการเกบขอมลเปนอะไรกไดทไดมการ implement Queue interface เชน เราอาจประกาศโครงสราง queue ของเราเปน
Queue<String> myQueue = new LinkedList<String>(); จงเขยนโปรแกรมทแสดงการใชโครงสรางดงกลาว โดยมกระบวนการทเกยวของกบ queue ทงหมด เชน การน าขอมลเขา การดงขอมลออก เปนตน
20. จงสราง class PriorityQueue ทใช class ArrayList เปนโครงสรางหลกในการเกบขอมล
โดยมเงอนไขวา ขอมลทม priority สงสดจะตองอยดานหลงสดของ queue เทานน และให เขยนโปรแกรมทดสอบโครงสรางดงกลาว
Stack และ Queue บทท 3
100
21. ก าหนดใหม queue อยสองตว โดยทตวแรกเปน queue ทมการจดเรยงขอมลจากนอยไปหามาก และตวทสองเปน queue ทมการจดเรยงจากมากไปหานอย จงเขยนโปรแกรมทท าการรวมเอาขอมลทงสองทอยใน queue เขาดวยกนโดยก าหนดให ขอมลหลงจากการรวมแลวใหอยในรปแบบของการจดเรยงจาก มากไปหานอย หรอนอยไปหามาก ทงนจะข นอยกบขอมลทอยดานหนาสดของ queue ทงสองตว ถาขอมลตวแรกสดของ queue ตวแรกนอยกวา ขอมลตวแรกสดของ queue ตวทสองใหจดเรยงจากมากไปหานอย ใหท าการตรงกนขามถาขอมลตวแรกไมเปนไปตามทกลาวไว
22. จงออกแบบ stack ทใช linked list เปนโครงสรางหลกในการจดการขอมล 23. จงออกแบบ queue ทใช linked list เปนโครงสรางหลกในการจดการขอมล
24. จากโจทยในขอ 22 ใหเปลยนมาใช circular linked list แทน 25. จากโจทยในขอ 23 ใหเปลยนมาใช circular linked list แทน