data structure lesson10

21
!"! แผนการสอนประจําบทเรียน รายชื ่ออาจารยผู จัดทํา สมชาย ประสิทธิ ์จูตระกูล หัวขอของเนื้อหา ตอนที่ 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. เขียนโปรแกรมแบบเวียนเกิดเพื่อหาคุณสมบัติ หรือเพื ่อการดําเนินการตางๆ กับตนไมได

description

ตอนที่ 10.2 การสรางและการดําเนินการตางๆ (2 คาบ) เรื่องที่ 10.2.1 การสรางตนไม เรื่องที่ 10.2.2 การดําเนินการตางๆ กับตนไม แผนการสอนประจําบทเรียน หัวขอของเนื้อหา สื่อการสอน 1. โฮมเพจชุดวิชา 2. สไลดประกอบการบรรยาย (Powerpoint) 3. โปรแกรมคอมพิวเตอร ประเมินผล 1. ประเมินผลจากกิจกรรมที่ทํา 2. ประเมินผลจากคําถามทายบทเรียน " # $ !"$ หัวเรื่อง เรื่องที่ 10.1.1 นิยามของตนไม เรื่องที่ 10.1.2 การประยุก

Transcript of data structure lesson10

!"!

แผนการสอนประจําบทเรียน

รายช่ืออาจารยผูจัดทํา สมชาย ประสิทธ์ิจูตระกูล

หัวขอของเนื้อหา

ตอนที ่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 1/23 4,5, 1/23 6,0+7

8$9.': 1$90;0 1%90;0 <=>'94,5, 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

# C $!

D

$

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 ตัวอยางแปลนพื้นของชิบวงจรรวม

!")

H I

J

@< K

I < K

H

@ J

H I < K

J @

H

I < 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 "

<LGM

KLCM

JL7M

?L$!M

@LDM

NLFM

OLEM

IL%M

PL#M

HL$M

$" %" #" 7" D" F" E" G" C" $!" $$" $%" $#"H" I" P" J" @" N" O" <" K" ?" " " "

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

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

?L$!M

@LDM

OLEM

IL%M

PL#M

HL$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;

ในที่นี้ Tree เปน record ประกอบดวยแถวลําดับ (มีชื่อวา element) ของขอมูลประเภท ElementType (ซึ่งจองไวใหมีขนาด MaxElement ตัว) และตัวแปรจํานวนเต็มอีกตัวหนึ่ง (มีชื่อวา size) ที่เก็บจํานวนโหนดของตนไม

เราสามารถเขียนฟงกชันที่คํานวณคาตําแหนง (ซ่ึงคือเลขดรรชนีของแถวลําดับ) ของลูกทางซาย ทางขวา และของโหนดแมไดงายๆ ดังน้ี (ในกรณีท่ีไมมีลูกซาย ลูกขวา หรือแม จะคืนคา 0)

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 แสดงตัวอยางการสรางตนไมแบบทวิภาคดวยแนวคิดดังกลาว

?

@ O

I P

H

H

I P

@ 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) ไดดังน้ี

++=

=-)/0)&01230423-)/0)&

023MMMLLLMMMLLL$

!MMLL "

!(&

ซึ่งใหความหมายวาถาเปนตนไมวาง (รากเปน 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) ไดดังน้ี

( )+=+

=+==

=

-)/01536-)/04)&01270427-)/01536-)/04)&0127-)/01536-)/04)&0427-)/01536-)/04)&

027

MLMLMMLLLMMQLLL>,;$MLMLMMLLL$MLMLMMLLL$MLML!

MMLL "

