data structure lesson10

Page 1

!"!

แผนการสอนประจําบทเรียน รายชือ่ อาจารยผจู ดั ทํา สมชาย ประสิทธิจ์ ตู ระกูล หัวขอของเนื้อหา ตอนที่ 10.1 นิยามและการประยุกต (1 คาบ) เรือ่ งที่ 10.1.1 นิยามของตนไม เรือ่ งที่ 10.1.2 การประยุกตตนไม ตอนที่ 10.2 การสรางและการดําเนินการตางๆ (2 คาบ) เรือ่ งที่ 10.2.1 การสรางตนไม เรือ่ งที่ 10.2.2 การดําเนินการตางๆ กับตนไม แนวคิด 1. ตนไมเปนโครงสรางที่จัดเก็บขอมูลที่มีความสัมพันธกันเปนลําดับชั้น 2. ตนไมเปนโครงสรางที่ไดรับการนําไปประยุกตมากมาย 3. วิธีการสรางขอมูลในตนไมกระทําไดทั้งแบบใชแถวลําดับเก็บขอมูลตามโหนดของตนไม ประกอบกับการใชสูตรคํานวณตําแหนงของโหนดลูก หรือใชการจองเนื้อที่สําหรับโหนดตางๆ ของตนไม ซึ่งประกอบดวยขอมูลและตัวชี้ตําแหนงของโหนดลูกๆ 4. การดําเนินงานตางๆ ของตนไมจะเปนเชนไรขึ้นการนําตนไมไปใชงาน 5. การเขียนโปรแกรมสําหรับการดําเนินการตางๆ กับตนไมมักเปนแบบเวียนเกิด 6. การแวะผานตนไมเปนกระบวนการเขาถึงขอมูลทุกๆ ตัวในตนไมอยางมีระบบ วัตถุประสงค หลังจากศึกษาบทเรียนที่ 10 แลว นักศึกษาสามารถ 1. 2. 3. 4.

เขาใจโครงสรางและนิยามตางๆ ของตนไม ยกตัวอยางการประยุกตตนไมได สรางตนไมแบบทวิภาคได เขียนโปรแกรมแบบเวียนเกิดเพื่อหาคุณสมบัติ หรือเพือ่ การดําเนินการตางๆ กับตนไมได


!"# กิจกรรมการเรียนการสอน กิจกรรมทีน่ กั ศึกษาตองทําสําหรับการเรียนการสอน ไดแก 1. ศึกษาเอกสารชุดวิชา/โฮมเพจชุดวิชา ตอนที่ 10.1 และตอนที่ 10.2 2. ทํากิจกรรมของบทเรียนที่ 10 3. ทําแบบประเมินผลของบทเรียนที่ 10 เอกสารประกอบการสอน 1. เอกสารชุดวิชา สื่อการสอน 1. โฮมเพจชุดวิชา 2. สไลดประกอบการบรรยาย (Powerpoint) 3. โปรแกรมคอมพิวเตอร ประเมินผล 1. ประเมินผลจากกิจกรรมที่ทํา 2. ประเมินผลจากคําถามทายบทเรียน


!"$ ตอนที่ 10.1 นิยามและการประยุกต หัวเรื่อง เรือ่ งที่ 10.1.1 นิยามของตนไม เรือ่ งที่ 10.1.2 การประยุกตตนไม แนวคิด 1. ตนไมเปนโครงสรางที่จัดเก็บขอมูลที่มีความสัมพันธกันเปนลําดับชั้น 2. ตนไมเปนโครงสรางที่ไดรับการนําไปประยุกตมากมาย วัตถุประสงค หลังจากที่ศึกษาตอนที่ 10.1 แลว นักศึกษาสามารถ 1. เขาใจโครงสรางและนิยามตางๆ ของตนไม 2. ยกตัวอยางการประยุกตตนไม เรื่องที่ 10.1.1 นิยามของตนไม ใครที่เคยศึกษาทฤษฎีกราฟ (graph theory) ก็คงรูวาตนไมเปนกราฟแบบพิเศษ และมีตนไมมาก มายหลากหลายแบบ ไมวาจะเปนตนไมแบบมีราก (rooted tree) ตนไมแบบอันดับ (ordered tree) ตนไม m ภาค (m-ary tree)เปนตน จะขอเริ่มอธิบายถึงลักษณะของตนไมแบบมีรากกันกอน ตนไมแบบมีราก ประกอบไปดวยเซตของโหนด (node) และเซตของเสนเชื่อม (edge) ที่ระบุทิศทางซึ่งเชื่อมตอระหวางคูของ โหนดตางๆ โดยมีกฏเกณฑดังนี้ • มีโหนดพิเศษโหนดหนึ่งเรียกวารากของตนไม • สําหรับโหนด x ใดๆ (ยกเวนราก) จะตองมีเสนเชือ่ มเพียงเสนเดียว (ซึง่ ตอออกจากโหนดอืน่ เพียงโหนดเดียว) พุงเขาหา x ! " %

!

&

"

$

# '

+

(

)

*

%

$

#

&

'

+

(ก)

(ข)

ภาพประกอบ 10.1 ตัวอยางตนไมแบบมีราก

(

)

*


!"% นิยามใหวิถี (path) จากโหนด x ถึง y คือลําดับของเสนเชือ่ มตางๆ เริม่ จากเสนทีพ่ งุ ออกจาก x ผาน โหนดตางๆ ตามทิศทางของเสนเชื่อมแลวไปพบโหนด y ดวยกฏสองขอขางบนนี้สรุปไดวามีเพียงวิถีเดียว เทานั้นจากรากถึงโหนดใดๆ ในตนไม เรานิยามใหความยาววิถี (path length) คือจํานวนเสนเชือ่ มบนวิถี ภาพประกอบ 10.1(ก) แสดงตัวอยางของตนไม เนื่องจากโดยทั่วไปเราวาดตนไมโดยใหรากอยูบนสุด แลว วางตําแหนงของโหนดไลลงมาเปนระดับ โดยมีแตเสนเชื่อมพุงจากบนลงลาง ดังนัน้ เราสามารถวาดเสนเชือ่ ม โดยไมตองเขียนหัวลูกศรก็ยังคงสื่อความหมายเชนกัน (ดังภาพประกอบ 10.1(ข)) จุดเดนของตนไมก็อยูตรงที่ความสัมพันธของโหนดตางๆ ที่ถูกจัดวางเปนระดับๆ (ซึ่งจะไดเห็นใน รายละเอียดตอไป) เพื่อใหเห็นภาพระหวางการบรรยาย จะขอเรียกความสัมพันธของโหนดตางๆ ในตนไม แบบแม ลูก หลาน บรรพบุรษุ (เทียบเคียงกับการใชตนไมแสดงความสัมพันธของบุคคลตางๆ ในตระกูล) ถา มีเสนเชื่อมพุงจาก x ไป y เราก็เรียก x วาเปนโหนดแม (parent node) ของ y และในทางกลับกันก็เรียก y วา เปนโหนดลูก (child node) ของ x โหนดตางๆ ที่มีโหนดแมเดียวกันก็เรียกวาเปนโหนดพี่นอง (sibling nodes) และเรียกโหนดทีเ่ ปนบรรพบุรษุ วาโหนดบน (ancestor nodes) และโหนดทีเ่ ปนลูกๆ หลานๆ วา โหนดลาง (descendant node) ตัวอยางเชนตนไมในภาพประกอบ 1 มี a เปนราก โดยที่ b, c และ d เปนลูก ของ a โหนด e, f และ k เปนโหนดลางของ b โหนด a, b และ e เปนโหนดบนของ k และโหนด h, i และ j เปนโหนดพีน่ อ งกัน เปนตน ดังนั้นรากก็คือโหนดที่ไมมีโหนดแมนั่นเอง เราเรียกโหนดที่ไมมีลูกวาใบ (leaf) หรือบางครั้งเรียกวา โหนดภายนอก (external node) และเรียกโหนดที่มีลูกวาโหนดภายใน (internal node) เราอาจมองวาตนไม ตนหนึ่งก็ประกอบดวยตนไมยอยๆ ตอๆ กัน ดังนัน้ โหนดๆ หนึ่งจะเปนรากของตนไมยอยซึ่งประกอบดวย โหนดนั้นกับโหนดลางตางๆ ของโหนดนั้น เชน b ก็เปนรากของตนไมยอยที่ประกอบดวย b, e, f, และ k เปนตน ทุกๆ โหนดในตนไมมีลักษณะประจํากํากับโหนด อันไดแกความลึกและความสูงของโหนด ความลึก ของโหนด x ก็คือความยาวของวิถีจากรากถึง x และความสูงของโหนด x ก็คือความยาวของวิถีจาก x ถึงใบที่ ลึกสุด (ของตนไมยอยที่มี x เปนราก) ดังนั้นความสูงของตนไมก็คือความสูงของราก ตาราง 10.1 สรุปความ สูงและความลึกของโหนดตางๆ ของตนไมในภาพประกอบ 1 ตาราง 10.1 ความลึกและความสูงของโหนดตางๆ ของตนไมในภาพประกอบ 10.1 โหนด

