Ch10-GeneralizingListsAndTrees

advertisement
Problem Solving with Data
Structures using Java:
A Multimedia Approach
Chapter 10: Generalizing
Lists and Trees
Chapter Objectives
Story


What we were just doing with LLNode
What linked lists are good for.
• Using linked lists to represent structure and
behavior

What trees are good for.
• Using trees to represent structure and behavior
• Examples
• Equations
• Matching
• Unification
Removing Redundant Code


The DisplayableNode (for scene
graphs) and CollectableNode (for
sound trees) class hierarchies have lots
of the exact same code.
Can we put all this code in one place (an
abstract superclass), make it general,
but make it so that all the old code still
works?
Abstracting
LLNode
abstract LLNode
Knows next
Knows how to do all basic list
operations
CollectableNode

Knows just knows the part that
specializes LLNode for collectable
(sound-returning) nodes.
DrawableNode

Just specializes LLNode to represent
things that can draw with a turtle and
draw down their linked list with a turtle
(drawOn).
LLNode: The abstract definition
of what a linked list node is


Given LLNode, we can make anything
we want into a linked list.
Want a linked list of students?
• Subclass LLNode into StudentNode
• StudentNodes know names and ID numbers,
•
•
and know how to access and return these.
The linked list part is inherited from LLNode.
Every StudentNode knows next and knows
how to getNext, add, etc.
/**
* Class that represents a node in a linked list
* @author Mark Guzdial
* @author Barb Ericson
*/
public abstract class LLNode {
/** The next node in the list */
private LLNode next;
/**
* Constructor for LLNode that just sets
* next to null
*/
public LLNode() {
next = null;
}
/**
* Method to set the next element
* @param nextOne the element to set as next
*/
public void setNext(LLNode nextOne) {
this.next = nextOne;
}
/**
* Method to get the next element
* @return the next element in the linked list
*/
public LLNode getNext() {
return this.next;
}
/** Method to remove a node from the list, fixing
* the next links appropriately.
* @param node the element to remove from the list.
*/
public void remove(LLNode node) {
if (node==this) {
System.out.println("I can't remove myself from " +
"the head of the list");
return;
}
LLNode current = this;
// While there are more nodes to consider
while (current.getNext() != null) {
if (current.getNext() == node) {
// Simply make node's next be this next
current.setNext(node.getNext());
// Make this node point to nothing
node.setNext(null);
return;
}
current = current.getNext();
}
}
A node can’t remove itself
/**
* Insert the input node after this node.
* @param node element to insert after this.
*/
public void insertAfter(LLNode node) {
// Save what "this" currently points at
LLNode oldNext = this.getNext();
this.setNext(node);
node.setNext(oldNext);
}
/**
* Return the last element in the list
* @return the last element in the list
*/
public LLNode last() {
LLNode current;
current = this;
while (current.getNext() != null) {
current = current.getNext();
}
return current;
}
/**
* Return the number of the elements in the list
* @return the number of elements in the list
*/
public int count() {
LLNode current;
int count = 1;
current = this;
while (current.getNext() != null) {
count++;
current = current.getNext();
}
return count;
}
/**
* Add the passed node after the last node in this list.
* @param node the element to insert after this.
*/
public void add(LLNode node) {
this.last().insertAfter(node);
}
/**
* Reverse the list starting at this,
* and return the last element of the
list.
* The last element becomes the
FIRST element
* of the list, and THIS goes to null.
* @return the new head of the list
*/
public LLNode reverse() {
LLNode reversed, temp;
while (this.getNext() != null) {
temp = this.last();
this.remove(temp);
reversed.add(temp);
}
// Now put the head of the old list
on the end of
// the reversed list.
reversed.add(this);
// At this point, reversed
// is the head of the list
return reversed;
// Handle the first node outside
the loop
reversed = this.last();
this.remove(reversed);
}
}
Next step: Change DrawableNode
to extend LLNode
/**
* Stuff that all nodes and branches in the
* scene tree know.
* @author Mark Guzdial
* @author Barb Ericson
*/
public abstract class DrawableNode extends LLNode {
/**
* Constructor for DrawableNode
*/
public DrawableNode() {
super(); // call to parent constructor
}
/**
* Use the given turtle to draw oneself
* @param t the Turtle to draw with
*/
public abstract void drawWith(Turtle t);
// no body in an abstract method
/**
* Draw on the given picture
* @param bg the background picture to draw on
*/
public void drawOn(Picture bg) {
Turtle t = new Turtle(bg);
t.setPenDown(false);
this.drawWith(t);
}
}
Have to fix
branches
/**
* Ask all our children to draw,
* then tell the next element to draw
* @param turtle the Turtle to draw with
*/
public void drawWith(Turtle turtle) {
// start with the first child
DrawableNode current = this.getFirstChild();
// Have my children draw
while (current != null) {
getNext() returns an
LLNode, but we need it to
be a DrawableNode so
that we can drawWith().
current.drawWith(turtle);
turtle.moveTo(turtle.getXPos()+gap,turtle.getYPos());
current = (DrawableNode)
current.getNext();
}
// Have my next draw
if (this.getNext() != null) {
current = (DrawableNode) this.getNext();
current.drawWith(turtle);
}
}
Rewriting CollectableNode to
extend LLNode
/**
* Node in a sound tree.
* @author Mark Guzdial
* @author Barb Ericson
*/
public abstract class CollectableNode extends LLNode {
/**
* No argument constructor
*/
public CollectableNode() {
super(); // call to parent class constructor
}
/**
* Play the list of sound elements
* after me
*/
public void playFromMeOn() {
this.collect().play();
}
/**
* Collect all the sounds from me on
* @return the collected sound
*/
public abstract Sound collect();
}
All the linked list
methods are now
factored out.
Have to
cast
/**
* Collect all the sound from our firstChild,
* then collect from next.
* @return the combined sound
*/
public Sound collect() {
Sound childSound;
CollectableNode node;
if (firstChild != null) {
childSound = firstChild.collect();
}
else {
childSound = new Sound(1);
}
This error doesn’t
show up at compile
time, but it does at
runtime.
// Collect from my next
if (this.getNext() != null) {
Same error really,
similar fix.
node=(CollectableNode) this.getNext();
childSound=childSound.append(node.collect());
}
return childSound;
}
Now we have a generic
Linked List Node!


