CS 181 - Data Structures
Static data structures = fixed
length
Dynamic data structures = grow and
shrink as needed during execution
We will implement data structures
using classes, inheritance,
composition
Self-Referencing Classes
Self-referencing class = one or more
data members are object of that
class
class ListNode
{
Object data;
ListNode next;
...
}
ListNode next usually referred to as
"link" or "pointer" ... can be
"next" in sense of sorted order,
chronological order, priority, etc.
Self-referencing objects can be used
for List, Stack, Queue, Tree
If link is null, no object follows
Dynamic Memory Allocation
Dynamic memory allocation
necessary for dynamic data
structures
Obtain needed memory, release
unneeded memory during execution
Java operator "new" does dynamic
memory allocation
ListNode work =
new ListNode (data, next);
Linked List
List is Abstract Data Type used in many CS applications
List is collection of nodes each with link to next one
List has beginning link to first node
List has last node with "null" link
Insertions and deletions can be made
anywhere in List
List is good choice when no idea
about size of data structure or if
may vary wildly during execution
class Vector is Java's version of
dynamic Array
Tradeoff: List = dynamic; Array = no
overhead for dynamically allocating
memory, updating links
Operations:
insertAtFront -- put node at front of List
insertAtBack -- put node at end of List
removeFromFront -- remove node from front of List
removeFromBack -- remove node from end of List
isEmpty -- ask if List is empty
Alternative operations:
print -- list contents of List in order
insertAtMiddle -- put node somewhere in List
removeFromMiddle -- remove node from somewhere in List
reportFront -- read node from front of List
class ListNode
{
Object data;
ListNode next;
ListNode( Object o,
ListNode nextNode )
{
data = o;
next = nextNode;
}
ListNode( Object o )
{ this( o, null ); }
Object getObject() { return data; }
ListNode getnext() { return next; }
}
public class List
{
ListNode firstNode;
ListNode lastNode;
String name;
public List( String s )
{
name = s;
firstNode = lastNode = null;
}
public List() { this( "list" ); }
public void insertAtFront(
Object insertItem )
{
if ( isEmpty() )
firstNode = lastNode =
new ListNode( insertItem );
else
firstNode = new ListNode(
insertItem, firstNode );
}
public void insertAtBack(
Object insertItem )
{
if ( isEmpty() )
firstNode = lastNode =
new ListNode( insertItem );
else
lastNode = lastNode.next = new
ListNode( insertItem );
}
public Object removeFromFront()
throws EmptyListException
{
Object removeItem = null;
if ( isEmpty() ) throw
new EmptyListException( name );
removeItem = firstNode.data;
if ( firstNode.equals( lastNode ))
firstNode = lastNode = null;
else
firstNode = firstNode.next;
return removeItem;
}
public Object removeFromBack()
throws EmptyListException
{
Object removeItem = null;
if ( isEmpty() ) throw
new EmptyListException( name );
removeItem = lastNode.data;
if ( firstNode.equals( lastNode ))
firstNode = lastNode = null;
else {
ListNode current = firstNode;
while (current.next !=lastNode)
current = current.next;
lastNode = current;
current.next = null;
}
return removeItem;
}
public boolean isEmpty()
{ return firstNode == null; }
public void print()
{
if ( isEmpty() )
{
System.out.println( "Empty " +
name );
return;
}
System.out.print( "The " + name +
" is: " );
ListNode current = firstNode;
while ( current != null )
{
System.out.print(
current.data.toString() + " ");
current = current.next;
}
System.out.println( "\n" );
}
}
public class EmptyListException
extends RuntimeException
{
public EmptyListException( String
name )
{
super("The " + name +
" is empty");
}
}
public class ListTest
{
public static void main()
{
List objList = new List();
Boolean b = new Boolean( true );
Character c = new Character('$');
Integer i = new Integer( 34567 );
String s = new String( "hello" );
objList.insertAtFront( b );
objList.print();
objList.insertAtFront( c );
objList.print();
objList.insertAtBack( i );
objList.print();
objList.insertAtBack( s );
objList.print();
Object removedObj;
try
{
removedObj =
objList.removeFromFront();
System.out.println(
removedObj.toString() +
" removed" );
objList.print();
removedObj =
objList.removeFromFront();
System.out.println(
removedObj.toString() +
" removed" );
objList.print();
removedObj =
objList.removeFromBack();
System.out.println(
removedObj.toString() +
" removed" );
objList.print();
removedObj =
objList.removeFromBack();
System.out.println(
removedObj.toString() +
" removed" );
objList.print();
}
catch ( EmptyListException e )
{
System.err.println( "\n" +
e.toString() );
}
}
}
Object class allows any kind of node
in List
Each node should be object of class
that has a toString() method
EmptyListException good way to
handle that situation
Stack
Stack is Abstract Data Type used in many CS applications
Stack is like cafeteria trays
Last-In, First-Out (LIFO)
Stack = insertions and deletions can
be made only at top of Stack
Operations:
push -- put node on top of Stack
pop -- remove node from top of Stack
isEmpty -- ask if Stack is empty
Alternative operations:
print -- list contents of Stack in order
top -- read node on top of Stack
Stack is constrained form of List
Implement Stack through List inheritance
public class Stack extends List
{
public Stack() { super( "stack" );}
public void push( Object o )
{ insertAtFront( o ); }
public Object pop()
throws EmptyListException
{ return removeFromFront(); }
public boolean isEmpty()
{ return super.isEmpty(); }
public void print(){super.print();}
}
public class StackTest
{
public static void main()
{
Stack objStack = new Stack();
Boolean b = new Boolean( true );
Character c = new Character('$');
Integer i = new Integer( 34567 );
String s = new String( "hello" );
objStack.push( b );
objStack.print();
objStack.push( c );
objStack.print();
objStack.push( i );
objStack.print();
objStack.push( s );
objStack.print();
Object removedObj = null;
try
{
while ( true )
{
removedObj = objStack.pop();
System.out.println(
removedObj.toString() +
" popped" );
objStack.print();
}
}
catch ( EmptyListException e )
{
System.err.println( "\n" +
e.toString() );
}
}
}
List contains other methods
(insertAtBack, removeFromBack) that
could be used...
objStack.insertAtBack (a);
Sometimes disadvantage of
implementing through inheritance
Implement Stack through List composition
public class Stack
{
List s;
public Stack()
{ s = new List( "stack" ); }
public void push( Object o )
{ s.insertAtFront( o ); }
public Object pop()
throws EmptyListException
{ return s.removeFromFront(); }
public boolean isEmpty()
{ return s.isEmpty(); }
public void print() { s.print(); }
}
Composition hides methods of List
that we do not want
objStack.insertAtBack (a);
...now is compile-time error
Queue
Queue is Abstract Data Type used in many CS applications
Queue is like ticket line
Queue = insertions made at back
(tail) and deletions made at front
(head) of queue
First-In, First-Out (FIFO)
Operations:
enqueue -- put node at rear of queue
dequeue -- remove node from front of queue
isEmpty -- ask if queue is empty
Alternative operations:
print -- list contents of Queue in order
front -- read node from front of queue
public class Queue
{
List q;
public Queue()
{ q = new List( "queue" ); }
public void enqueue( Object o )
{ q.insertAtBack( o ); }
public Object dequeue()
throws EmptyListException
{ return q.removeFromFront(); }
public boolean isEmpty()
{ return q.isEmpty(); }
public void print() { q.print(); }
}
Tree
Tree = data structure with links to
one or more sub-trees
Binary tree = tree with links to at
most two sub-trees
Refer to sub-trees as left child and
right child
Node with no children is leaf node
Binary tree can make searching and
sorting more efficient
Binary search tree = nodes in left
sub-tree "less than" this node and
nodes in right sub-tree "greater
than" this node
class TreeNode
{
TreeNode left;
int data;
TreeNode right;
public TreeNode( int d )
{
data = d;
left = right = null;
}
public void insert( int d )
{
if ( d < data )
{
if ( left == null )
left = new TreeNode( d );
else
left.insert( d );
}
else if ( d > data )
{
if ( right == null )
right = new TreeNode( d );
else
right.insert( d );
}
}
}
public class Tree
{
TreeNode root;
public Tree() { root = null; }
public void insertNode( int d )
{
if ( root == null )
root = new TreeNode( d );
else
root.insert( d );
}
public void inorderTraversal()
{ inorderHelper( root ); }
private void inorderHelper(
TreeNode node )
{
if ( node == null )
return;
inorderHelper( node.left );
System.out.print(node.data + " ");
inorderHelper( node.right );
}
}
Binary search tree facilitates
duplicate elimination
Tightly packed binary tree = each
level has about twice as many nodes
as previous level
More Complex Data Structures
Doubly-linked lists = usually next
and previous links
Multiply-linked lists = each node is
on several lists simultaneously