General Trees A tree T is a finite set of one or more nodes such that there is one designated node r called the root of T, and the remaining nodes in (T-{r}) are partitioned into n 0 disjoint subsets T 1, T 2, …, T k, each of which is a tree, and whose roots r 1, r 2, …, r k, respectively, are children of r.
General Tree Example Root Ancestors of V R P V C1C1 C2C2 S1S1 S2S2 Siblings of V Subtree rooted at V Children of V
ADT of General Tree Node class Gtnode{ public: GTnode(const ELEM); ~GTnode(); ELEM value(); bool isLeaf(); GTnode * parent(); GTnode* leftmost_child(); GTnode* right_sibling(); void setValue(ELEM); void insert_first(GTnode *); void insert_next(GTnode *); void remove_first(); void remove_next(); };
ADT General Tree and Traversal class GenTree{ public: GenTree(); ~GenTree(); void clear(); GTnode * root(); void newroot(ELEM, GTnode *, GTnode *); }; void print(Gtnode * root) { if (root->isLeaf()) cout<<“Leaf:”; else cout<<“Internal:”; cout value()<<endl; Gtnode * temp=root->leftmost_child(); while (temp!=NULL) {print(temp); temp=temp->right_sibling();}}
Parent Pointer Implementation R AB F DEC ZYX W
Why Parent Pointer? Parent Pointer is good for answering the question: Are these two nodes in the same tree? Bool Gentree::sametree(int a, int b){ Gtnode * root1=a; Gtnode * root2=b; while (parent[root1] != -1) root1=parent[root1]; while (parent[root2] != -1) root2=parent[root2]; return root1 == root2; }
Equivalence Classes A set of items that are “equivalent”. The sets of equivalence classes are mutually exclusive. When 2 items are found to be equivalent, you want to union the two sets containing these items. If both items are already in the same set, then no need to do anything. When joining the 2 sets, want to keep the depth small. Join the tree with fewer nodes to the tree with more nodes. Path compression: want all nodes to point to the root.
Implementations of General Trees Lists of Children - Use an array of structures. Each element has the data, a parent pointer and a pointer to a list of pointers to the children of that node. –Easy to find leftmost child –To find a node’s sibling, first go to his parent, then go through the child list until you find a pointer to that node, the next node is his sibling. –Easy to combine trees if using just one array, difficult if using several arrays. Left Child/Right Sibling - Each node has a pointer to his leftmost child and a pointer to his right sibling (can also have a parent pointer). –Easy to find parent, leftmost child and right sibling. –Easy to combine trees if in same array.
Dynamic Node Implementation Allocate variable space for node for the number of children pointers. –Difficult to change (add or delete children). –Assumes know the number of children when creating. Keep a linked list of children pointers since do not know the number of children. –Same as Lists of Children method. Dynamic Left Child/Right Sibling method. –Each node has 2 or 3 pointers (left child, right sibling, parent). –Easy to work with forests K-ary trees - Have a fixed number of pointers in each node. –Large number of null pointers as K gets bigger.