!('

ซ่ึงแบงวิธีการหาความสูงออกเปนส่ีกรณีคือ กรณีไมมีลูกท้ังสอง ซึ่งหมายความวาเปนใบ มีความสูงเปน 0 กรณีมีเฉพาะลูกซาย ความสูงยอมเทาความสูงของตนซายที่เปนลูกบวกอีกหนึ่ง และในทํานองเดียวกัน กรณีมีเฉพาะลูกขวา ความสูงยอมเทาความสูงของตนขวาที่เปนลูกบวกอีกหนึ่ง แตกรณีมีท้ังสองลูก ก็ตองนําความสูงของตนไมยอยที่เปนลูกที่สูงกวา บวกอีกหนึ่ง

แลวถาเปนกรณีตนไมวาง จะสูงเทาใด ตรงนี้ขอใหคิดแบบนี้ ถามีเพียงโหนดเดียวแสดงวาสูงศูนย ดังนั้นจึงขอกําหนดวาตนไมวางใหสูง –1 หากกําหนดเชนน้ี สงผลใหเราสามารถเขียนนิยามของ H(T(r)) ไดกระทัดรัดข้ึนเหลือเพียงสองกรณีดังน้ี (ขอใหนักศึกษาลองคิดดูวาใชไดจริง)

( )+=−

=-)/0)&01270427-)/0)&

027MMLLLMMQLLL>,;$

$MMLL "

จากนิยามขางบนนี้เราสามารถเขียนเปนฟงกชันไดอยางตรงไปตรงมาดังนี้ 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 <

?

O

RK @

P

S

ภาพประกอบ 10.12 ตัวอยางการแวะผานตนไมแบบกอนลําดับ

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

==

-)/0)&01280%04280%0-)/0)&9%:;%-#%%<=>?

0280%MMMLLLMMMQLLLQ

MMLL "

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

TU=LVLHMM"H" TU=LVLIMM" TU=LVLNMM"" I" TU=LVLJMM" TU=LVL<MM" N" TU=LVLOMM" TU=LVLSMM"" " J" TU=LVLKMM" <" TU=LVL@MM" TU=LVL?MM" " O S" TU=LVLRMM"" " " K" " @" TU=LVLPMM" ?" " " " R"" " " " " " P" " " " " "

"

จากนิยามการแวะผานแบบกอนลําดับขางบนนี้สามารถเขียนเปนโปรแกรมแบบเวียนเกิดไดดังนี้ PROCEDURE PreOrder( R : BinaryTree )

BEGIN

IF R <> nil THEN

BEGIN

Visit( r );

PreOrder( R^.Left );

PreOrder( R^.Right );

END;

END;

!((

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

การแวะผานแบบตามลําดับ การแวะผานแบบตามลําดับ (inorder traversal) ก็มีขั้นตอนการทํางานคลายกับการแวะผานแบบ

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

==

-)/0)&012@-0042@--)/0)&9%:;%-#%%<=>?

02@-MMMLLLQMMMQLLL

MMLL "

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

N-LVLHMM"N-LVLIMM" H" N-LVLNMM"

N-VLJMM" I" N-LVL<MM" " N-LVLOMM" N" N-LVLSM"N-LVLKMM" J" " N-LVL@MM" <" N-LVL?MM" " O" " N-LVLRMM" S"K" " " @" N-LVLPMM" " ?" " " " R" "" " " " 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 เปนราก จะไดวา

!()

==

-)/0)&00128A9>0428A9>-)/0)&9%:;%-#%%<=>?

028A9>MMMQLLLMMMQLLL

MMLL "

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

T'W0LVLHMM"T'W0LVLIMM" T'W0LVLNMM H"

T'W0LVLJMM" T'W0LVL<MM" I" T'W0LVLOMM" T'W0LVLSMM" N "T'W0LVLKMM" J" T'W0LVL@MM" T'W0LVL?MM" <" " O" T'W0LVLRMM" S" "

K" " T'W0LVLPMM" @" ?" " " " " " "

" " P" " " " " " " " ""

จากนิยามการแวะผานแบบหลังลําดับขางบนนี้สามารถเขียนเปนโปรแกรมแบบเวียนเกิดไดดังนี้ 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)

P

H

I N

J <

?

O

RK @

S

!

$

%

#

7

! $ % # 7 D F E G C $! $$

ภาพประกอบ 10.14 ตัวอยางการวาดรูปตนไม

กิจกรรม 10.1 การสรางตนไมนิพจน

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

!)!

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

หารดวย 1 ยกกําลังดวย 0 หรือ 1 เปนตน