We can generate a new linked list easily,
by subclassing LLNode.
Let’s create a linked list of students.
/**
* Class that represents a student node
* in a linked list
* @author Mark Guzdial
* @author Barb Ericson
*/
public class StudentNode extends LLNode {
/** the student this node is keeping track off */
private Student myStudent;
/**
* Constructor that takes the student
* @param someStudent the student to store at this node
*/
public StudentNode(Student someStudent) {
super();
myStudent = someStudent;
}
/**
* Method to get the student stored at this node
* @return the student stored at this node
*/
public Student getStudent() {return myStudent;}
/**
* Method to get information about this node
* @return an information string
*/
public String toString() {
if (this.getNext() == null) {
return "StudentNode with student: " + myStudent;
}
else {
return "StudentNode with student: " + myStudent +
" and next: " + this.getNext();
}
}
/**
* Main method for testing
*/
public static void main(String[] args) {
Student student1 = new Student("Tanya Clark",1);
Student student2 = new Student("Tim O'Reilly",2);
Student student3 = new Student("Tesheika Mosely",3);
StudentNode node1 = new StudentNode(student1);
StudentNode node2 = new StudentNode(student2);
StudentNode node3 = new StudentNode(student3);
node1.setNext(node2);
node2.setNext(node3);
StudentNode node = (StudentNode) node1.getNext().getNext();
System.out.println(node.getStudent());
}
}
Why do people use linked lists?




Whenever you want dynamic size to the
list,
You may want the ordering to represent
something,
You want insertion and deletion to be
cheap and easy,
You are willing to make finding a
particular item slower.
Examples of Linked Lists





Order of layers in Visio or PowerPoint
Notes in a Phrase in JMusic
Video segments in non-linear video
editing.
Items in a toolbar.
Slides in a PowerPoint presentation.
But what are trees good for?

Trees represent hierarchical structure.
• When just representing ordering (linearity)
isn’t enough.

Trees can store operations (behavior),
as well as data.
• Linked lists can, too, but not as useful.
Examples of Trees



Representing how parts of music assemble to form a
whole.
Representing the elements of a scene.
•
Representing the inheritance relationships among
classes.
•