ความลึก

ความสูง

!,

!"

#"

", #,

$" $"

%" $"

$,

$"

$"

%,

%"

$"

&,

%"

!"

',

%"

!"

(,

%"

!"


!"& ),

%"

!"

*,

%"

!"

+,

#"

!"

อันดับของลูกๆ ของโหนดภายในนั้นสามารถตีความไดเปนความสัมพันธฉันพี่นอง มีความเปนพี่คน โต คนรอง คนทีส่ าม ไปเรือ่ ยๆ จนถึงคนสุดทอง เราเรียกตนไมที่มีลักษณะเชนนี้วาตนไมแบบอันดับ นัน่ คือ ลูกๆ ของโหนดภายในมีอันดับ การสลับอันดับของลูกๆ จะไดตนไมที่มีความหมายตางกันไป สําหรับตนไมแบบอันดับทีเ่ รากําหนดไดแนๆ วาจํานวนลูกของโหนดภายในจะมีไมเกิน m ลูก ก็มีชื่อ เรียกพิเศษวาตนไม m ภาค กรณีพเิ ศษเมือ่ m = 2 นัน้ พบบอยทีส่ ดุ ก็วา ไดในวงการคอมพิวเตอร จึงมีชื่อให พิเศษวาตนไมแบบทวิภาค (binary tree) นอกจากนีย้ งั พิเศษขึน้ ไปอีกตรงทีว่ า เราเรียกลูกๆ ของโหนดๆ หนึ่งในตนไมแบบทวิภาควาลูกทางซาย และลูกทางขวา (ไมใชลูกคนที่ 1 และลูกคนที่ 2) หมายความวาใน กรณีที่โหนด x มีลูกเดียวคือ y ก็ตองบอกใหชัดเจนดวยวา y เปนลูกทางซาย หรือลูกทางขวา กําหนดให n คือ จํานวนโหนดทั้งหมดในตนไมแบบทวิภาค และ h คือความสูง จะไดขอบเขตความสูงของตนไมแบบทวิภาคที่ นาจดจําดังนี้ (จะขอละวิธีพิสูจนไวเปนแบบฝกหัด) &'(%"-""",≤"""("""≤""""-")"$"

ซึ่งแสดงใหเห็นวาความสูงที่เปนไปไดของตนไมแบบทวิภาคที่มี n โหนดนั้นมีชวงที่กวางมาก เชน ตนไมแบบทวิภาคที่มีหนึ่งลานโหนดนั้นมีมากมายหลายรูปแบบ มีไดตั้งแตความสูงเปน log2 106 = 19 ไป จนถึง 999,999 เปนตน ภาพประกอบ 10.2 แสดงตัวอยางของตนไมแบบทวิภาคที่มี 8 โหนดจํานวน 4 ตน (จากจํานวนทีเ่ ปนไปไดมากมาย 1430 ตน) จะเห็นไดวาตนทางขวาสุดนั้นเตี้ยที่สุดเทาที่จะเตี้ยไดแลว (มี ความสูงเปน log2 8 = 3) เรียกวาเปนตนไมไดดลุ (balanced tree)

ภาพประกอบ 10.2 ตัวอยางตนไมแบบทวิภาคที่มี 8 โหนด เรื่องที่ 10.1.2 การประยุกตตนไม หลังจากไดรูคําศัพทและคุณสมบัติตางๆ มากมายที่เกี่ยวกับตนไมกันแลว ก็ถึงเวลาที่จะมายกตัว อยางการประยุกตตนไมในการเก็บขอมูลกัน (ขอเนนวาตนไมถูกนําไปเปนแบบจําลองชวยการวิเคราะห ปญหามากมาย ซึ่งจะขอไมครอบคลุมในหัวขอนี้ เนื่องจากจะไมเกี่ยวกับการจัดเก็บโดยตรงนัก นักศึกษา สามารถศึกษาเพิ่มเติมไดจากหนังสือหรือวิชาทางทฤษฎีกราฟ) จะขอยอตัวอยางสัก 5 ตัวอยางคือสารบบ แฟมขอมูล นิพจนทางคณิตศาสตร เซตไมมีสวนรวม ตนไมคนหาแบบทวิภาค และแปลนพื้นของชิปวงจร รวม


!"' สารบบแฟมขอมูล เครื่องคอมพิวเตอรทั่วไปจัดเก็บแฟมขอมูลเปนสารบบ (directory) โดยสารบบหนึง่ สามารถเก็บได ทั้งแฟมขอมูลและสารบบยอย โดยมีสารบบพิเศษหนึ่งเปนสารบบของระบบแฟมขอมูลทั้งหมด จะเห็นไดวา เราสามารถแทนการจัดเก็บแฟมขอมูลดังกลาวดวยตนไมแบบมีราก โดยรากของตนไมแทนสารบบพิเศษนั้น โหนดภายในของตนไมแทนสารบบยอยและใบของตนไมแทนแฟมขอมูลหรือแทนสารบบยอยซึ่งไมมีแฟมขอ มูล ภาพประกอบ 10.3 แสดงตัวอยางของระบบแฟมขอมูลในเครื่องคอมพิวเตอรเครื่องหนึ่ง * %$$!%!$ +,-.'/0

8$9.':

1/23

1$90;0

%$$!#!$ 4,5,

1%90;0

1/23

<=>'94,5,

6,0+7

1$9.':

?@ (A9-B

:'=A9-B

ภาพประกอบ 10.3 โครงสรางแบบตนไมของสารบบแฟมขอมูลในคอมพิวเตอร นิพจนทางคณิตศาสตร เราสามารถแทนนิพจนทางคณิตศาสตรเชน (a + b) × c ดวยตนไม เรียกวาตนไมนพิ จน (expression tree) ซึ่งมีใบแทนตัวถูกดําเนินการเชน a, b และ c และโหนดภายในแทนตัวดําเนินการเชน × และ + โดยที่ตัวดําเนินการที่โหนดภายในหนึ่งๆ จะกระทํากับนิพจนยอยที่แทนดวยตนไมยอยตางๆ ทีเ่ ปน ลูกๆ ของโหนดภายในโหนดนั้น ภาพประกอบ 10.4 แสดงตัวอยางตนไมที่แทนนิพจน (a + b) × c การใชตน ไมนิพจนจะชวยใหเราสามารถดําเนินการอะไรไดหลายๆ อยางกับนิพจนทางคณิตศาสตร เชนการลดรูป การ หาอนุพันธ เปนตน . !

"

#

ภาพประกอบ 10.4 ตนไมนิพจนของ (a + b) × c ตนไมคนหาแบบทวิภาค วิธีการจัดเก็บขอมูลแบบหนึ่งซึ่งมีประสิทธิภาพในการคนขอมูลคือ การจัดเก็บขอมูลที่โหนดตางๆ ในลักษณะที่เรียกวาตนไมคนหาแบบทวิภาค (binary search tree) ตนไมแบบนี้เปนตนไมแบบทวิภาคที่มีกฎ การเก็บขอมูลตามโหนดตางๆ โดยกําหนดใหขอมูลของโหนดๆ หนึ่งในตนไมจะมีคามากกวาขอมูลของทุกๆ


!"" โหนดลางทางซายของโหนดนัน้ และจะมีคานอยกวาขอมูลของทุกๆ โหนดลางทางขวาของโหนดนั้น (สมมติ ไมมีกรณีที่ขอมูลซ้ํากัน) $C D

%C

#

$#

%

E

%#

#E

$E

#$

$$

ภาพประกอบ 10.5 ตัวอยางตนไมคนแบบทวิภาค ภาพประกอบ 10.5 แสดงตนไมที่เก็บเลขจํานวนเฉพาะ 12 ตัวแรก การคนหาขอมูลในตนไมนี้ เริม่ จากรากของตนไม เปรียบเทียบขอมูลที่ตองการกับขอมูลที่โหนด ถาไมเทา ก็ตองตัดสินใจวาจะคนตอในตน ไมยอยทางดานขวาหรือทางดานซาย ซึ่งขึ้นกับผลของการเปรียบเทียบวาขอมูลที่ตองการมีคามากกวาหรือ นอยกวาขอมูลที่โหนด เชนถาตองการคนวา 23 มีอยูในตนไมหรือไม ก็เริม่ เปรียบเทียบกับรากซึง่ คือ 19 พบ วาไมเทา และเนือ่ งจาก 23 > 19 ดังนัน้ สรุปไดวา 23 ตองไมอยูทางตนไมยอยดานซายของ 19 แนๆ ถาจะมีก็ ตองอยูในตนไมยอยดานขวา จึงไปคนตอที่ตนไมยอยดานขวา กระทําการคนในลักษณะดังกลาว ไปจนกระทั่ง คนพบ หรือการคนจบลงที่ไมขอมูลใหคนตอ ก็แสดงวาไมมีขอมูลที่ตองการในตนไม จะเห็นขอดีของการจัด เก็บขอมูลในลักษณะนีก้ ต็ รงที่ เราสามารถขจัดขอมูลที่ไมตองนํามาพิจารณาออกไปไดระหวางการคน ทําใหมี ประสิทธิภาพการคนทีด่ กี วาการจัดเก็บขอมูลในรายการแลวใชการคนแบบลําดับ (sequential search) เราจะ ไดเจาะลึกถึงตนไมคนหาแบบทวิภาคในบทถัดไป เซตไมมีสวนรวม ลักษณะความตองการในการจัดเก็บขอมูลแบบหนึ่ง ซึ่งใชมากสําหรับปญหาที่มีการทดสอบความสม มูลกันของขอมูล คือการจัดเก็บขอมูลตางๆ (ซึ่งไมซ้ํากัน) เปนเซตหลายๆ เซต ซึ่งไมมีสมาชิกรวมกัน (เรียก วาเปนเซตไมมีสวนรวม – disjoint sets) สามารถกระทําไดโดยแทนเซตหนึ่งเซตดวยตนไมหนึ่งตน สมาชิก ตางๆ ที่อยูในเซตเดียวกัน ก็คือโหนดตางๆ ของตนไมตนเดียวกัน ดังนั้นเซตทั้งหลายที่ไมมีตัวรวมจึงแทน ดวยปาไม ภาพประกอบ 10.6 แสดงตัวอยางปาไมที่มีตนไม 3 ตนแทนเซต 3 เซตคือ {1, 3, 5, 4, 9, 10} , {6}, และ {7, 2, 8, 11} 7 # D

C

F $!

G E

% $$

$

ภาพประกอบ 10.6 ตัวอยางปาไมที่แทนเซตไมมีสวนรวม


!"( เราใชหมายเลขสมาชิกที่รากของตนไมเปนตัวแทนหมายเลขเซต ดังนัน้ การถามวา x อยูใ นเซต อะไร ก็เพียงแตวิ่งไลจาก x ขึ้นไปหาโหนดแม โหนดยาย ...ไปเรือ่ ยๆ จนชนรากของตนไม สิง่ ทีไ่ ดเปนคํา ตอบก็คือหมายเลขสมาชิกที่รากของตนไมที่ x อยู จากตัวอยางในภาพประกอบ 10.6 เซตทัง้ สามมีหมายเลข เซตคือ 4, 6, และ 8 ตามลําดับ ดังนัน้ การทดสอบวา x และ y อยูในเซตเดียวกันหรือไม ก็เพียงเปรียบเทียบ หมายเลขของรากซึ่ง x และ y อยูเ ทานัน้ แปลนพื้นของชิปวงจรรวม การออกแบบชิปวงจรรวมประกอบไปดวยหลากหลายขั้นตอน มีอยูข น้ั ตอนหนึง่ เรียกวาเปนการออก แบบแปลนพื้น (floor-planning) ของตัวชิบซึ่งมีจุดประสงคในการวางตําแหนงของหนวยจําเพาะตางๆ ของวง จรรวม ภาพประกอบ 10.7 แสดงตัวอยางชิปวงจรรวมชิปหนึ่ง ซึง่ เราจะเห็นไดวา มีการจัดสรรเนือ้ ทีบ่ นชิป สําหรับหนวยจําเพาะตางๆ ลักษณะแปลนพื้นแบบงายแบบหนึ่งเรียกวาโครงสรางแบบหั่นยาว (slicing structure) นั่นคือเราหั่นตัวชิป (ซึง่ เปนสีเ่ หลีย่ มผืนผา) ออกดวยเสนจากขอบดานหนึง่ ไปยังขอบอีกดานหนึง่ เริม่ ดวยการแบงตามแนวนอนกอน ไดเปนสี่เหลี่ยมยอยหลายๆ ชิ้น ก็นําไปหั่นตอตามแนวตั้ง ไดหลายๆ ชิ้น ยอยก็หน่ั ตอตามแนวนอน กระทําสลับเชนนีไ้ ปเรือ่ ยๆ จนกระทั่งไดเนื้อที่สําหรับหนวยจําเพาะตางๆ ภาพ ประกอบ 10.8 แสดงตัวอยางแปลนพื้นของหนวยจําเพาะจํานวน 5 หนวยสองแบบ แตละแบบสามารถถูก แทนไดดวยตนไมแบบอันดับที่มีใบแทนหนวยจําเพาะ และโหนดภายในแทนการหั่น โดยโหนดภายในของ ระดับที่ 0, 2, 4, ... เปนการหัน่ ในแนวนอน และโหนดภายในของระดับที่ 1, 3, 5, ... เปนการหั่นในแนวตั้ง การ จัดการกับแปลนพืน้ (เชนเปลี่ยนยายตําแหนง เปลีย่ นแปลน) ดวยการปรับเปลีย่ นตนไมสามารถกระทําได สะดวกมากขึ้น

ภาพประกอบ 10.7 ตัวอยางแปลนพื้นของชิบวงจรรวม


!") I

H

<

K

H

J @

I

<

K

@

J

H I < K H I

<

@

J

K

J @

ภาพประกอบ 10.8 ตัวอยางแปลนพื้นสองแบบ และตนไมที่แทนแปลนพื้นทั้งสอง


!(* ตอนที่ 10.2 การสรางและการดําเนินการตางๆ หัวเรื่อง เรือ่ งที่ 10.2.1 การสรางตนไม เรือ่ งที่ 10.2.2 การดําเนินการตางๆ กับตนไม แนวคิด 1. วิธีการสรางขอมูลในตนไมกระทําไดทั้งแบบใชแถวลําดับเก็บขอมูลตามโหนดของตนไม ประกอบกับการใชสูตรคํานวณตําแหนงของโหนดลูก หรือใชการจองเนื้อที่สําหรับโหนดตางๆ ของตนไม ซึ่งประกอบดวยขอมูลและตัวชี้ตําแหนงของโหนดลูกๆ 2. การดําเนินงานตางๆ ของตนไมจะเปนเชนไรขึ้นการนําตนไมไปใชงาน 3. การเขียนโปรแกรมสําหรับการดําเนินาการตางๆ กับตนไมมักเปนแบบเวียนเกิด 4. การแวะผานตนไมเปนกระบวนการเขาถึงขอมูลทุกๆ ตัวในตนไมอยางมีระบบ วัตถุประสงค หลังจากที่ศึกษาตอนที่ 10.2 แลว นักศึกษาสามารถ 1. สรางตนไมแบบทวิภาคได 2. เขียนโปรแกรมแบบเวียนเกิดเพื่อหาคุณสมบัติ หรือเพือ่ การดําเนินการตางๆ กับตนไมได เรื่องที่ 10.2.1 การสรางตนไม หลังจากไดทราบวาตนไมคืออะไร ไดเห็นตัวอยางการใชตนไมเพื่อเก็บขอมูล ก็มาถึงคําถามวา แลว เราจะสรางตนไมกนั ไดอยางไรในโปรแกรมคอมพิวเตอร เนื่องจากภาษาคอมพิวเตอรโดยทั่วไปนั้นไมมี ประเภทขอมูลแบบตนไมใหใช ก็จาํ เปนตองอาศัยกลไกตางๆ ของตัวภาษามาประกอบกันเพื่อสรางตนไม ซึง่ มีหลากหลายรูปแบบ ทั้งนี้ขึ้นกับจุดประสงคและลักษณะของตนไมที่จะสราง จะขอนําเสนอการสรางตนไม แบบทวิภาคกันกอนสองวิธีดังนี้ (เนือ่ งจากเปนตนไมทใ่ี ชกนั มากและซับซอนนอย) 1. ใชแถวลําดับเก็บขอมูลตามโหนดของตนไม และใชสูตรคํานวณตําแหนงของโหนดลูก และโหนด พอ วิธแี รกนีจ้ ะเหมาะกับตนไมแบบทวิภาคทีไ่ ดดลุ 2. ใชการจองเนื้อที่สําหรับโหนดตางๆ ของตนไม ซึ่งประกอบดวยขอมูลและตัวชี้ตําแหนงของโหนด ลูกทางซาย และทางขวา จะขอนําเสนอรายละเอียดของวิธกี ารในหัวขอยอยตอไปนี้


!(! การสรางตนไมแบบทวิภาคดวยแถวลําดับและสูตรการคํานวณตําแหนงโหนด พิจารณาตนไมแบบทวิภาคในภาพประกอบ 10.9 ซึง่ เปนตนไมไดดลุ ที่มีขอมูลกํากับโหนดตางๆ เปนตัวอักษร ถาเราจัดเก็บขอมูลตามโหนดตางๆ ในตนไมน้ี ไวในแถวลําดับในชองที่มีเลขดรรชนีตามที่กํากับ ไวในวงเล็บใตโหนด (ดังตัวอยางในภาพประกอบ 10.9) จะสังเกตเห็นความสัมพันธของดรรชนีของชองเก็บ โหนดแมกับโหนดลูก โดยสามารถแสดงไดดว ยสูตรการคํานวณดังนี้ กําหนดใหโหนดรากอยูที่ดรรชนี 1 ของแถวลําดับ ให L(k) คือดรรชนีของโหนดลูกซายของโหนดที่อยูที่ดรรชนี k จะไดวา L(k) = 2k ให R(k) คือดรรชนีของโหนดลูกขวาของโหนดที่อยูที่ดรรชนี k จะไดวา R(k) = 2k + 1 ให P(k) คือดรรชนีของโหนดแมของโหนดที่อยูที่ดรรชนี k จะไดวา

P(k) =  k / 2"

H L$M I L%M

P L#M

J L7M < LGM

@ LDM K LCM

N LFM

O LEM

? L$!M

$"

%"

#"

7"

D"

F"

E"

G"

C"

$!"

$$"

$%"

$#"

H"

I"

P"

J"

@"

N"

O"

<"

K"

?"

"

"

"

ภาพประกอบ 10.9 ตัวอยางการสรางตนไมแบบทวิภาคดวยแถวลําดับ ขอใหนักศึกษาทวนสอบความถูกตองของสูตรขางบนนี้กับตัวอยางที่ปรากฎในภาพประกอบ 10.9 สําหรับกรณีที่ตนไมแหวงๆ ไมเปนระเบียบเหมือนในภาพประกอบ 10.9 ก็จะมีชองในแถวลําดับบางชองที่ ถูกขามไปไมมีขอมูลทําใหสิ้นเปลือง ดังตัวอยางในภาพประกอบ 10.10 จะพบวาถึงแมมีเพียง 6 โหนด ในตนไมแตก็ตองมีแถวลําดับขนาดอยางนอย 10 ชอง H L$M I L%M

P L#M @ LDM

O LEM

? L$!M $"

%"

#"

7"

D"

F"

E"

G"

C"

$!"

$$"

$%"

$#"

H"

I"

P"

"

@"

"

O"

"

"

?"

"

"

"

ภาพประกอบ 10.10 ตัวอยางการสรางตนไมแบบทวิภาคดวยแถวลําดับ


!(# พิจารณาภาพประกอบ 10.10 จะพบปญหาอีกประการหนึ่งก็คือเราจะตองทราบดวยวาชองใดในแถว ลําดับไมมีขอมูลอยูดวย (ในรูปนั้นแสดงดวยชองวางๆ ก็จริง แตในเชิงของที่เก็บในหนวยความจํานั้น ตัว โปรแกรมไมสามารถแยกความแตกตางออกไดวาวางหรือไมวาง) จึงจําเปนตองมีขอมูลเสริมอีกเพื่อระบุวาชอง ใดวางไมวาง เพื่อใหสามารถใชเนื้อที่ไดอยางมีประสิทธิภาพที่สุด (นั่นคือตนไมมี n โหนดสามารถเก็บไดในแถว ลําดับขนาด n ชอง) วิธีการสรางตนไมในลักษณะนี้จึงมักใชเฉพาะกับตนไมแบบทวิภาคซึ่งมีขอมูลเต็มทุก ระดับ ยกเวนระดับลางสุดซึง่ จะไมเต็มก็ได แตโหนดตางในระดับลางสุดตอง “อยูติดทางซาย” ดังตัวอยางตน ไมในภาพประกอบ 10.9 เราจะไดพบตนไมที่มีลักษณะแบบนี้ในบทหลัง ที่วาดวยเรื่องของฮีป - heap) การแทนตนไมที่มีลักษณะพิเศษขางตนนี้ จึงประกอบไปดวยแถวลําดับหนึ่งแถว กับตัวแปรอีกตัว หนึ่งซึ่งมีไวเก็บจํานวนโหนดในตนไม เขียนไดดงั นี้ TYPE" Tree = RECORD Element : ARRAY[1..MaxElement] OF ElementType; Size

: integer;

END;

เปน record ประกอบดวยแถวลําดับ (มีชื่อวา element) ของขอมูลประเภท ElementType (ซึ่งจองไวใหมีขนาด MaxElement ตัว) และตัวแปรจํานวนเต็มอีกตัวหนึ่ง (มีชื่อวา size) ที่ เก็บจํานวนโหนดของตนไม เราสามารถเขียนฟงกชันที่คํานวณคาตําแหนง (ซึง่ คือเลขดรรชนีของแถวลําดับ) ของลูกทางซาย ทางขวา และของโหนดแมไดงายๆ ดังนี้ (ในกรณีทไ่ี มมลี กู ซาย ลูกขวา หรือแม จะคืนคา 0) ในที่นี้

Tree

FUNCTION LeftChild( T : Tree; N : integer ) : integer; BEGIN IF ( 2*N <= T.Size ) THEN LeftChild := 2*N ELSE LeftChild := 0; END;

FUNCTION RightChild( T : Tree; N : integer ) : integer; BEGIN IF ( 2*N+1 <= T.Size ) THEN RightChild := 2*N+1 ELSE RightChild := 0; END;


!($ FUNCTION Parent( T : Tree; N : integer ) : integer; BEGIN IF (T.Size > 0) THEN Parent := N DIV 2 ELSE Parent := 0; END;

การสรางตนไมแบบทวิภาคดวยโหนดขอมูลและตัวชี้ตําแหนงของโหนดลูกๆ วิธีการสรางตนไมแบบทวิภาคที่งาย และไดรับความนิยมกันมากวิธีหนึ่งก็คือ การนิยามใหโหนดของ ตนไมประกอบดวยขอมูลกํากับโหนด และตัวชี้ตําแหนงของโหนดลูกซายและโหนดลูกขวา ตนไมแบบ ทวิภาคตนหนึ่งจึงประกอบดวยโหนดตางๆ ซึ่งถูกจองเนื้อที่ไว โครงสรางของตนไมถกู สรางขึน้ จากการเก็บ ความสัมพันธของแมลูกดวยตัวชี้ตําแหนงโหนด ภาพประกอบ 10.11 แสดงตัวอยางการสรางตนไมแบบ ทวิภาคดวยแนวคิดดังกลาว H H I

I

P

P @

@ O

O

?

?

ภาพประกอบ 10.11 ตัวอยางการสรางตนไมแบบทวิภาคดวยโหนดขอมูลและตัวชี้ลูกๆ จะวาไปแลวการสรางตนแบบทวิภาคในลักษณะก็พอเทียบเคัยงไดกบั การสรางรายการแบบรายการ โยง นั่นคือเราใชวิธีการจําความสัมพันธ ในรายการโยงมีการเก็บตําแหนงของโหนดของขอมูลตัวถัดไปราย การ ในที่นี้ก็ใชวิธีการเก็บตําแหนงของโหนดของลูกซายและลูกขวา ดวยวิธีนี้ถึงแมวาจะตองเสียเนื้อที่ในการ เก็บตัวชี้คําแหนง แตก็ไมมีขอจํากัดในเรื่องรูปรางของตนไมที่จัดเก็บ ปริมาณเนื้อที่ที่ใชในการจัดเก็บตนไมไม ขึ้นรูปรางของตนไม ดังนัน้ สิง่ ทีต่ อ งออกแบบก็คอื ขอมูลตางๆ ที่กํากับโหนด ซึ่งสามารถนิยามดวย record ไดดงั นี้ TYPE TreePtr = ^TreeNode; TreeNode = RECORD Element : ElementType; Left : TreePtr; Right : TreePtr; END; BinaryTree = TreePtr;

ตัวหลักที่เราไไดนิยามขางบนนี้ก็คือ record TreeNode ซึ่งบรรยายสมาชิกของโหนดซึ่งประกอบ ดวยตัวขอมูล (element) และตัวชี้ลูกซายและลูกขวา (left และ right) โดยนิยาม TreePtr ใหเปน


!(% ประเภทตัวชี้โหนด (^TreeNode) และนิยามประเภทตนไมแบบทวิภาค BinaryTree คือตัวชี้โหนด TreePtr ซึ่งพิเศษตรงที่วาความหมายโดยนัยของโหนดที่ถูกชี้คือรากของตนไมแบบทวิภาคนั้น ใหสังเกตวาการสรางตนไมแบบทวิภาคในลักษณะเชนนี้ เราถือวาทางเขาถึงของขอมูลตางๆ ในตน ไมนั้นอยูที่ราก (คลายกับรายการโยงซึ่งมีทางเขาอยูที่หัวรายการ) ซึ่งอาจดูแลวคอนขางจํากัด แตก็ใชดีที เดียวในการจัดเก็บขอมูลหลากหลายกรณี (จะไดเห็นในรายละเอียดตอไป) แตถาดูที่สมาชิกตางๆ ของ TreeNode อีกสักครั้งหนึ่งก็จะพบวา เราไมรูวาโหนดๆ หนึ่งมีโหนดใดเปนแม (รูแตวาลูกคือใคร) ก็เพราะไม ไดจําตัวชี้ตําแหนงโหนดแม ซึง่ ก็อาจดูแลวคอนขางจํากัดอีกเชนกัน แตในทางปฏิบัติพบวา การมีเพียงแคทาง เขาที่รากนั้นเปนบีบบังคับโดยนัยวาการหาเขาหาโหนดใดในตนไมตองวิ่งจากบนลงรากโดยธรรมชาติ เมื่อใด ทีว่ ง่ิ ลง และมีการจําโหนดตางๆ ตามวิถีที่ลงมา ก็ยอมทราบวาใครคือโหนดแมโดยอัตโนมัติจากวิถีนั้น และ ตองขอทิ้งทายไวตรงนี้วาในกรณีที่ไมมีโหนดลูก เปนที่นิยมกันวาจะเก็บตัวชี้พิเศษคือ nil (เชนเดียวกับราย การโยงซึ่งมี nil ระบุจุดสิน้ สุดรายการ) เรื่องที่ 10.2.2 การดําเนินการตางๆกับตนไม การดําเนินการกับตนไมจะมีอะไรบางนัน้ ทั้งนี้ก็ขึ้นกับวา เราประยุกตตนไมกับงานอะไร และ ตองการบริการใดจากการจัดเก็บดังกลาว ในหัวขอนีจ้ ะขอนําเสนอตัวอยางการดําเนินการทัว่ ไป พอใหเห็น เปนแนวทางการเขียน procedure หรือ function ที่กระทํากับตนไม ดังนี้ • การนับจํานวนโหนด • การหาความสูง • การแวะผานตนไม การนับจํานวนโหนด ตัวการการดําเนินการทีจ่ ะนําเสนอตอไปนี้ อาศัยธรรมชาติแบบเวียนเกิดของโครงสรางของตนไม (recursive structure) หมายความวาเราสามารถมองตนไมแบบทวิภาควาเปนตนไมที่ประกอบดวยราก และ ตนไมแบบทวิภาคยอยสองตนซึ่งถูกนํามาตอเปนลูกของรากนั้น นั่นคือการนิยามตนไมแบบทวิภาคดวยตนไม แบบทวิภาค สงผลใหการเขียนโปรแกรมออกมาในลักษณะโปรแกรมแบบเวียนเกิด (recursive program) ซ฿ งมักสรางความสับสนใหกับโปรแกรมเมอรมือใหมทั้งหลาย อยางไรก็ตามการไดเห็นและศึกษาโปรแกรมใน ลักษณะหลายๆ ตัวอยาง ก็จะเกิดแนวคิดของลักษณะการเขียนเอง ก็เริม่ ดวยการนับจํานวนโหนดกันกอน กําหนดให T(r) คือตนไมแบบทวิภาคที่มีโหนด r เปนราก N(T(r)) คือจํานวนโหนดของตนไม T(r) L(r) และ R(r) คือโหนดลูกซายและโหนดลูกขวาของ r เราสามารถเขียนนิยามของจํานวนโหนดในตนไม T(r) ไดดงั นี้ 3 L2 L0 MM =

! )& 0 = -)/ " $ + 3 L2 L 4L0 MMM + 3 L2 L 1 L0 MMM )& 0 -)/


!(& ซึ่งใหความหมายวาถาเปนตนไมวาง (รากเปน nil) จํานวนโหนดก็เปน 0 ถามีโหนดราก จํานวนโหนดของ ทั้งตนก็ยอมเทากับ 1 (ซึง่ คือการนับโหนดราก) บวกกับจํานวนโหนดของตนไมยอยทางซาย และจํานวนโหนด ของตนไมยอยทางขวา เราสามารถเขียนเปนฟงกชันไดตรงไปตรงมาจากนิยามขางตนดังนี้ FUNCTION Size( R : BinaryTree ) : integer; BEGIN IF R = nil THEN Size := 0 ELSE Size := 1 + Size(R^.Left) + Size(R^.Right)); END;

นักศึกษาสวนมากเกิดความสงสัยวาเมื่อมีการเรียกตัวเองดังเชนที่ภายในฟงกชัน size มีการเรียก ึ ษาถึงกลไกการ size ดวยนั้น มันเปนอยางไร ทําไดอยางไร ตองขอบอกตรงนี้วาอยาไปสงสัยมาก (จะไดศก ทํางานของโปรแกรมกันอยางลึกซึ้งในวิชาอื่นๆ) เอาแความันทําได ขอใหมองเปนธรรมชาติ เสมือนกับทีน่ กั ศึกษาสวนใหญไมคอยมีปญหาอะไรกับนิยามของ N(T(r)) ที่อางอิงถึง N(T(L(r))) และ N(T(R(r))) ขางตน แต ขอใหเขาใจหลักการของการเขียนโปรแกรมแบบเวียนเกิดวาเมือ่ มีการเรียกแบบเวียนเกิด ก็ดเู สมือนวาจะเกิด การทํางานทีไ่ มสน้ิ สุด ตรงนี้จึงเปนกฏขอแรกวาเมื่อเรียกแบบเวียนเกิด จะตองมีความมั่นใจวาขอมูลที่สงใน การเรียกแบบเวียนเกิดนัน้ ตองเปลีย่ นไปในแนวทางทีจ่ ะสิน้ สุด เชนในกรณีของ size นัน้ สิ่งที่รับมาคือโหนด ของตนไม และเมื่อมีการเรียกแบบเวียนเกิด จะสงโหนดลูกไป ซึ่งโดยโครงสรางของตนไม การลงไปยังลูก เรือ่ ยๆ นั้นตองมีจุดจบคือพบ nil แนนอน จึงเปนทีม่ าของกฏขอสองคือเมือ่ รูว า จุดสิน้ สุดอยูใ นสภาพใด ก็นาํ การตรวจสอบสภาพเชนนั้นมาไวเปนการทํางานในชวงตนๆ ของโปรแกรมแบบเวียนเกิด โดยตรวจสอบวาถา เปนสภาพสิน้ สุด จะไดเลิก ไมเรียกแบบเวียนเกิดตออีก การหาความสูง จากนิยามของความสูงของตนไมซึ่งคือความยาวของวิถีจากรากถึงใบที่ลูกที่สุดในตนไม แลวเราหา ใบทีล่ กึ ทีส่ ดุ อยางไร ลองจินตนการดูวา ถาเราอยูท โ่ี หนดๆ หนึ่งในตนไม เราจะทราบไดอยางไรวาใบที่ลึกที่ สุดในตนไมนั้นเปนลูกหลานทางซายหรือทางขวาจากโหนดที่เราอยู คําตอบก็คอื เราไมทราบ จนกวาจะได ลองไปดูทั้งสองทางทั้งทางซายและขวา แลวจึงนํามาเปรียบเทียบกัน และแทนที่เราจะหาใบลึกที่สุด ถาเรา หันมานึกถึงโครงสรางแบบเวียนเกิดของตนไม จะพบวา ความสูงของโหนดใด ยอมคิดมาจากความสูงของตน ไมยอยตนที่สูงกวาระหวางตนไมยอยทั้งสองซึ่งเปนลูกของโหนดนั้น กําหนดให T(r) คือตนไมแบบทวิภาคที่มีโหนด r เปนราก H(T(r)) คือจํานวนโหนดของตนไม T(r) L(r) และ R(r) คือโหนดลูกซายและโหนดลูกขวาของ r เราสามารถเขียนนิยามของจํานวนโหนดในตนไม T(r) ไดดงั นี้ 7 L2 L0 MM =

! $ + 7 L2 L 4 L0 MM $ + 7 L2 L 1 L0 MM

$ + >,; (7 L2 L 4L0 MMQ 7 L2 L 1L0 MM )

)& 4L0 M = -)/ 536 1L0 M = -)/ )& 4L0 M -)/ 536 1 L0 M = -)/ " )& 4L0 M = -)/ 536 1L0 M -)/ )& 4L0 M -)/ 536 1 L0 M -)/


