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