Scene graph
Class hierarchy
Files and directories on your hard disk.
Elements in an HTML page.
Organization chart
Political affiliations
Example Tree: Equation

It’s fairly easy to turn an
equation into a tree.
•
•


3+4*5
If you see an operation,
make a branch.
Otherwise, make a node.
The structure here
represents order of
operation.
Evaluating this tree is like
collecting() the sounds, but
collection involves
computation (behavior) at
the branches.
+
3
*
4
5
Example Tree: Taxonomies
(Keeping track of meaning)

Let’s say that you want to compare prices at
various websites for the same item.
•
•
•

At one site, they call it the “retail price”
At another, they call it the “customer’s cost”
How do you track that these are similar meanings?
It’s the same as knowing that a MoveBranch
is a kind of Branch is a kind of
DrawableNode!
Tree of Meanings
Here, the arrows mean
the same thing as in a
class hierarchy.
The thing below is a
specialization of the thing
above.
Artificial Intelligence (AI)
and Semantic Web
researchers really do
represent taxonomies in
just this way.
Price
Retail price
Customer
price
Customer
cost
Wholesale price
Business-tobusiness price
Algorithm for matching
1.
2.
3.
Traverse the tree to find phrase1.
Traverse the tree to find phrase2.
Do both have a common ancestor
(parent)?
 That’s the meaning in common!
4.
If not, need to extend the taxonomy.
Example tree: Unification


My cat ate a fat worm.
Sentence Diagrams
are trees.
Arrows don’t mean
the same things
here.
•
•
Some arrows are
saying instance-of
a category (like
“noun”)
Other arrows are
saying has-pieceswithin (like links
from “subject” and
“object”)
ate
verb
subject
object
noun
article
cat
noun
adjective
adjective
a
worm
My
fat
How do we search a collection
of sentence diagrams?




Imagine: You’re an expert with the
National Security Agency.
You have megabytes of captured
terrorist messages.
Using specialized Natural Language
Understanding (NLU) technology, you
have trees of all this text.
Now what do you do with it?
Compare trees


We can create a tree
describing the kind of
sentence that we want to
find.
We leave variables that can
be bound in the process of
the query.
•

Matching complex trees like
this is sometimes called
unification.
Not exactly the same word?
How do we determine if it’s
close enough? See
previous slides…
attack
verb
subject
noun
object
noun
<suspect>
<location in United States>
Trees in User Interfaces


Any user interface
is actually
composed of a
tree!
Windows hold
panes hold buttons
and text areas.
window
pane
pane
button
text area
button
button
Binary Trees

Binary trees have at most
two children per branch.
•

Any node can be a branch.
Binary search trees are
binary trees that are wellstructured.
Binary Trees


Binary trees can
represent any kind
of tree.
Lots of interesting
properties.
• The minimum
number of levels of n
nodes in a binary
tree is log2(n)+1
Binary Search Trees

Binary search trees are
particularly fast to search.
•
•

Lists are always O(n) to
search
Well-structured trees are
O(log2 n) to search.
Rule: For each data
node
•
•
bear
Items to left are “less than”
data in this node.
Items to right are “greater
than” data in this node.
apple
ant
cash
ark
card
cat
Implementing Binary Trees
/**
* Class that represents a binary tree node
* @author Mark Guzdial
* @author Barb Ericson
*/
public class TreeNode {
/** the data stored at this node */
private String data;
/** the left child */
private TreeNode left;
/** the right child */
private TreeNode right;
Constructing a new node
/**
* Constructor that takes the string
* to store
* @param something the string to store
*/
public TreeNode(String something) {
data = something;
left = null;
right = null;
}
Any node can
be the root of
a tree
Printing a tree, recursively
// Skipping getters and setters – you know what those look like
/**
* Method to return a string of information
* @return the information string
*/
public String toString() {
return
"This: " + this.getData()+
" Left: " + this.getLeft() +
" Right: " + this.getRight();
}
}
Testing our TreeNode
> TreeNode node1 = new TreeNode("George");
> node1 // with no ending ';' it is like a
System.out.println(node);
This: George Left: null Right: null
> TreeNode node1b = new TreeNode("Alicia");
> node1.setLeft(node1b);
> node1
This: George Left: This: Alicia Left: null Right: null
Right: null
Implementing insert for
binary search trees