!(' ซึง่ แบงวิธกี ารหาความสูงออกเปนสีก่ รณีคอื กรณีไมมลี กู ทัง้ สอง ซึ่งหมายความวาเปนใบ มีความสูง เปน 0 กรณีมเี ฉพาะลูกซาย ความสูงยอมเทาความสูงของตนซายที่เปนลูกบวกอีกหนึ่ง และในทํานองเดียว กัน กรณีมเี ฉพาะลูกขวา ความสูงยอมเทาความสูงของตนขวาที่เปนลูกบวกอีกหนึ่ง แตกรณีมที ง้ั สองลูก ก็ ตองนําความสูงของตนไมยอยที่เปนลูกที่สูงกวา บวกอีกหนึ่ง แลวถาเปนกรณีตน ไมวา ง จะสูงเทาใด ตรงนี้ขอใหคิดแบบนี้ ถามีเพียงโหนดเดียวแสดงวาสูงศูนย ดังนั้นจึงขอกําหนดวาตนไมวางใหสูง –1 หากกําหนดเชนนี้ สงผลใหเราสามารถเขียนนิยามของ H(T(r)) ได กระทัดรัดขึน้ เหลือเพียงสองกรณีดงั นี้ (ขอใหนักศึกษาลองคิดดูวาใชไดจริง) 7 L2 L0 MM =

)& 0 = -)/ −$ " $ + >,; (7 L2 L 4L0 MMQ 7 L2 L 1L0 MM ) )& 0 -)/

