Chapter 20
Advanced Data Structures
Chapter Goals
- To learn about set and map data types
- To understand the implementation of hash tables
- To be able to program hash functions
- To learn about binary trees
- To be able to use tree sets and tree maps
Sets
- A set is an unordered collection of distinct elements
- Sets don't have duplicates
- Trying to add a duplicate has no effect
Fundamental Operations on a Set
- Adding an element
- Removing an element
- Containment testing (Does the set contain a given object?)
- Listing all elements ( in arbitrary order)
Sets
- Classes that realize the Set interface
Iterator
- Use an iterator to visit all elements in a set
- A set iterator does not visit the elements in the order in which they were
inserted
- An element can not be added to a set at an iterator position
- A set element can be removed at an iterator position
Set Classes and Interface in the Standard Library
Code for Creating and Using a HashSet
-
//Creating a hash set
Set names = new HashSet();
-
//Adding an element
names.add("Romeo");
-
//Removing an element
names.remove("Juliet");
-
//Is element in set
if (names.contains("Juliet") {. . .}
Listing All Elements with an Iterator
Iterator iter = names.iterator();
while (iter.hasNext())
{
String name = (String) iter.next();
//do something here
}
File SetTest.java
Maps
- A map keeps associations between key and value objects
- Every key in a map has a unique value.
- A value may be associated with several keys
Maps
- Classes that realize the Map interface
An Example of a Map

Code for Creating and Using a HashMap
-
//Creating a HashMap
Map favoriteColors = new HashMap();
-
//Adding an association
favoriteColors.put("Juliet", Color.pink);
-
//Changing an existing association
favoriteColor.put("Juliet",Color.red);
Code for Creating and Using a HashMap
Printing key/value Pairs
Set keySet = favoriteColors.keySet();
Iterator iter = keySet.iterator();
while (iter.hasNext())
{
Object key = iter.next();
Object value = favoriteColors.getkey();
System.out.println(key + "->" + value);
}
Map Classes and Interface in the Standard Library

File MapTest.java
Hash Tables
- Hashing can be used to find elements in a data structure quickly without
making a linear search
- A hash function computes an integer value (called the hash code) from an
object
- A good hash function minimizes collisions, identical hash codes for
different objects
- To compute the hash code of object x: int h = x.hashcode()
Sample Strings and Their Hash Codes
Simplistic Implementation of a Hash Table
- To implement
- Generate hash codes for objects
- Make an array
- Insert each object at the location of its hash code
- To test if an object is contained in the set
- Compute its hash code
- Check if the array position with that hash code is already occupied
Simplistic Implementation of a Hash Table
Problems with Simplistic Implementation
- It is not possible to allocate an array that is large enough to hold all
possible integer index positions
- It is possible for two different objects to have the same hash code
Solutions
Hash Table with Linked Lists to Store Elements with Same Hash Code

Algorithm for Finding an Object x in a Hash Table
- Get the index h into the hash table
- Compute the hash code.
- Reduce it modulo the table size
- Iterate through the elements of the bucket at position h
- For each element of the bucket, check whether it is equal to x
- If a match is found among the elements of that bucket, then x is in the
set.
- Otherwise, x is not in the set
Hash Tables
- A hash table can be implemented as an array of buckets
- Buckets are sequences of links that hold elements with the same hash code
- The table size should be a prime number larger than the expected number
of elements
- If there are few collisions, then adding, locating, and removing hash table
elements takes constant time
- Big-Oh notation: O(1)
File HashSet.java
File SetTest.java
Hash Function
- A hash function computes an integer hash code from an object
- Choose a hash function so that different objects are likely to have different
hash codes.
- Bad choice for hash function for a string
- Adding the unicode values of the characters in the string
- Because permutations ("eat" and "tea") would have the same hash code
Computing Hash Codes
A hashCode method for the Coin class
- There are two instance fields: String coin name and double coin value
- Use String's hashCode method to get a hash code for the name
- To compute a hash code for a floating-point number:
- Wrap the number into a Double object
- Then use Double's hashCode method
- Combine the two hash codes using a prime number as the HASH_MULTIPLIER
A hashCode method for the Coin class
class Coin
{
public int hashCode()
{
int h1 = name.hashCode();
int h2 = new Double(value).hashCode();
final int HASH_MULTIPLIER = 29;
int h = HASH_MULTIPLIER * h1 + h2:
return h
}
. . .
}
Creating Hash Codes for your classes
Creating Hash Codes for your classes
HashMap
-
In a hash map, only the keys are hashed
- The keys need compatible hashCode
and equals method
File Coin.java
File HashCodeTest.java
Binary Search Trees
- Binary search trees allow for fast insertion and removal of elements
- A binary tree consists of two nodes, each of which has two child nodes
- All nodes in a binary search tree fulfill the property that:
- Descendants to the left have smaller data values than the node data
value
- Descendants to the right have larger data values than the node data
value
A Binary Search Tree

A Binary Tree That Is Not a Binary Search Tree

Implementing Tree Classes
- Implement a class for the tree containing a reference to the root node
- Implement a class for the nodes
- A node contains two references ( to left and right child nodes)
- A node contains a data field
- The data field has type Comparable, so that you can compare the values
in order to place them in the correct position in the binary search tree
Public interface for Tree and Node classes
class Tree
{
public Tree() { . . . }
public void insert(Comparable obj) { . . . }
public void print() { . . . }
private Node root;
private class Node
{
public void insertNode(Node newNode) { . . . }
public void printNodes() { . . . }
public Comparable data;
public Node left;
public Node right;
}
}
Insertion Algorithm
- If you encounter a non-null pointer, look at its data value
- If the data value of that node is larger than the one you want to insert,
continue the process with the left subtree
- If the existing data is smaller, continue the process with the right
subtree
- If you encounter a null node pointer, replace it with the new node
Insertion Algorithm - Tree class
class Tree
{
. . .
public void insert(Comparable obj)
{
Node newNode = new Node();
newNode.data = obj;
newNode.left = null;
newNode.right = null;
if (root == null) root = newNode;
else root.insertNode(newNode);
}
. . .
}
Insertion Algorithm - Node class
private class Node
{
public void insertNode(Node newNode)
{
if (newNode.data.compareTo(data) < 0)
{
if (left == null) left = newNode;
else left.insertNode(newNode);
}
else
{
if (right == null) right = newNode;
else right.insertNode(newNode);
}
}
. . .
}
Binary Search Tree After Four Insertions
The tree before the insertion of node with data "Romeo"

Binary Search Tree After Five Insertions
The tree after the insertion of node with data "Romeo"

Algorithm for Printing Elements in Sorted Order
- Print the left subtree
- Print the data
- Print the right subtree
Tree Class Method to Implement Printing in Sorted Order
class Tree
{
public void print()
{
if (root != null)
root.printNodes();
}
. . .
}
Node Class Method to Implement Printing in Sorted Order
private class Node
{
public void printNodes()
{
if (left != null)
left.printNodes();
System.out.println(data);
if (right != null)
right.printNodes();
}
. . .
}
Binary Search Tree
- There is no insertion position
- You can not select the position at which to insert an element
- The structure is self-organizing
- Each element finds its own place
- If it is approximately balanced, then adding an element takes O(log(n))
time
File TreeTest.java
File Tree.java
TreeSet and HashSet
- Both implement the Set interface
- With a good hash function, hashing is generally faster than tree-based algorithms
- TreeSet's balanced tree guarantees reasonable performance
- TreeSet's iterator visits the elements in sorted order rather than the HashSet's
random order
To use a TreeSet
- Either your objects must implement Comparable interface
- Or you must provide a Comparator object
To use a TreeMap
- Either the keys must implement the Comparable interface
- Or you must provide a Comparator object for the keys
- There is no requirement for the values
File TreeSetTest.java
This program constructs a TreeSet of Coins using the CoinComparator object