We’ll use strings
as data.
To compare them,
use compareTo
/**
* Method to add a new tree node in the tree
* @param newOne the node to add
*/
public void insert(TreeNode newOne) {
/* if the data at this node is greater than the
* data in the passed node
*/
if (this.data.compareTo(newOne.data) > 0) {
// and no left child then add this as the left child
if (this.getLeft() == null) {
this.setLeft(newOne);
}
// else insert it into the left subtree
else {
this.getLeft().insert(newOne);
}
}
// must be great than or equal
else {
// if no right child use this as the right child
if (this.getRight() == null) {
this.setRight(newOne);
}
// else insert into the right subtree
else {
this.getRight().insert(newOne);
}
}
Testing:
> TreeNode node1 = new TreeNode("Shilpa");
> TreeNode node2 = new TreeNode("Sam");
> TreeNode node3 = new TreeNode("Tina");
> TreeNode node4 = new TreeNode("Zach");
> System.out.println(node1);
This: Shilpa Left: null Right: null
> node1.insert(node2);
> System.out.println(node1);
This: Shilpa Left: This: Sam Left: null Right: null Right: null
> node1.insert(node3);
> System.out.println(node1);
This: Shilpa Left: This: Sam Left: null Right: null Right: This:
Tina Left: null Right: null
> node1.insert(node4);
> System.out.println(node1);
This: Shilpa Left: This: Sam Left: null Right: null Right: This:
Tina Left: null Right: This: Zach Left: null Right: null
Finding in a binary search tree
/**
* Method to find the passed someValue in
* the tree and return the node or return null
* if it isn't found in the tree
* @param someValue the value to find
*/
public TreeNode find(String someValue) {
// if we found the value return the node
if (this.getData().compareTo(someValue) == 0) {
return this;
}
/* if the data in the current node is greater than
* the value */
if (this.data.compareTo(someValue) > 0) {
// if no left child return null (not found)
if (this.getLeft() == null) {
return null;
}
// else look in the left subtree
else {
return this.getLeft().find(someValue);
}
}
/* the data in the current node is less than the
value */
else {
// if no right child then not found
if (this.getRight() == null) {
return null;
}
// look in the right subtree
else {
return this.getRight().find(someValue);
}
}
}
Testing the search on example
tree
Balancing a tree



Both these trees
are well-ordered
as search trees.
But balanced trees
lead to O(log2n)
search times.
We balance by
rotating subtrees
Original,
unbalanced
Rotating
right
branch of
Sam
Printing out the elements in
alphabetical order


Our default
toString() goes as
left as possible
before doing any
right branches.
How do we print
out everything in
the binary search
tree in order?
/**
* Method to do an inorder traversal of the tree
* @return a string with the data values in it
*/
public String traverse() {
String returnValue = "";
// Visit left
if (this.getLeft() != null) {
returnValue += " " + this.getLeft().traverse();
}
// Visit me
returnValue += " " + this.getData();
// Visit right
if (this.getRight() != null) {
returnValue += " " + this.getRight().traverse();
}
return returnValue;
}
In-Order Traversal of our
example tree
> node1.traverse()
Sam Shilpa Tina Zach
Pre-order vs. In-Order


If equations were trees 
toString() does a pre-order
traversal.
•


+*34*xy
traverse() does in-order
•
3*4+x*y
Post-order gives the
RPN version
•
yx*43*+
Using trees as lists
/**
* Method to add the newOne node as
* the first node in the tree (treating
* the tree like a list
* @param newOne the new node to add
*/
public void addFirst(TreeNode newOne) {
if (this.getLeft() == null) {
this.setLeft(newOne);
}
else {
this.getLeft().addFirst(newOne);
}
}
/**
* Method to add the newNode as the last
* node in a list (treating the tree like a list)
* @param newOne the node to add
*/
public void addLast(TreeNode newOne) {
if (this.getRight() == null) {
this.setRight(newOne);
}
else {
this.getRight().addLast(newOne);
}
Example use:
}
> TreeNode node1 = new TreeNode("the");
> node1.addFirst(new TreeNode("George of"));
> node1.addLast(new TreeNode("jungle"));
> node1.traverse()
" George of the jungle"
Download