จากนิยามขางบนนี้เราสามารถเขียนเปนฟงกชันไดอยางตรงไปตรงมาดังนี้ FUNCTION Height( R : BinaryTree ) : integer; BEGIN IF R = nil THEN Height := -1 ELSE Height := 1 + Max(Height(R^.Left) + Height(R^.Right)); END;

การแวะผานตนไม การดําเนินการบนตนไมทพ่ี บเห็นบอยมากคือการแวะผานตนไม (tree traversal) ซึง่ เปนขัน้ ตอนการ วิ่งเขาไปในตนไมโดยมีจุดประสงคเพื่อวิ่งผานใหครบทุกโหนด คําถามที่ตามมาก็คือจะแวะโหนดทุกๆ โหนด ไปทําไม ? คําตอบก็คงขึ้นกับลักษณะของการจัดเก็บขอมูลในตนไมวาแทนอะไรอยู ตัวอยางที่เห็นไดชัดก็คือ การแวะผานตนไมเพื่อพิมพขอมูลของทุกๆ โหนดในตนไม เปนตน (จะไดนาํ เสนอการประยุกตการแวะผาน ตนไมแบบอื่นๆ ในภายหลัง) เมื่อเราตองการแวะผานตนไม ก็มีคําถามอีกวาจะแวะที่โหนดใดกอน โหนดใด หลัง หมายความวาลําดับของโหนดที่ถูกแวะนั้นจะเปนอยางไร ในหัวขอจะไดนําเสนอวิธีการแวะผานตนไมที่ ใชกันมาก เขียนเปนโปรแกรมก็งา ย โดยมีลําดับของโหนดที่ถูกแวะผานตางๆ กัน สามวิธีคือ การแวะผาน แบบกอนลําดับ ตามลําดับ และหลังลําดับ การแวะผานแบบกอนลําดับ การแวะผานแบบกอนลําดับ (preorder traversal) นั้นเริ่มแวะรากของตนไมกอน จากนั้นวิ่งแวะ โหนดลงไปตามแนวลึก ลงไปเรือ่ ยๆ วิ่งลงผานโหนดใดก็แวะโหนดนั้น โดยเลือกทีจ่ ะวิง่ ลงทางซายกอนเสมอ เมื่อใดวิ่งลงตอไมได ก็วิ่งยอนขึ้นตามทางเดิมที่ผานมา ผานโหนดใดที่ยังไมเคยลงทางขวา ก็ลงลุยตอตามแนว ลึกในลักษณะทีก่ ลาวมา ภาพประกอบ 10.12 แสดงตัวอยางการแวะผานตนไมแบบกอนลําดับ เริ่มจากรากวิ่ง ลงทางซายตามแนวที่แสดงดวยเสนสีแดง ลงมาตันที่ E จึงยอนขึ้น ตามแนวเสนประสีนาํ เงิน จนถึง B พบวามี ทางแยกขวาก็ลงตอ ตามแนวสีสแี ดง ตันที่ H ก็วิ่งยอนขึ้น และกระทําในลักษณะวิง่ ลงลึก และมีการยอนขึ้น เชนนี้ จนกระทั่งกลับมาที่รากของตนไมอีกครั้งหนึ่ง เมื่อใดที่ผานโหนดตามทิศที่กําลังวิ่งลง ก็แวะโหนดนั้น ดวย ดังนัน้ ลําดับการแวะโหนดของตนไมในภาพประกอบ 10.12 จึงเปน


