stack #1 ผู้สอน อาจารย์ ยืนยง กันทะเนตร สาขาวิชาเทคโนโลยีคอมพิวเตอร์เคลื่อนที่ คณะเทคโนโลยีสารสนเทศและการสื่อสาร Website : ict.up.ac.th/yeunyong
หัวข้อวันนี้ ADT of stack Value Definition Operation Definition Implementations of stack (โดยใช้ array) Accessing Stack Example ปัญหาในการสร้างสแตกและวิธีแก้ไข
ลักษณะของ stack ในชีวิตประจำวัน Example of Stack ลักษณะของ stack ในชีวิตประจำวัน
ADT : Value Definition ข้อมูลที่จะเก็บในสแตกจะมีค่าเป็นอย่างไรก็ได้ ขึ้นอยู่กับว่าเราจะเก็บข้อมูลอะไร เช่น อาจเป็นตัวเลข ตัวอักษร หรือข้อมูลเชิงโครงสร้าง (Structure Data Type) มีความสัมพันธ์แบบเชิงเส้น ข้อมูลถูกเก็บเป็นแถวเรียงต่อกันไปในหน่วยความจำ มีลำดับของการใส่ข้อมูลและนำข้อมูลออก
ADT : Value Definition (cont’) ข้อมูลที่เข้าสแตกทีหลังจะทับอยู่บนตัวก่อนหน้า ข้อมูลที่จะนำออกจากสแตกได้ต้องเป็นข้อมูลที่อยู่ตำแหน่งบนสุดเท่านั้น เรียกตำแหน่งนี้ว่า top of stack ข้อมูลที่นำเข้าไปในสแตกทีหลังจะถูกนำออกมาใช้งานก่อน เข้าหลังออกก่อน (Last In First Out : LIFO)
ADT : Operation Definition การเพิ่มข้อมูล (push) ใส่ข้อมูลลงด้านบนสุดของโครงสร้าง (วางทับตำแหน่ง top of stack เดิม และส่งผลให้ตำแหน่งของข้อมูลตัวนั้นกลายเป็น top of stack ตัวใหม่) การดึงข้อมูล (pop) นำข้อมูลที่อยู่ด้านบนสุดของโครงสร้างออก (ดึงข้อมูล ณ ตำแหน่ง top of stack ออก และส่งผลให้ตำแหน่ง top of stack ย้ายไปอยู่ที่ตำแหน่งของข้อมูลลำดับถัดลงไป)
แสตก sว่าง Top = 0 s Top = 1 Top = 2 Top = 1 s ใส่ ‘a’ push(‘a’,s) ใส่ ‘b’ push(‘b’,s) a s a s b X=pop(s) b a X=‘b’
Top = 2 Top = 3 Top = 2 Top = 3 ใส่ ‘a’ push(‘a’,s) a s ใส่ ‘c’ push(‘c’,s) a s a s c y=pop(s) ใส่ ‘d’ push(‘d’,s) a s c d a y=‘c’
Implementation of stack (โดยใช้ array )
จาก ADT ได้ข้อสังเกต โครงสร้างสแตก มีลักษณะเป็นสายข้อมูลเรียงต่อกัน นำข้อมูลเข้า/ออก ได้ที่ปลายสายเท่านั้น Array มีลักษณะโครงสร้างเป็นสายข้อมูลเช่นกัน แต่นำข้อมูลเข้า/ออก ที่ตำแหน่งไหนก็ได้ สร้าง Array และบังคับให้เข้าถึงได้เฉพาะปลายด้านหนึ่ง โครงสร้างสแตก ปัญหา -> จะรู้ได้อย่างไรว่าตอนนี้ปลายสายอยู่ที่ตำแหน่งไหน?
ภาษาซี นิยมสร้างสแตกโดยกำหนดเป็นตัวแปรแบบ structure ที่ประกอบไปด้วยเขตข้อมูล 2 เขต คือ อาร์เรย์ที่ใช้เก็บข้อมูลของสแตก (อาจเป็นอาร์เรย์ของข้อมูลเชิงเดี่ยวหรืออาร์เรย์ของข้อมูลเชิงโครงสร้างก็ได้) จำนวนเต็มที่บอกตำแหน่งปัจจุบันของส่วนบนสุดของสแตก (ทำหน้าที่บอก index ของ top of stack) Operation ที่เกี่ยวข้อง เขียนเป็นฟังก์ชัน โดยให้สามารถทำงานตาม ADT ได้
Accessing Stack index D C B A โครงสร้างข้อมูลสแตก การเข้าถึงข้อมูลในโครงสร้างสแตก เมื่อสร้างด้วยอาร์เรย์ จำเป็นต้องอาศัย index ซึ่งทำหน้าที่ชี้ตำแหน่งของข้อมูลตัวบนสุดของสแตก (top of stack) เสมอ D index C (Top of stack) B A โครงสร้างข้อมูลสแตก
Accessing Stack (cont.) index 100 1 2 3 99 … 1 8 … 2 8 2 … 3 8 2 6 … แสดงโครงสร้างข้อมูลสแตกชื่อ Stack[100]
Ex. การสร้างสแตก #define STACKSIZE 10 //กำหนดขนาดของสแตก typedef struct { int data[STACKSIZE]; // กำหนดเป็นสแตกของ int // โดยใช้ตัวแปรอาร์เรย์ int index; // กำหนด index ของสแตก } STACK; STACK s; // กำหนดตัวแปรสแตกไว้ใช้งาน
Ex. การสร้างฟังก์ชัน push() parameter ข้อมูลที่จะใส่ลงสแตก ชื่อของตัวแปรสแตกที่จะใส่ข้อมูลลงไป return value ไม่มี body of function เพิ่มค่าตัวแปร index (เพิ่มตำแหน่ง top of stack) นำข้อมูลใส่อาร์เรย์ ณ ตำแหน่ง index (push ค่าลง ณ ตำแหน่ง top of stack)
Ex. การสร้างฟังก์ชัน pop() parameter ชื่อของตัวแปรสแตกที่จะดึงข้อมูลออกมา return value ผลลัพธ์ของการดึงข้อมูล (ชนิดตามชนิดของข้อมูลใน stack) body of function return ข้อมูลในอาร์เรย์ ณ ตำแหน่ง index ออกจากฟังก์ชัน (pop ค่า ณ ตำแหน่ง top of stack ออก) ลดค่า index ลงมา 1 ตำแหน่ง (ลดตำแหน่ง top of stack ลง)
Ex. ฟังก์ชัน push() และ pop() void push ( int x , STACK *s ){ s->index = s->index+1; s->data[s->index] = x; } int pop ( STACK *s ){ return s->data[s->index--]; ถ้ามีข้อมูลเต็มอาร์เรย์? ตอนเริ่มทำงาน stack index (s->index) ชี้ไปที่ไหน ? ถ้าไม่มีข้อมูลในอาร์เรย์ ?
ปัญหาในการสร้างสแตก stack overflow – ปัญหาที่จะเกิดขึ้นเมื่อมีการนำเข้าข้อมูล (push) ลงสแตก (อาร์เรย์) ที่มีข้อมูลอยู่เต็มแล้ว stack underflow – ปัญหาที่จะเกิดขึ้นเมื่อมีการดึงข้อมูล (pop) จากสแตก (อาร์เรย์) ที่ไม่มีข้อมูลอยู่ จำเป็นต้องมีฟังก์ชันที่จะทำการกำหนดค่าเริ่มต้นของตำแหน่ง top of stack ให้กับตัวแปรนั้น (กระทำหลังจากที่ประกาศตัวแปรชนิดสแตกเสร็จ)
Ex. ฟังก์ชัน stack_initial(), stack_empty(), stack_full() เนื่องจากอาร์เรย์ในภาษาซีข้อมูลตัวแรกเริ่มต้นที่ลำดับที่ 0 , และข้อมูลตัวสุดท้ายคือลำดับที่ n-1 เมื่อ n คือขนาดของอาร์เรย์ ดังนั้นเมื่อเราสร้างสแตกด้วยอาร์เรย์ในภาษาซีจึงต้องคำนึงถึงลักษณะของอาร์เรย์ด้วย อาจกำหนดได้ดังนี้ สแตกว่าง – stack index มีค่า -1 สแตกเต็ม – stack index มีค่า n-1
Ex. การสร้างฟังก์ชัน stack_initial() parameter ชื่อของตัวแปรสแตกที่จะกำหนดค่าเริ่มต้น return value ไม่มี body of function กำหนดค่า index เป็น -1 พึ่งประกาศตัวแปร ดังนั้นสแตกจึงยังว่างอยู่
Ex. stack_initial() void stack_initial ( STACK *s ) { s->index = -1; } เรียกใช้หลังจากสร้าง stack แค่ครั้งเดียว กำหนดตำแหน่งเริ่มต้นของ top of stack
Ex. การสร้างฟังก์ชัน stack_empty() parameter ชื่อของตัวแปรสแตกที่จะตรวจสอบว่าว่างหรือไม่ return value มีได้ 2 สถานะ คือ ว่าง (1) หรือ ไม่ว่าง (0) body of function ตรวจสอบว่าสแตกว่างหรือไม่ (index == -1) ถ้าใช่ return 1 ถ้าไม่ใช่ return 0
Ex. stack_empty() int stack_empty ( STACK *s ) //ถ้า return 1 ,สแตกว่าง { //ถ้า return 0 ,สแตกไม่ว่าง if (s->index == -1) return 1 ; else return 0 ; } เรียกใช้ทุกครั้งก่อนที่จะ pop ข้อมูลจากสแตก ถ้า return 1 -> ห้าม pop (อาจแสดง error บอกผู้ใช้) ถ้า return 0 -> เรียกใช้ฟังก์ชัน pop() เพื่อดึงข้อมูล
Ex. การสร้างฟังก์ชัน stack_full() parameter ชื่อของตัวแปรสแตกที่จะตรวจสอบว่าเต็มหรือไม่ return value มีได้ 2 สถานะ คือ เต็ม (1) หรือ ไม่เต็ม (0) body of function ตรวจสอบว่าสแตกเต็มหรือไม่ (index == STACKSIZE-1) ถ้าใช่ return 1 ถ้าไม่ใช่ return 0
Ex. stack_full() int stack_full ( STACK *s ) //ถ้า return 1 ,สแตกเต็ม return (s->index == (STACKSIZE-1)) ; } เรียกใช้ทุกครั้งก่อนที่จะ push ข้อมูลลงสแตก ถ้า return 1 -> ห้าม push (อาจแสดง error บอกผู้ใช้) ถ้า return 0 -> เรียกใช้ฟังก์ชัน push() เพื่อใส่ข้อมูล
การบ้าน ให้เขียนโปรแกรมแสดงการทำงานของสแตกตามที่ได้กำหนดไว้ด้วย ADT เป็นสแตกของ int ที่สร้างด้วยอาร์เรย์ขนาด 5 ช่อง โปรแกรมมีเมนูอย่างน้อย 3 เมนู คือ push, pop , exit โปรแกรมต้องมีการตรวจสอบปัญหา stack overflow, stack underflow และมีการกำหนดค่าเริ่มต้นเพิ่มเติมจากโปรแกรมด้วย