200 likes | 378 Views
Trees and Expressions. Eric Roberts CS 106B May 11, 2009. Trees. In the textbook, the first example I use to illustrate tree structures is the royal family tree of the House of Normandy:. This example is useful for defining terminology: William I is the root of the tree.
E N D
Trees and Expressions Eric Roberts CS 106B May 11, 2009
Trees • In the textbook, the first example I use to illustrate tree structures is the royal family tree of the House of Normandy: • This example is useful for defining terminology: • William I is the root of the tree. • Adela is a child of William I and the parent of Stephen. • Robert, William II, Adela, and Henry I are siblings. • Henry II is a descendant of William I, Henry I, and Matilda. • William I is an ancestor of everyone else.
Family Trees • Trees of the English royal family can get very complicated. The tree at the right, for example, shows the royal lineage through the time of the War of the Roses encompassing the Houses of Plantagenet, York, and Lancaster. • A tree diagram of this sort is very helpful, for example, if you want to understand Shakespeare’s history plays.
Family Trees • In looking for family tree diagrams on the web, I found lots of fascinating examples. • In the category of royal family trees, one of the more interesting is this tree of the royal family of Bhutan. • In Bhutan, the constitution now puts Gross National Happiness ahead of Gross National Product.
Family Trees • There was also a Biblical family tree from Adam to Moses:
Family Trees • And one of my all time favorites from J. R. R. Tolkien:
Trees Are Everywhere • But family trees are by no means the only kind. In this year when we celebrate the bicentennial of Darwin’s birth, it is useful to remember the Tree of Life, as shown in this diagram from Notebook B: Charles Darwin (around the time of Voyage of the Beagle)
Trees Are Everywhere • The tree structure of evolution is even more clearly shown in Ernst Haeckel’s diagram of the tree of life from 1866. • The details of evolutionary tree diagrams have changed markedly over the last 150 years, but their general structure—starting with a single root and branching to form new lineages—remains constant.
Representing Family Trees • The first step in writing programs that work with trees is to design a suitable data structure. • In diagrammatic form, the goal is to transform a tree diagram into an internal representation that looks like this:
typedef nodeT *treeT; A Recursive Definition for Trees • When you think about trees with an eye toward representing their internal structure, the following definition is extremely useful: • A tree is a pointer to a node. • A node is a structure that contains some number of trees. • Although this definition is clearly circular, it is not necessarily infinite. • In most applications involving trees, clients will work far more frequently with the tree than with the underlying node. For this reason, I usually name the pointer type using the typedef facility in C++. The typical pattern looks like this:
The famtree.h Interface /* * File: famtree.h * --------------- * This file is an interface to a simple class that represents * an individual person in a family tree. */ #ifndef _famtree_h #define _famtree_h #include "genlib.h" #include "vector.h" /* * Type: familyTreeT * ----------------- * This type gives a name to the pointer type that points to a * FamilyTreeNode. Clients are far more likely to refer to the * pointer type than to the underlying node type. */ class FamilyTreeNode; typedef FamilyTreeNode *familyTreeT;
/* * Class: FamilyTreeNode * --------------------- * This class defines the structure of an individual in the family * tree, which consists of a name and a vector of children. */ class FamilyTreeNode { public: /* * Constructor: FamilyTreeNode * Usage: familyTreeT person = new FamilyTreeNode(name); * ------------------------------------------------- * This function constructs a new FamilyTreeNode with the specified * name. The newly constructed entry has no children, but clients * can add children by calling the addChild method. */ FamilyTreeNode(string name); The famtree.h Interface /* * File: famtree.h * --------------- * This file is an interface to a simple class that represents * an individual person in a family tree. */ #ifndef _famtree_h #define _famtree_h #include "genlib.h" #include "vector.h" /* * Type: familyTreeT * ----------------- * This type gives a name to the pointer type that points to a * FamilyTreeNode. Clients are far more likely to refer to the * pointer type than to the underlying node type. */ class FamilyTreeNode; typedef FamilyTreeNode *familyTreeT;
/* * Method: getName * Usage: string name = person->getName(); * --------------------------------------- * Returns the name of the person. */ string getName(); /* * Method: addChild * Usage: person->addChild(child); * ------------------------------- * Adds child to the end of the list of children for person, and * makes person the parent of child. */ void addChild(familyTreeT child); The famtree.h Interface /* * Class: FamilyTreeNode * --------------------- * This class defines the structure of an individual in the family * tree, which consists of a name and a vector of children. */ class FamilyTreeNode { public: /* * Constructor: FamilyTreeNode * Usage: familyTreeT person = new FamilyTreeNode(name); * ------------------------------------------------- * This function constructs a new FamilyTreeNode with the specified * name. The newly constructed entry has no children, but clients * can add children by calling the addChild method. */ FamilyTreeNode(string name);
/* * Method: getParent * Usage: familyTreeT parent = person->getParent(); * ------------------------------------------------ * Returns the parent of the specified person. */ familyTreeT getParent(); /* * Method: getChildren * Usage: Vector<familyTreeT> children = person->getChildren(); * ------------------------------------------------------------ * Returns a vector of the children of the specified person. * Note that this vector is a copy of the one in the node, so * that the client cannot change the tree by adding or removing * children from this vector. */ Vector<familyTreeT> getChildren(); private: string name; familyTreeT parent; Vector<familyTreeT> children; }; #endif The famtree.h Interface /* * Method: getName * Usage: string name = person->getName(); * --------------------------------------- * Returns the name of the person. */ string getName(); /* * Method: addChild * Usage: person->addChild(child); * ------------------------------- * Adds child to the end of the list of children for person, and * makes person the parent of child. */ void addChild(familyTreeT child);
The famtree.cpp Implementation /* * File: famtree.cpp * ----------------- * This program implements the famtree.h interface. */ #include "genlib.h" #include "vector.h" #include "famtree.h" FamilyTreeNode::FamilyTreeNode(string name) { this->name = name; this->parent = NULL; } string FamilyTreeNode::getName() { return name; }
void FamilyTreeNode::addChild(familyTreeT child) { children.add(child); child->parent = this; } familyTreeT FamilyTreeNode::getParent() { return parent; } Vector<familyTreeT > FamilyTreeNode::getChildren() { return children; } The famtree.cpp Implementation /* * File: famtree.cpp * ----------------- * This program implements the famtree.h interface. */ #include "genlib.h" #include "vector.h" #include "famtree.h" FamilyTreeNode::FamilyTreeNode(string name) { this->name = name; this->parent = NULL; } string FamilyTreeNode::getName() { return name; }
E constant E identifier E E op E E ( E ) Recursive Structure of Expressions • An arithmetic expression in a programming language is a recursive structure that can be thought of as a tree. • In the simple model I use in Chapter 14, every expression falls into one of the following forms: • An integer constant • A variable name that holds an integer value • Two expressions joined by an operator • An expression enclosed in parentheses • This structure can be represented in the form of a grammar.
Expression Trees • When the C++ compiler looks at an expression, it needs to understand what the expression means by translating it into an internal form. This process generally consists of two steps: • Lexical analysis, in which the line is broken up into units • Parsing, in which the tokens are assembled into a tree that embodies the expression structure