!(" A, B, C, E, D, F, H, G, I, J, K, L H I

N

J

<

K

@

O

S

?

R

P

ภาพประกอบ 10.12 ตัวอยางการแวะผานตนไมแบบกอนลําดับ แลวจะเขียนเปนโปรแกรมไดอยางไร การแวะผานแบบกอนลําดับนัน้ อาศัยแนวคิดของการทํางาน แบบเวียนเกิดคือถาตองการแวะผานตนไมแบบกอนลําดับในตนไมที่มีรากเปน r ก็ใหแวะโหนด r กอน จากนั้น จึงตามดวยการแวะผานตนไมยอยทางซายของ r แบบกอนลําดับ และตามดวยการแวะผานตนไมยอยทางขวา ของ r แบบกอนลําดับ กําหนดให Pre(T(r)) คือลําดับของโหนดที่ถูกแวะดวยการแวะผานแบบกอนลําดับในตน ไมที่มี r เปนราก จะไดวา 80%L2 L0 MM =

%<=>? 9%:;%-#% )& 0 = -)/ " 0 Q 80%L2 L 4L0 MMMQ 80%L2 L 1L0 MMM )& 0 -)/

ดังนัน้ การแวะผานตนไมแบบกอนลําดับในภาพประกอบ 10.12 จากนิยามขางบนนี้แสดงไดผังขาง ลางนี้ ผลทีไ่ ดกค็ อื ลําดับ A, B, C, E, D, F, H, G, I, J, K, L " TU=LVLHMM" H"

TU=LVLIMM"

"

I"

"

"

J"

TU=LVLJMM" TU=LVLKMM"

"

"

"

K"

"

@"

"

"

"

"

"

"

TU=LVLNMM" TU=LVL<MM"

<"

N"

TU=LVLOMM"

TU=LVLSMM"

TU=LVL?MM"

"

O

S" TU=LVLRMM"

TU=LVLPMM"

?"

"

"

"

R"

P"

"

"

"

"

"

TU=LVL@MM"

"

จากนิยามการแวะผานแบบกอนลําดับขางบนนี้สามารถเขียนเปนโปรแกรมแบบเวียนเกิดไดดังนี้ PROCEDURE PreOrder( R : BinaryTree ) BEGIN IF R <> nil THEN BEGIN Visit( r ); PreOrder( R^.Left ); PreOrder( R^.Right ); END; END;


!(( ที่ปรากฏในโปรแกรมขางบนนี้ แทนกระบวนการแวะโหนด ซึ่งจะทําอะไรนั้น ก็ขึ้นกับปญหา ทีส่ นใจ (เชนถาตองการพิมพขอมูลในทุกๆ โหนด visit ก็อาจแทนไดดวย writeln เปนตน) visit

การแวะผานแบบตามลําดับ การแวะผานแบบตามลําดับ (inorder traversal) ก็มีขั้นตอนการทํางานคลายกับการแวะผานแบบ กอนลําดับ จะตางกันก็ตรงที่ลําดับที่เราแวะโหนดราก โดยการแวะผานตนไมแบบตามลําดับในตนไมที่มีราก เปน r จะเริ่มดวยการแวะผานตนไมยอยทางซายของ r แบบตามลําดับกอน ตามดวยการแวะโหนดราก r แลว จึงคอยการแวะผานตนไมยอยทางขวาของ r แบบตามลําดับ กําหนดให In(T(r)) คือลําดับของโหนดที่ถูกแวะ ดวยการแวะผานแบบตามลําดับในตนไมที่มี r เปนราก จะไดวา @-L2 L0 MM =

%<=>? 9%:;%-#% )& 0 = -)/ " @-L2 L 4L0 MMMQ 0 Q @-L2 L 1L0 MMM )& 0 -)/

ดังนั้นการแวะผานตนไมแบบตามลําดับในภาพประกอบ 10.12 จากนิยามขางบนนี้แสดงไดผังขาง ลางนี้ ผลทีไ่ ดกค็ อื ลําดับ E, C, B, F, H, D, G, A, J, I, L, K " " N-LVLHMM" N-LVLIMM" N-VLJMM"

I"

H" N-LVL<MM"

N-LVL@MM"

N-LVLNMM"

"

N-LVLOMM"

N"

N-LVLSM"

N-LVLKMM"

J"

"

<"

N-LVL?MM"

"

O"

"

N-LVLRMM"

K"

"

"

@"

N-LVLPMM"

"

?"

"

"

"

R"

S" "

"

"

"

"

P"

"

"

"

"

"

"

"

"

จากนิยามการแวะผานแบบตามลําดับขางบนนี้สามารถเขียนเปนโปรแกรมแบบเวียนเกิดไดดังนี้ PROCEDURE InOrder( R : BinaryTree ) BEGIN IF R <> nil THEN BEGIN InOrder( R^.Left ); Visit( R ); InOrder( R^.Right ); END; END;

การแวะผานแบบหลังลําดับ การแวะผานแบบหลังลําดับ (postorder traversal) ก็มขี น้ั ตอนการทํางานคลายกับการแวะผานทัง้ สองแบบทีไ่ ดนาํ เสนอมา จะตางกันก็ตรงที่ลําดับที่เราแวะโหนดราก โดยการแวะผานตนไมแบบหลังลําดับใน ตนไมที่มีรากเปน r จะเริ่มดวยการแวะผานตนไมยอยทางซายของ r แบบหลังลําดับกอน ตามดวยการแวะ ผานตนไมยอยทางขวาของ r แบบหลังลําดับ แลวจึงคอยแวะโหนดราก r กําหนดให Post(T(r)) คือลําดับของ โหนดที่ถูกแวะดวยการแวะผานแบบหลังลําดับในตนไมที่มี r เปนราก จะไดวา


!() 8A9> L2 L0 MM =

%<=>? 9%:;%-#% )& 0 = -)/ " 8A9> L2 L 4 L0 MMMQ 8A9> L2 L 1 L0 MMMQ 0 )& 0 -)/

ดังนัน้ การแวะผานตนไมแบบหลังลําดับในภาพประกอบ 10.12 จากนิยามขางบนนี้แสดงไดผังขาง ลางนี้ ผลทีไ่ ดกค็ อื ลําดับ E, C, H, F, G, D, B, J, L, K, I, A " T'W0LVLHMM" T'W0LVLIMM" T'W0LVLJMM"

T'W0LVLNMM

T'W0LVL<MM"

T'W0LVLKMM"

J"

K"

"

T'W0LVLPMM"

"

"

P"

H"

I"

T'W0LVLOMM"

T'W0LVL?MM"

<"

"

O"

T'W0LVLRMM"

S"

"

@"

?"

"

"

"

"

"

"

"

"

"

"

"

"

"

"

T'W0LVL@MM"

T'W0LVLSMM"

N

"

"

จากนิยามการแวะผานแบบหลังลําดับขางบนนี้สามารถเขียนเปนโปรแกรมแบบเวียนเกิดไดดังนี้ PROCEDURE PostOrder( R : BinaryTree ) BEGIN IF R <> nil THEN BEGIN PostOrder( R^.Left ); PostOrder( R^.Right ); Visit( R ); END; END;

เวลาในการแวะผานตนไม ไมวาจะเปนการแวะผานแบบใดก็ตาม เวลาการแวะผานตนไมทั้งตน ก็ประกอบไปดวย การแวะผาน ตนไมยอยสองตน บวกกับเวลาในการแวะโหนดราก กําหนดให t(n) คือเวลาในการแวะผานตนไมที่มี n โหนด เราสามารถบรรยาย t(n) ไดดวยความสัมพันธเวียนเกิด (recurrence relation) ขางลางนี้ t(n) = t(k) + t(n-k-1) + c เมื่อ n > 0 , t(0) = 1 เราสามารถหาผลเฉลยของความสัมพันธเวียนเกิดได t(n) = O(n) (จะขอไมอธิบายรายละเอียดการ หาผลเฉลยในที่นี้ นักศึกษาคงจะไดศึกษารายละเอียดในวิชาภินทนคณิตศาสตร) แสดงใหเห็นวาการแวะผาน ตนไมไมวาจะดวยโปรแกรมใดที่ไดนําเสนอมานั้น จะใชเวลาแปรตามจํานวนโหนดในตนไม การวาดตนไม ภาพประกอบ 10.13 แสดงตัวอยางการวาดตนไมแบบทวิภาคขนาดใหญสองตน เราสังเกตไดวา การ วาดตนไมทแ่ี สดงในรูปนีม้ ลี กั ษณะดังนี้ • โหนดของตนไมถูกวางเรียงเปนระดับๆ ในแนวตั้ง ตามความลึกของโหนดนั้นๆ ในตนไม • ตนไมมีความกวางเทากับจํานวนโหนดในตนไม โดยถาพิจารณาที่โหนด x ใดๆ จะพบวาโหนด ลางทางซายของ x ทุกโหนดตองอยูทางซายของ x ในรูปและในทํานองเดียวกันโหนดลางทาง ขวาของ x ทุกโหนดตองอยูทางขวาของ x ในรูป


!)* • จากลักษณะขางตนทั้งสองจะทําใหการวดรูปตนไมนี้ไมมีกิ่งใดในตนไมตัดขวางกันและกัน

ภาพประกอบ 10.13 ตัวอยางการวาดตนไมขนาดใหญ 1 ดังนั้นสิ่งที่เราสนใจในที่นี้ก็คือพิกัดของโหนดตางๆ ในตนไม กําหนดให (x) , y)) คือพิกัดของโหนด i ในตนไม (โดยกําหนดใหมุมซายบนมีพิกัดเปน (0,0) พิกดั x และ y เพิ่มขึ้นเมื่อไปทางขวาและลงลางตาม ลําดับ) เราสามารถหาคาของ x) และ y) ไดงายๆ ดังนี้ •

