Trees and beyond Tutorial #3 CPSC 261
Trees are just an example The next two weeks in labs we are playing with trees – Trees are interesting – Trees are not the only interesting data structure – They are a good, first, simple data structure that uses pointers – We could use lists, graphs, etc. – Trees fit the bill perfectly
What is a tree? A directed, acyclic graph, where every node (except the root) has exactly one incoming edge
Diagrammatically
A binary tree No node has more than 2 children
A “complete” binary tree Every node has either 2 or 0 children All leaves are at the same “level”
Node ordering At their most general, trees assume nothing about the order of their nodes Enforcing order provides value – Search trees – Binary search trees – Heaps – B-Trees
Thoughts about trees They grow upside down (the root is at the top) They are used for many purposes – A look-up structure – Sorting – Some of Java’s collection classes are trees TreeSet TreeMap
Trees in C In C, we use structures like this to represent trees struct tree { sometype value; long nchildren; struct tree *children[MAXC]; }
Trees in C Or in the case of binary trees struct tree { sometype value; struct tree *left, *right; } Why no nchildren ?
Traversing Trees in C – pre-order Visiting all the nodes of a tree void visit(struct tree *t) { if (t == NULL) return; // do something with t->value visit(t->left); visit(t->right); }
Traversing Trees in C – in-order Visiting all the nodes of a tree void visit(struct tree *t) { if (t == NULL) return; visit(t->left); // do something with t->value visit(t->right); }
Traversing Trees in C – post-order Visiting all the nodes of a tree void visit(struct tree *t) { if (t == NULL) return; visit(t->left); visit(t->right); // do something with t->value }
Questions about visits Will visit always terminate? – Why? Will it visit every node? – Why? Will it visit any node more than once? – Why?
Making trees What kind of tree does this make? struct tree *makeTree(long levels) { if (levels <= 0) return NULL; struct tree *t = malloc (sizeof *t); t->value = random() % 1000; t->right = makeTree(levels - 1); t->left = makeTree(levels - 1); return t; }
Storing trees in files Can you meaningfully store a value in a file? – Yes, if it is char, int, long, char * Can you meaningfully store a pointer in a file? – Yes, but... – What does it mean when you read it back? long *p; readPtr(infile, swap, &p); printf(“*p is %d\n”, *p); – Stack, heap are (likely) at a different address now than when the pointer was written to the file – *(long *) random() = 23;
Swizzling “the conversion of references based on name or position to direct pointer references” [Wikipedia] – Performed when reading complex objects Unswizzling is the opposite – Performed when writing complex objects
Here’s a tree, draw it Each blue box represents 8 bytes Draw the tree with circles, values and arrows root 37 left (0x1248) right (0x1260) 0x1230 0x1238 0x left (0x1278) right (0x0) 0x1248 0x1250 0x left (0x0) right (0x1290) 0x1260 0x1268 0x left (0x0) right (0x0) 0x1278 0x1280 0x left (0x0) right (0x0) 0x1290 0x1298 0x12a0 0x1230
Circles and arrows
Don’t make assumptions Tree nodes won’t be consecutive in memory “Higher” nodes won’t have lower addresses root 37 left (0x1248) right (0x1260) 0x1230 0x1238 0x left (0x1278) right (0x0) 0x1248 0x1250 0x left (0x0) right (0x1290) 0x1260 0x1268 0x left (0x0) right (0x0) 0x1278 0x1280 0x left (0x0) right (0x0) 0x1290 0x1298 0x12a0 0x1230
A file representation Nodes have ids Pointers have been converted to ids 37 left (2) right (3) 25 left (4) right (0) 52 left (0) right (5) 13 left (0) right (0) 99 left (0) right (0)
Swizzling, how to convert? 37 left (2) right (3) 25 left (4) right (0) 52 left (0) right (5) 13 left (0) right (0) 99 left (0) right (0) left (0x1248) right (0x1260) 0x1230 0x1238 0x left (0x1278) right (0x0) 0x1248 0x1250 0x left (0x0) right (0x1290) 0x1260 0x1268 0x left (0x0) right (0x0) 0x1278 0x1280 0x left (0x0) right (0x0) 0x1290 0x1298 0x12a0
Basic idea - reading Repeat Read an id (x) Allocate space for the node, call its address p Read the fields of *p Remember the mapping x -> p Until there is no more data in the file When reading data, you may need to swap bytes
After reading 1 -> 0xa > 0xa > 0xa > 0xa > 0xa390 root 37 left (2) right (3) 0xa330 0xa338 0xa left (4) right (0) 0xa348 0xa350 0xa left (0) right (5) 0xa360 0xa368 0xa left (0) right (0) 0xa378 0xa380 0xa left (0) right (0) 0xa390 0xa398 0xa3a0 1 1
Basic idea - swizzling For every entry in the table ( x -> p ) if p->left is not 0 new = lookup p->left in table p->left = new if p->right is not 0 new = lookup p->right in table p->right = new root = p from the first entry in the table
After swizzling 1 -> 0xa > 0xa > 0xa > 0xa > 0xa390 root 37 left (0xa348) right (0xa360) 0xa330 0xa338 0xa left (0xa378) right (0x0) 0xa348 0xa350 0xa left (0x0) right (0xa390) 0xa360 0xa368 0xa left (0x0) right (0x0) 0xa378 0xa380 0xa left (0x0) right (0x0) 0xa390 0xa398 0xa3a0 0xa330
Necessary type evil new = lookup p->left in table p->left has type struct tree * table lookup expects an id (a long ) C casts convert between things – When the source and destination types of a cast are the same size (both 64 bits) and both “integer- like” the cast doesn’t modify any of the bits – (long)p->left