330 likes | 529 Views
Trees ADT. การเข้าถึงข้อมูลที่มีโครงสร้างแบบ Linked List ซึ่งความสัมพันธ์ระหว่างข้อมูลแต่ละตัวเป็นแบบก่อน (Before) และหลัง (After) Linked List ให้ Running Time เป็นแบบ Linear, O(N) เมื่อจำนวน Input (N) มากๆ Running Time อาจจะมากเกินควร
E N D
TreesADT • การเข้าถึงข้อมูลที่มีโครงสร้างแบบ Linked List ซึ่งความสัมพันธ์ระหว่างข้อมูลแต่ละตัวเป็นแบบก่อน (Before) และหลัง (After) • Linked List ให้ Running Time เป็นแบบ Linear, O(N) • เมื่อจำนวน Input (N) มากๆ Running Time อาจจะมากเกินควร • โครงสร้างข้อมูลแบบต้นไม้ หรือ Trees ซึ่งเป็นโครงสร้างข้อมูลแบบง่ายที่ให้ Running Time เป็น O(log N)โดยเฉลี่ย ซึ่งเป็นการทำงานที่ใช้เวลาน้อยกว่ามาก Week 09 - Tree ADT
โครงสร้างของ Trees ADT • Tree เป็น Abstract Data Type ที่เก็บสมาชิกเป็นแบบลำดับชั้น(Hierarchical) • ความสัมพันธ์ระหว่างข้อมูลแต่ละตัวในโครงสร้างข้อมูลแบบ Trees จะเป็นแบบลำดับขั้น ซึ่งสมาชิกบางตัวอยู่เหนือ (above) สมาชิกอื่น และสมาชิกบางตัวอยู่ใต้ (below) สมาชิกอื่น • ทำงานแบบ nonlinear ซึ่งเป็นวิธีการที่ทำให้เราสามารถสร้าง Algorithm ที่ทำงานได้เร็วขึ้นมาก • ยกเว้นแต่สมาชิกที่อยู่บนสุด (Top Element) สมาชิกตัวอื่นๆแต่ละตัวจะมีสมาชิกแม่ (Parent) และสมาชิกลูก (Child) 0 ตัวหรือมากกว่า • สมาชิกตัวบนสุด (Top Element) ว่า Root ของ Tree Week 09 - Tree ADT
ตัวอย่างการประยุกต์ใช้งาน Tree ใช้ Trees ในการทำงานต่อไปนี้ • ใช้ในการสร้างระบบไฟล์ (File System) ใน Operating System • ใช้ในการประเมินนิพจน์ทางคณิตศาสตร์ (Arithmetic Expressions) • ใช้ช่วยในการหาข้อมูล (Search) เพื่อให้ได้เวลาโดยเฉลี่ยในการหาข้อมูล (Search) เป็น O(log N) และปรับปรุงวิธีการหาข้อมูล (Search) เพื่อให้ได้กรณีที่เลวร้ายที่สูด (Worst-Case) ในการหาข้อมูล (Search) ใช้เวลาไม่เกิน O(log N) Week 09 - Tree ADT
คุณสมบัติพื้นฐานของ Trees • จากนิยามแบบรีเคอร์ซีพ Tree เป็นชุด (Collection) ของ Nodes ซึ่งอาจเป็นศูนย์ (Empty) • Tree ประกอบด้วย Node หลัก Node r เรียกว่า Root หรือรากของต้นไม้ และมี ต้นไม้ย่อยหรือ Subtrees T1, T2, …, Tk • Subtree แต่ละอันจะถูกเชื่อมต่อด้วย Edge โดยตรงจาก Node r • Root ของแต่ละ Subtree ถือว่าเป็น Child ของ r และเรียกได้ว่า r เป็น Parent ของ Subtree แต่ละตัว Edge Subtree Week 09 - Tree ADT
Tree เป็นชุด (Collection) ของ Node จำนวน N nodes • มีกิ่งหรือ Edges ทั้งหมด N – 1 กิ่ง โดยแต่ละกิ่งทำหน้าที่เชื่อม Nodes เข้ากับ Parent ของมัน • ทุก Node ยกเว้น Root จะ Parent 1 ตัว ยกเว้น Root ซึ่งไม่มี Parent • จากรูป Node A เป็น Root ของ Tree นี้ Week 09 - Tree ADT
แต่ละ Node มี Child จำนวนหนึ่ง ซึ่งอาจเป็น 0 • Node ที่ไม่มี Children เรียกว่า Leave หรือใบของต้นไม้ • Node ที่มี Parent เดียวกันเรียกว่าเป็น Siblings (พี่น้อง) กัน Week 09 - Tree ADT
Path หมายถึงเส้นทาง เช่นจาก node n1 ไปยัง nk โดยที่ ni เป็น parent ของ ni+1 สำหรับ 1≤ i < k • Length หมายถึงระยะทางของ Path นี้ซึ่งก็คือจำนวนกิ่ง (Edge) บน path คิดเป็น k – 1 • สังเกตได้ว่า ใน Tree หนึ่งๆ จะมี path 1 path จาก Root Week 09 - Tree ADT
Depth ของ ni หมายถึง Length ของ path เฉพาะจาก Root มาถึง ni ดังนั้นถือได้ว่า Depth ของ Root เป็น 0 • Height ของ ni หมายถึง Length ของ path ที่ยาวที่สุดจาก ni ไปยัง Leave ของ Tree ดังนั้นถือได้ว่า Leave ทุกตัวอยู่ที่ Height ที่ 0 Week 09 - Tree ADT
Height ของ Tree เท่ากับค่า height ของ Root • Depth ของ Tree เท่ากับค่า depth ของ leave ที่อยู่ลึกที่สุด ซึ่งจะมีค่าเท่ากับ Height ของ Tree เสมอ • ถ้ามี Path จาก n1 ไปยัง n2 แสดงว่า • n1 เป็น Ancestor(บรรพบุรุษ)ของ n2 • n2 เป็น Descendant(ลูกหลาน) ของ n1 Week 09 - Tree ADT
การสร้างโครงสร้างข้อมูลแบบTreeการสร้างโครงสร้างข้อมูลแบบTree วิธีการหนึ่งในการสร้าง Trees คือการสร้าง Node โดยแต่ละNode มีองค์ประกอบต่อไปนี้ • ข้อมูล • Pointer ไปยัง Childของ Node นั้นๆ • เนื่องจากไม่มีการกำหนดจำนวน Child ของแต่ละ Node ไว้ล่วงหน้า การสร้าง Pointer เพื่อเชื่อมไปยัง Child แต่ละตัวโดยตรงจะทำให้เสียเนื้อที่มาก • ทางออกอย่างง่ายๆคือการเก็บ Child ของแต่ละ Node ไว้ใน Linked Listที่เก็บโหนดต่างๆของต้นไม้ Week 09 - Tree ADT
struct TreeNode { string element ; TreeNode* firstChild ; TreeNode* nextSibling ; } element nextSibling firstChild • firstChild คือ Pointer ที่ชี้ลงด้านล่าง • nextSibling คือPointer ที่ชี้จากซ้ายไปขวา • NULL Pointer จะไม่ได้วาดเอาไว้เนื่องจากมีเป็นจำนวนมาก Week 09 - Tree ADT
การเดินทางใน Tree (Tree Traversal) • Traversal สำหรับ Tree คือระบบวิธีในการเข้าถึง (access/ visit) สมาชิก Nodes ทั้งหมดที่มีอยู่ใน Tree • พิจารณา Tree ที่เป็นโครงสร้างไดเร็กทอรีของ Operating System ทั่วไป เช่นโครงสร้างระบบไฟล์ใน UNIX Week 09 - Tree ADT
Root Directory ของคือ Node /usr • /usr มี Children ทั้งหมด 3 ตัว ได้แก่ mark, alex และ bill ซึ่งล้วนเป็น Directory เช่นเดียวกัน ดังนั้น เรียกได้ว่า /usr ประกอบด้วย directories 3 อันแต่ไม่มี file • File ที่ชื่อว่า /usr/mark/book/ch1.r ได้มาจากการไล่ไปตาม child ตัวซ้ายสุด 3 ครั้ง Week 09 - Tree ADT
พิจารณา /usr/mark/book/ch1.r เครื่องหมาย / แต่ละอันหลังจากอันแรก แสดง Edge แต่ละกิ่ง ผลลัพธ์ที่ได้คือชื่อเต็มของ pathname • ระบบไฟล์แบบลำดับขั้น (Hierarchical File System) • File ใน directory ต่างกันสามารถมีชื่อซ้ำกันได้ เนื่องจากมี path จาก root ต่างกันซึ่งทำให้มี pathname ที่ต่างกันไป Week 09 - Tree ADT
Directory Listing • แสดงรายชื่อของ Files ทั้งหมดใน directory ออกมา • File ที่มี Depth di จะถูกแท็บ (Tab) ย่อหน้า (indent) เข้าไป di แท็บ ListDir (DirectoryOrFile D, int Depth) { PrintName(D, Depth); if(D is a directory) for each child, C, of D ListDir(C, Depth+1); } main() { … ListDir(D, 0); } Week 09 - Tree ADT
ListDir (DirectoryOrFile D, int Depth) { PrintName(D, Depth); if(D is a directory) for each child, C, of D ListDir (C, Depth+1); } main() { … ListDir (D, 0); } • ใช้ Recursive Function ที่ชื่อ ListDir • ค่าอาร์กิวเมนต์ที่ส่งให้กับ ListDir เป็นค่าอ้างอิงไปยัง Tree ได้แก่ชื่อDirectory หรือ File ที่ต้องการแสดงชื่อ และ ค่า Depth ของ Directory หรือ File นั้นๆ ซึ่งบ่งบอกถึงจำนวน tab ที่ต้องแสดง Week 09 - Tree ADT
ListDir (DirectoryOrFile D, int Depth) { PrintName(D, Depth); if(D is a directory) for each child, C, of D ListDir(C, Depth+1); } main() { … ListDir(D, 0); } ชื่อของ Directory หรือ File จะถูก print ออกพร้อมกับจำนวน Tabs ที่ต้องการ print ไว้ด้านหน้า • Printname ซึ่งมีพารามิเตอร์คือ ชื่อ File/Directory และ จำนวน Tab ที่ต้องแสดงก่อนหน้าชื่อ ซึ่งระบุโดย Depth • ถ้าอาร์กิวเมนต์ที่ส่งให้กับ ListDir เป็น Directory ฟังก์ชั่นจะทำการเรียกตัวเองเพื่อทำงานแบบ Recursive กับ Child ทุกตัวของ Directory นั้นๆทีละตัว แต่เนื่องจาก Child เหล่านี้มี Depth เพิ่มขึ้น 1 ค่าจึงต้องการ Tab เพิ่มขึ้นเป็น Depth + 1 Week 09 - Tree ADT
วิธีการท่อง Tree แบบนี้เรียกว่า Tree Traversal แบบ Preorder Week 09 - Tree ADT
Tree Traversal • Preorder Traversal:Root ของ Tree จะถูก visit ก่อน จากนั้นจึงค่อย visit Subtrees ที่เป็น root ของ child แต่ละตัว โดยงานที่ต้องกระทำกับ Node จะทำก่อน (pre) ที่จะเลื่อนไป visit Child ของ Node นั้นๆ • Postorder Traversal:Subtrees ที่เป็น root ของ child แต่ละตัวจะถูก visit แบบ recursive ก่อนที่จะ visit ตัว Root โดยงานที่ต้องกระทำกับ Node จะทำหลัง (post) จากที่ visit Child ของ Node และทำงานบน Child ของ Node นั้นๆหมดแล้ว Week 09 - Tree ADT
Preorder Traversal Week 09 - Tree ADT
Postorder Traversal Week 09 - Tree ADT
Binary Tree • Binary Tree หมายถึง Tree ที่แต่ละ Node ไม่สามารถมี Child ได้เกินกว่า 2 ตัว • Binary Tree ที่ประกอบด้วย Root และ Subtree 2 อันคือ TL และ TR ซึ่งทั้งคู่อาจจะเป็น Tree ว่างก็ได้ Week 09 - Tree ADT
Depth ของ Binary Tree โดยเฉลี่ยจะมีค่าน้อยกว่า N ค่อนข้างมาก • ค่า Depth เฉลี่ยอยู่ที่ O(√N) • ค่า Depth มีอาจสูงถึง N-1 ก็ได้เช่นเมื่อ Tree มีลักษณะดังรูป Week 09 - Tree ADT
การสร้างต้นไม้แบบทวิภาค (Binary Tree Implementation) • Binary Tree สามารถมี Child ได้มากที่สุดเพียงแค่ 2 ตัว จึงสามารถสร้าง Pointer ไปยัง Child แต่ละตัวได้โดยตรง • Node มีโครงสร้างเป็นแบบ Structure ที่ประกอบด้วยส่วน Key element เพื่อเก็บข้อมูล และ Pointer 2 ตัว (Left และ Right) เพื่อชี้ไปยัง Nodes อื่น struct TreeNode { string element ; TreeNode* left ; TreeNode* right ; } left element right Week 09 - Tree ADT
การประยุกต์ใช้งาน Binary Treeต้นไม้ของนิพจน์ทางคณิตศาสตร์ (Expression Tree) • เราสามารถแทนนิพจน์ทางคณิตศาสตร์ด้วยโครงสร้างข้อมูลแบบต้นไม้ได้ เช่นแทน ((A*B) + C) + ((D + (E / F)) - G) Week 09 - Tree ADT
ต้นไม้ของนิพจน์ทางคณิตศาสตร์ (Expression Tree) • โหนดใบ (Leave)ของ Expression Tree คือ Operands(ตัวถูกดำเนินการ) ได้แก่ค่าคงที่ (Constant) หรือตัวแปร (Variable) • โหนดอื่นๆบรรจุ Operators (ตัวดำเนินการ) Week 09 - Tree ADT
การคำนวณหาค่าของ Expression Tree หาค่าของ Infix expression (อาจรวมวงเล็บด้วย) ได้โดย • หาค่าของนิพจน์ทางคณิตศาสตร์ (Expression) พร้อมวงเล็บ ของ subtree หรือ tree ทางด้านซ้ายด้วยวิธีแบบ recursive • แสดงค่า Operator ที่ Root ของ subtree/tree ออกมา • หาค่าของนิพจน์ทางคณิตศาสตร์ (Expression) พร้อมวงเล็บ ของ subtree หรือ tree ทางด้านขวาด้วยวิธีแบบ recursive • วิธีการที่กระทำการบนสมาชิกทางด้านซ้ายของโหนดก่อนตัวโหนด แล้วตามการกระทำบนสมาชิกทางด้านขวาของโหนด (Left, Node, Right) นี้เรียกว่า Inorder Traversal Week 09 - Tree ADT
การคำนวณหาค่าของ Expression Tree Inorder Traversal (Left, Node, Right) • ให้ Infix Expression ((A*B)+C)+((D+(E/F))-G) Postorder Traversal (Left, Right, Node) • ให้ Postfix Expression AB*C+DEF/+G-+ Preorder Traversal (Node, Left, Right) • ให้ Prefix Expression ++*ABC-+D/EFG Week 09 - Tree ADT
การคำนวณหาค่าของ Expression Tree Infix Expression Postfix Expression Prefix Expression (A + (B/C)) + ((D*E)+((F-G)*H)) A B C / + D E * F G – H * + + + + A / B C + * D E * - F G H Week 09 - Tree ADT
การสร้างต้นไม้ของนิพจน์ทางคณิตศาสตร์ การเปลี่ยน Postfix Expression เป็น Expression Tree • อ่านนิพจน์ทางคณิตศาสตร์ (Expression) เข้ามาทีละตัว • ถ้าอักษรที่ได้เป็น Operand (ตัวถูกดำเนินการ) ให้สร้างต้นไม้ขนาด 1 โหนด แล้ว push พอยน์เตอร์ 1 อันลงบนสแตก • ถ้าอักษรที่ได้เป็น Operator (ตัวดำเนินการ) ให้ pop พอยน์เตอร์ไปยังต้นไม้ 2 อันคือ T1 และ T2 ออกมาจากสแตกแล้วสร้างต้นไม้ใหม่ขึ้นหนึ่งอันให้มี Root อยู่ที่ Operator ที่รับเข้ามาและมี Child ด้านซ้ายและด้านขวา เป็น T1 และ T2 ตามลำดับ • พอยน์เตอร์ที่ชี้ไปยังต้นไม้ที่สร้างขึ้นมาใหม่นี้ จะถูก Push ลงไปบนสแตก Week 09 - Tree ADT
Inputa b + c d e + * * a b + c d e + * * • Input 2 ตัวแรกเป็น Operand จึงสร้างต้นไม้ขนาด 1 โหนดแล้ว push พอยน์เตอร์ 1 อันลงบนสแตก a b + c d e + * * • อ่าน + เข้ามา จึง pop พอยน์เตอร์ออกมาจากสแตก สร้างต้นไม้ใหม่ Push พอยน์เตอร์ที่ชี้ไปยังต้นไม้ที่สร้างขึ้นมาใหม่ลงไปบนสแตก Week 09 - Tree ADT
a b + c d e + * * • อ่าน c, d, e เข้ามา สร้างต้นไม้ขนาด 1 โหนด สำหรับอักษรแต่ละตัว แล้ว Push พอยน์เตอร์ที่ชี้ไปยังต้นไม้ แต่ละอันนี้ลงบนสแตก a b + c d e + * * • อ่าน + เข้ามา จึง pop พอยน์เตอร์ออกมาจากสแตก สร้างต้นไม้ใหม่ Push พอยน์เตอร์ที่ชี้ไปยังต้นไม้ที่สร้างขึ้นมาใหม่ลงไปบนสแตก Week 09 - Tree ADT
a b + c d e + * * • อ่าน * เข้ามา จึงทำการ Pop พอยน์เตอร์ออกมาจากสแตก แล้วสร้างต้นไม้ใหม่โดยมี * ที่อ่านเข้ามาเป็น Root a b + c d e + * * • อ่าน * เข้ามา ทำการ Pop พอยน์เตอร์ออกมาจากสแตก แล้วสร้างต้นไม้ใหม่โดยมี * ที่อ่านเข้ามาเป็น Root Week 09 - Tree ADT