y) มีคา เทากับเลขระดับ (ซึง่ ก็คอื ความลึก) ของโหนด i ในตนไม

• x) มีคาเทากับเลขลําดับของโหนด i ในการแวะผานตนไมแบบตามลําดับ ภาพประกอบ 10.14 แสดงตัวอยางการหาพิกัดของโหนดตางๆ ดวยวิธีขางบนนี้ การแวะผานตนไม นี้แบบตามลําดับจะได E, C, B, F, H, D, G, A, J, I, L, K ลําดับที่ของโหนดตางๆ ในลําดับนีก้ ค็ อื พัด x ของ โหนดเหลานั้นในรูป (กําหนดใหเลขลําดับเริ่มที่ 0) ตัวอยางเชน E เปนโหนดแรกในของการแวะผาน (มีเลข ลําดับคือ 0) และมีความลึก 3 จึงอยูที่พิกัด (0, 3) ในขณะที่ K เปนโหนดลําดับสุดทาย (เลขลําดับที่ 11) และมึ ความลึก 2 จึงอยูที่พิกัด (11, 2) !

$

%

#

7

D

F

!

E

G

C $! $$

H

$

I

%

N

J

# K

< @

O ?

S R

P

7

ภาพประกอบ 10.14 ตัวอยางการวาดรูปตนไม กิจกรรม 10.1 การสรางตนไมนิพจน

1

M. Weiss, "Data Structures and Algorithm Analysis in C++," The Benjamin/Cumming Publishing, 1994


!)! 1. 2. 3. หารดวย 1

ใหเขียนโปรแกรมรับนิพจนแบบหลังลําดับ ใชสแตกสรางเปนตนไมนพิ จน ใหเขียนโปรแกรมหาคาผลลัพธของนิพจนของตนไม ใหเขียนโปรแกรมที่ปรับโครงสรางตนไมของนิพจนใหงายขึ้นเชน เมือ่ มีการบวกลบกับ 0 คูณ ยกกําลังดวย 0 หรือ 1 เปนตน


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.