1 / 40

Chapter 10: Trees

Chapter 10: Trees. Applications. Implementations. Traversals. Balancing. CS 240. 182. Sample Tree. Tree Abstract Data Type. Root. The tree abstract data type provides a hierarchical structure to the representation of certain types of relationships. Leaf. Node. Leaf.

nevan
Télécharger la présentation

Chapter 10: Trees

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 10: Trees Applications Implementations Traversals Balancing CS 240 182

  2. Sample Tree Tree Abstract Data Type Root The tree abstract data type provides a hierarchical structure to the representation of certain types of relationships. Leaf Node Leaf • In addition to constructors and a destructor, the tree ADT needs the following functions: • isEmpty- to determine whether the tree contains any entries • insert- to insert a new entry at the appropriate location within the tree • traverse- some mechanism to explore the tree Node Node Node Leaf Leaf Node Leaf Leaf Leaf Node Node Leaf Leaf Node Leaf Leaf CS 240 183

  3. Sample Binary Tree Binary Tree ADT Root The binary tree abstract data type is a structure in which entries are configured to have at most two offspring. Leaf Node The binary aspect of this structure makes it particularly useful in applications where individual choices are needed, making it possible to recursively proceed to the right offspring or to the left offspring of the current node. Node Node Node Node Leaf For example, video games frequently present a player with two options, using a game tree to keep track of the alternatives selected by the player. Node Leaf Node Leaf Node Leaf Leaf Leaf CS 240 184

  4. value sibling nodes value value value value value value value value value parent node value value value child node value leaf nodes value value Basic Binary Tree Terminology CS 240 185

  5. 1000 875 650 325 700 575 250 425 150 400 75 275 350 25 100 Application: Heap By keeping the data in each node greater than or equal to the data in both of its subtrees, the nodes with the highest values are closer to the root and, consequently, more accessible. CS 240 186

  6. 72 41 90 23 50 74 97 14 38 67 82 99 31 79 98 Application: Binary Search Tree This structure keeps the data in each node's left subtree less than or equal to the node's data, and the data in the node's right subtree greater than or equal to the node's data. When the data is well distributed, this significantly reduces data search time. CS 240 187

  7. + / * + C D % A B + - E F G H Application: Binary Expression Tree This structure strategically places the binary operators in the upper nodes, and the primitive operands in the leaf nodes, facilitating the quick evaluation of the overall expression. Example: (A+B)/C+D*((E+F)%(G-H)) CS 240 188

  8.                        Binary Tree: Array Implementation • Place root in slot 0 • Place left child of slot k's node in slot 2 * k + 1 • Place right child of slot k's node in slot 2 * k + 2 • Locate parent of slot k's node in slot (k - 1) / 2 0  28 56 84 112 140 168 196 1  29  57 85 113 141 169 197 2  30 58 86 114 142 170 198 3  31 59 87 115 143 171 199 4  32 60 88 116 144 172 200 5  33 61 89 117 145 173 201 6  34 62 90 118 146 174 202 7 35 63 91 119 147 175 203 8 36 64 92 120 148 176 204 9  37 65 93 121 149 177 205  10 38 66 94 122 150 178 206 11  39  67 95 123 151 179 207 12 40  68 96 124 152 180 208 13 41 69 97 125 153 181 209 14  42 70 98 126 154 182 210 15 43 71 99  127 155 183 211 16 44 72 100  128 156 184 212 17 45 73 101 129 157 185 213 18 46 74 102  130 158 186 214 19  47 75 103 131 159 187 215 20  48 76 104 132 160 188 216 21 49  77 105 133 161 189 217 22 50  78 106 134 162 190 218 23  51 79 107 135 163 191 219 24  52 80 108 136 164 192 220 25 53 81 109 137 165 193 221 26 54 82  110 138 166 194 222 27 55 83 111 139 167 195 223 CS 240 189

  9. Binary Tree: Array Implementation // Class declaration file: BinTree.h ///////////////////////////////////////////////////////////////////////// // This file contains the array implementation of the binary tree ADT. // ///////////////////////////////////////////////////////////////////////// #include <assert.h> #include <iostream> #include <iomanip> using namespace std; #ifndefBIN_TREE_H //////////////////////////////////////////////////////////// // DECLARATION SECTION FOR THE BINARY TREE CLASS TEMPLATE // //////////////////////////////////////////////////////////// constintMAX_TREE_NODES = 15; template <class E> classbinary_tree { public: // Class constructors binary_tree(); binary_tree(constbinary_tree<E> &bt); // Member functions boolisEmpty(); voidinsert(const E &item); voidpreorder_traverse(int location); voidinorder_traverse(int location); voidpostorder_traverse(int location); binary_tree<E>& operator = (constbinary_tree<E> &bt); void display_array(); CS 240 190

  10. protected: // Data members structnode // The node structure is { // set up so that if the boolvacant; // vacant field is FALSE, E data; // then the data field is }; // irrelevant. node tree[MAX_TREE_NODES]; // Array of nodes intnumber_nodes; // Number of nodes in tree // Member function voidinsertStartingHere(const E &item, intlocation); }; /////////////////////////////////////////////////////////////// // IMPLEMENTATION SECTION FOR THE BINARY TREE CLASS TEMPLATE // /////////////////////////////////////////////////////////////// // Default constructor. // template<class E> binary_tree<E>::binary_tree() { number_nodes= 0; for(int i = 0; i < MAX_TREE_NODES; i++) tree[i].vacant = true; } // Copy constructor. // template<class E> binary_tree<E>::binary_tree(constbinary_tree<E> &bt) { number_nodes= bt.number_nodes; for(int i = 0; i < MAX_TREE_NODES; i++) if(bt.tree[i].vacant) tree[i].vacant = true; else tree[i] = bt.tree[i]; } CS 240 191

  11. // Assignment operator. // template<class E> binary_tree<E>& binary_tree<E>::operator = (constbinary_tree<E> &bt) { number_nodes= bt.number_nodes; for(int i = 0; i < MAX_TREE_NODES; i++) if(bt.tree[i].vacant) tree[i].vacant = true; else tree[i] = bt.tree[i]; return*this; } // Empty function. // template<class E> boolbinary_tree<E>::isEmpty() { return(number_nodes == 0); } // Insert function; inserts via insertStartingHere function. // template<class E> voidbinary_tree<E>::insert(const E &item) { assert(number_nodes< MAX_TREE_NODES); // Room in tree? insertStartingHere(item, 0); number_nodes++; } CS 240 192

  12. // Preorder_traverse function; prints tree contents in preorder. // template<class E> voidbinary_tree<E>::preorder_traverse(int location) { if((location < MAX_TREE_NODES) && (!tree[location].vacant)) { cout<< tree[location].data << '\t'; preorder_traverse(2 * location + 1); preorder_traverse(2 * location + 2); } return; } // Inorder_traverse function; prints tree contents in inorder. // template<class E> voidbinary_tree<E>::inorder_traverse(int location) { if((location < MAX_TREE_NODES) && (!tree[location].vacant)) { inorder_traverse(2 * location + 1); cout<< tree[location].data << '\t'; inorder_traverse(2 * location + 2); } return; } // Postorder_traverse function; prints tree contents in postorder. // template<class E> voidbinary_tree<E>::postorder_traverse(int location) { if((location < MAX_TREE_NODES) && (!tree[location].vacant)) { postorder_traverse(2 * location + 1); postorder_traverse(2 * location + 2); cout<< tree[location].data << '\t'; } return; } CS 240 193

  13. // Display_array function; prints tree contents as an // // array, printing the word vacant if a node is vacant. // template<class E> voidbinary_tree<E>::display_array() { intindex; for(int i = 0; i < MAX_TREE_NODES/5; i++) { for(int j = 0; j < 5; j++) { index = (MAX_TREE_NODES / 5) * j + i; cout<< setw(2) << index << " = "; if(tree[index].vacant) cout<< setw(6) << "vacant" << setw(3) << ""; else cout<< setw(6) << tree[index].data << setw(3) << ""; } cout<< endl; } } // InsertStartingHere function; inserts item into tree using ordering (i.e., // // inserting smaller values to the left and larger values to the right). // template<class E> voidbinary_tree<E>::insertStartingHere(const E &item, intlocation) { assert(location < MAX_TREE_NODES); // Must be legitimate array index if(tree[location].vacant) { tree[location].data = item; tree[location].vacant = false; } else if (item < tree[location].data) insertStartingHere(item, 2 * location + 1); else insertStartingHere(item, 2 * location + 2); } #define BIN_TREE_H #endif CS 240 194

  14. Sample Driver For Array Implementation // Program file: TreeDriver.cpp ////////////////////////////////// // This program illustrates the // // creation of a binary tree. // ////////////////////////////////// #include <iostream> #include "BinTree.h" using namespace std; voidprint_tree(binary_tree<int> &tree); // The main function queries the // // user for new tree elements. // voidmain() { binary_tree<int> tree; intnumber; cout<< "Enter a number: "; cin>> number; while(number > 0) { tree.insert(number); tree.display_array(); cout<< "Enter a number: "; cin>> number; } print_tree(tree); } // The print_tree function outputs the final // // contents of the tree, first by displaying // // the entire array, then by printing out // // the non-vacant tree elements in inorder, // // preorder, and postorder. // void print_tree(binary_tree<int> &tree) { cout<< "Array contents:" << endl; tree.display_array(); cout << endl << "Inordertraversal: " << endl; tree.inorder_traverse(0); cout<< endl; cout<< "Preorder traversal: " << endl; tree.preorder_traverse(0); cout<< endl; cout<< "Postorder traversal: " << endl; tree.postorder_traverse(0); cout<< endl << endl; } CS 240 195

  15. CS 240 196

  16. Application For Array Implementation: Heaps // IMPLEMENTATION SECTION FOR // THE HEAP CLASS TEMPLATE // Default constructor. // template <class E> heap<E>::heap(): binary_tree<E>() { } // Copy constructor. // template <class E> heap<E>::heap(const heap<E> &h) { number_nodes = h.number_nodes; for (int i=0; i<MAX_TREE_NODES; ++i) if (h.tree[i].vacant) tree[i].vacant = true; else tree[i] = h.tree[i]; } // Assignment operator. // template <class E> heap<E>& heap<E>::operator = (const heap<E> &h) { number_nodes = h.number_nodes; for (int i=0; i<MAX_TREE_NODES; ++i) if (h.tree[i].vacant) tree[i].vacant = true; else tree[i] = h.tree[i]; return *this; } // Class declaration file: heap.h// // This file contains the array // // implementation of the heap ADT. // #include "BinTree.h" #include <assert.h> #include <iostream> #ifndefHEAP_H // DECLARATION SECTION FOR // THE HEAP CLASS TEMPLATE template <classE> class heap: public binary_tree<E> { public: // Class constructors heap(); heap(const heap<E> &h); // Member functions void insert(const E &item); heap<E>& operator = (const heap<E> &h); }; CS 240 197

  17. // Insert function; inserts into the heap to retain the heap structure // // and to ensure non-fragmentation of the array structure, moving low // // elements down when a high element is inserted. // template <class E> void heap<E>::insert(const E &item) { intlocation, parent; assert(number_nodes < MAX_TREE_NODES); // Now walk the new item up the tree, starting at location location = number_nodes; parent = (location-1) / 2; while ((location > 0) && (tree[parent].data < item)) { tree[location] = tree[parent]; location = parent; parent = (location-1) / 2; } tree[location].data = item; tree[location].vacant = false; number_nodes++; } #define HEAP_H #endif CS 240 198

  18. Example Driver For The Heap Application // Program file: heapdriv.cpp // // This program illustrates // // the creation of a heap. // #include <iostream> #include "heap.h" void print_heap(heap<int> &tree); // The main function queries the // // user for new heap elements. // void main() { heap<int> tree; intnumber; cout << "Enter a number: "; cin >> number; while (number > 0) { tree.insert(number); tree.display_array(); cout << "Enter a number: "; cin >> number; } print_heap(tree); } // The print_heap function outputs // // the final contents of the heap, // // first by displaying the entire // // array, then by printing out the // // non-vacant heap elements in in- // // order, preorder, and postorder. // void print_heap(heap<int> &tree) { cout << "Array contents: \n"; tree.display_array(); cout << "\nInorder traversal: \n"; tree.inorder_traverse(0); cout << "\nPreorder traversal: \n"; tree.preorder_traverse(0); cout << "\nPostorder traversal: \n"; tree.postorder_traverse(0); cout << endl << endl; } CS 240 199

  19. CS 240 200

  20. Binary Tree: Linked List Implementation // Class declaration file: bintree.h // This file contains the array implementation of the binary tree ADT. // #ifndefBIN_TREE_H #include <stdlib.h> #include <assert.h> #include <fstream> #include <iomanip> // DECLARATION SECTION FOR LINKED VERSION OF BINARY TREE template <class E> class binary_tree { public: // Class constructors and destructor binary_tree(); binary_tree(constbinary_tree<E> &bt); ~binary_tree(); // Member functions boolisEmpty() const; void insert(const E &item); void preorderTraverse(ofstream &os) const; void inorderTraverse(ofstream &os) const; void postorderTraverse(ofstream &os) const; binary_tree<E>& operator = (constbinary_tree<E> &bt); CS 240 201

  21. protected: // Data members structnode; typedefnode *node_ptr; structnode { E data; node_ptr left; node_ptr right; }; node_ptr root; // Member functions node_ptrget_node(const E &data); void insertIntoTree(node_ptr &treeRoot, constE &data); void copyTree(node_ptrtreeRoot) const; void destroyTree(node_ptrtreeRoot); void preorderOutput(node_ptrtreeRoot, ofstream &os) const; void inorderOutput(node_ptrtreeRoot, ofstream &os) const; void postorderOutput(node_ptrtreeRoot, ofstream &os) const; }; // IMPLEMENTATION SECTION FOR LINKED VERSION OF BINARY TREE // Default constructor. // template <class E> binary_tree<E>::binary_tree() { root = NULL; } CS 240 202

  22. // Copy constructor. // template <class E> binary_tree<E>::binary_tree(constbinary_tree<E> &bt) { root = NULL; copyTree(bt.root) } // Assignment operator. // template <class E> binary_tree<E>& binary_tree<E>::operator = (constbinary_tree<E> &bt) { root = NULL; copyTree(bt.root); return *this; } // Function copyTree: Starting at the node pointed to by // // parameter treeRoot, this function recursively copies the // // elements of an existing tree into the *this tree. By // // proceeding in a preorder fashion, this function ensures // // that the elements of *this will correspond exactly (in // // value and position) to the elements of the existing tree. // template <class E> void binary_tree<E>::copyTree(node_ptrtreeRoot) const { if (treeRoot != NULL) { insert(treeRoot->data); copyTree(treeRoot->left); copyTree(treeRoot->right); } } CS 240 203

  23. // Destructor. // template <class E> binary_tree<E>::~binary_tree() { destroyTree(root); root = NULL; } // Function destroyTree: returns all memory associated with // // the tree to the system heap, by recursively destroying // // the two subtrees and then deleting the root. // template <class E> void binary_tree<E>::destroyTree(node_ptrtreeRoot) { if (treeRoot != NULL) { destroyTree(treeRoot->left); destroyTree(treeRoot->right); delete treeRoot; } } // Empty function. // template <class E> boolbinary_tree<E>::isEmpty() const { return root == NULL; } CS 240 204

  24. // Insert function. // template <class E> void binary_tree<E>::insert(const E &item) { insertIntoTree(root, item); } // Function insertIntoTree: recursively inserts parameter // // item into tree using binary search tree assumption. // template <class E> void binary_tree<E>::insertIntoTree(node_ptr &treeRoot,constE &item) { if (treeRoot == NULL) treeRoot = get_node(item); elseif (item < treeRoot->data) insertIntoTree(treeRoot->left, item); else insertIntoTree(treeRoot->right, item); } CS 240 205

  25. // Function preorderTraverse. // template <class E> void binary_tree<E>::preorderTraverse(ofstream &os) const { preorderOutput(root, os); } // Function preorderOutput: Outputs contents of // // the tree into parameterized file by recursively // // traversing the tree in preorder. // template <class E> void binary_tree<E>::preorderOutput(node_ptrtreeRoot, ofstream &os) const { if (treeRoot != NULL) { os << treeRoot->data << endl; preorderOutput(treeRoot->left, os); preorderOutput(treeRoot->right, os); } } CS 240 206

  26. // Function inorderTraverse. // template <class E> void binary_tree<E>::inorderTraverse(ofstream &os) const { inorderOutput(root, os); } // Function inorderOutput: Outputs contents of // // the tree into parameterized file by recursively // // traversing the tree in inorder. // template <class E> void binary_tree<E>::inorderOutput(node_ptrtreeRoot, ofstream &os) const { if (treeRoot != NULL) { inorderOutput(treeRoot->left, os); os << treeRoot->data << endl; inorderOutput(treeRoot->right, os); } } CS 240 207

  27. // Function postorderTraverse. // template <class E> void binary_tree<E>::postorderTraverse(ofstream &os) const { postorderOutput(root, os); } // Function postorderOutput: Outputs contents of // // the tree into parameterized file by recursively // // traversing the tree in postorder. // template <class E> void binary_tree<E>::postorderOutput(node_ptrtreeRoot, ofstream &os) const { if (treeRoot != NULL) { postorderOutput(treeRoot->left, os); postorderOutput(treeRoot->right, os); os << treeRoot->data << endl; } } CS 240 208

  28. // Function get_node: Dynamically allocates space for a new // // tree node, placing the parameter item's value into the // // new node, and initializing both of its pointers to NULL. // template <class E> typenamebinary_tree<E>::node_ptrbinary_tree<E>::get_node(const E &item) { node_ptr temp = new node; assert(temp != NULL); temp->data = item; temp->left = NULL; temp->right = NULL; return temp; } #define BIN_TREE_H #endif CS 240 209

  29. Example Driver For The Linked List Implementation // Program file: testtree.cpp // This program illustrates working // with a binary tree. The input // consists of an unordered list of // integers from a file. The // output consists of three files // containing the preorder, inorder, // and postorder traversals of the // binary search tree that is con- // structed from the input integers. #include <iostream> #include <fstream> #include "BinTree.h" void main() { intnumber; ifstreaminput_file; ofstreampre_file; ofstreamin_file; ofstreampost_file; binary_tree<int> tree; input_file.open("numbers.txt"); pre_file.open("preordered.txt"); in_file.open("inordered.txt"); post_file.open("postordered.txt"); input_file >> number; while (!input_file.eof()) { tree.insert(number); input_file >> number; } input_file.close(); tree.preorderTraverse(pre_file); tree.inorderTraverse(in_file); tree.postorderTraverse(post_file); pre_file.close(); in_file.close(); post_file.close(); return; } CS 240 210

  30. File Contents 79 54 47 43 13 23 14 24 42 31 29 49 47 78 76 74 68 78 79 96 13 14 23 24 29 31 42 43 47 47 49 54 68 74 76 78 78 79 79 96 54 79 14 29 31 42 24 23 13 43 47 49 47 68 74 76 78 78 54 96 79 79 47 78 96 43 49 76 78 13 47 23 74 14 24 68 42 preordered.txt 31 inordered.txt postordered.txt 29 79 numbers.txt 79 54 47 43 13 49 78 23 24 78 42 76 31 74 29 47 79 14 68 96 CS 240 211

  31. Additional Tree Traversal Member Function #1 // Public function to “spell out” the values from the root to the tree’s // // leaf nodes, with the results output on a separate line for each leaf. // template <class E> void BinaryTree<E>::spellFromTop(ofstream &os) { continueSpelling(root, os, ""); } // Protected function to concatenate the char value of the current subtree’s // // root to the strparam. until a leaf is reached, whereupon str is output. // template <class E> void BinaryTree<E>::continueSpelling(nodePtrtreeRoot, ofstream &os, string str) { if (treeRoot == NULL) return; else { str += char(treeRoot->data); if ((treeRoot->left == NULL) && (treeRoot->right == NULL)) os << str << endl; else { continueSpelling(treeRoot->left, os, str); continueSpelling(treeRoot->right, os, str); } } } CS 240 212

  32. Applying spellFromTop To A Sample Tree str: “M” M I O str: “MI” str: “MO” str: “MOM” str: “MOP” str: “MIC” str: “MIL” C L M P output “MOM” str: “MILK” str: “MICE” str: “MOPS” str: “MILL” E K L S output “MICE” output “MILK” output “MILL” output “MOPS” CS 240 213

  33. Additional Tree Traversal Member Function #2 // Public function to output the sequence of left and right offspring that // // comprise the longest path from the tree’s root to one of its leaf nodes. // template <class E> void BinaryTree<E>::outputLongestPath(ofstream &os) { os << longestPath(root); } // Protected function to recursively generate a string indicating which // // offspring (left or right) yield the longest path from the root to a leaf. // template <class E> stringBinaryTree<E>::longestPath(nodePtrtreeRoot) { if (treeRoot == NULL) return""; else { string leftStr = longestPath(treeRoot->left); string rightStr = longestPath(treeRoot->right); if (leftStr.size() > rightStr.size()) { leftStr.insert(0, 'L'); return leftStr; } else { rightStr.insert(0, 'R'); returnrightStr; } } } CS 240 214

  34. Applying outputLongest To A Sample Tree “LRR” “RL” “L” “L” “RR” “R” “” “” “” “” Final result: “LLRR”  leftStr: “LLRR” rightStr: “RRL”   leftStr: “LRR” leftStr: “LL” rightStr: “” rightStr: “RL”    leftStr: “L” leftStr: “L” leftStr: “” rightStr: “” rightStr: “” rightStr: “RR”    leftStr: “” leftStr: “L” leftStr: “” rightStr: “” rightStr: “R” rightStr: “”   leftStr: “” leftStr: “” rightStr: “” rightStr: “” CS 240 215

  35. AVL Trees k2 k1 Z X Y k1 X k2 Y Z Although an average search in an n-node binary search tree is O(logn), the worst case could be as bad as O(n). An AVL (Adelson-Velskii and Landis) tree places a balance condition on a binary search tree by requiring the left and right subtrees of each node to have heights differing by at most one. During insertion, this is accomplished by means of single and double rotations. Single rotations: Note that in each of the trees illustrated at right: (any element of X)  k1 (any element of Y)  k2 (any element of Z) So when a new element’s insertion causes an imbalance, “rotate” the tree to restore the balance. CS 240 216

  36. Single Rotation Examples 27 27 27 INSERT 5 ROTATE 14 31 14 31 12 31 12 30 45 12 30 45 5 14 30 45 5 84 84 93 INSERT 99 ROTATE 75 93 75 93 84 98 92 98 92 98 75 92 99 99 CS 240 217

  37. Double Rotations k1 k3 A k2 D B C k2 k1 k3 A B C D k3 k1 H k2 E F G k2 k1 k3 E F G H If a single rotation doesn’t restore balance, a double rotation will. Note that in the two trees illustrated at right: (any value in A)  k1 (any value in B)  k2 (any value in C)  k3 (any value in D) Also note that in the two trees illustrated at right: (any value in E)  k1 (any value in F)  k2 (any value in G)  k3 (any value in H) If a single rotation fails to restore balance after a new insertion, a double rotation may be tried. CS 240 218

  38. Double Rotation Example SINGLE ROTATION 25 25 25 25 INSERT 47 16 16 49 49 16 49 16 36 9 9 19 19 36 36 64 64 9 19 36 64 9 19 31 49 31 31 41 41 31 41 41 64 47 47 DOUBLE ROTATION 25 25 INSERT 47 16 49 16 41 9 19 36 64 9 19 36 49 31 41 31 47 64 47 STILL UNBALANCED BALANCED! CS 240 219

  39. General Trees 6 6 6 6 6 6 6 6 6 6 6 2 2 2 2 8 2 6 2 2 2 2 2 8 8 8 8 8 8 8 8 8 8 8 1 3 3 3 3 3 3 3 3 6 3 3 7 5 7 2 5 7 5 3 3 2 3 2 8 7 4 7 7 7 7 7 7 7 7 7 1 1 1 1 1 1 1 1 1 1 1 4 5 5 5 5 5 5 5 5 1 5 5 4 4 4 4 4 4 4 4 4 4 4 There are occasions when non-binary trees are needed Application Example: Game Trees CS 240 220

  40. Binary Implementation Of General Trees A B C F D G I H E J K L M N A B C D E F G H I J K L M N A common approach for implementing general trees is to use binary trees, with offspring and sibling pointers Actual tree Binary representation, with left pointers for “first offspring” and right pointers for “next sibling” CS 240 221

More Related