Download Notes

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Lattice model (finance) wikipedia , lookup

Red–black tree wikipedia , lookup

Quadtree wikipedia , lookup

B-tree wikipedia , lookup

Linked list wikipedia , lookup

Interval tree wikipedia , lookup

Binary tree wikipedia , lookup

Binary search tree wikipedia , lookup

Transcript
Week 9: Data Structures
(Horstmann ch.20, Deitel&Deitel ch.22)
Until now we have studied only fixed-size data structures such as arrays. This chapter introduces
linked lists - dynamic data structures that grow and shrink at execution time. We also introduce
stacks, queues and trees, data structures which have numerous interesting applications. For each
data structure we shall discuss how to create and manipulate them using references manipulations.
Self-Referential Classes
These classes contain a reference member that refers to an object of the same class type. For
instance the following code defines a type called Node:
class Node {
private int data;
private Node next;
public Node(int d);
public void setData(int d);
public int getData();
public void setNext(Node nextNode);
public Node getNext();
}
Self-referential objects can be linked together to form the data structures already mentioned.
The following diagram illustrates two such objects linked together in a list.
15
10
The last box symbolizes a null reference and is always at the end of the data structure.
Dynamic Memory Allocation
Creating dynamic data structures requires dynamic memory allocation. New nodes will require new
memory and deleting nodes will release space through automatic garbage collection. The limit for
dynamic memory is the size of the principal memory minus the memory used by the operating
system and by other users or tasks. To create a new Node object one uses the line:
Node nodeToAdd = new Node(10);
Linked Lists
The program below uses a class called List to manipulate various object types. The main method
of the class ListTest creates a list of objects, inserts objects at the front or at the end of the list and
then removes them both from the front and from the back. After each insert or delete the content of
the list is displayed. If an attempt is made to remove an item from an empty list an
EmptyListException is thrown.
class EmptyListException extends RuntimeException {
public EmptyListException( String name ) {
super( "The " + name + " is empty" );
}
}
class ListNode {
Object data;
ListNode next;
ListNode( Object o ) { this( o, null ); }
ListNode( Object o, ListNode nextNode ) {
data = o;
next = nextNode;
}
Object getObject() { return data; }
ListNode getNext() { return next; }
}
class List {
private ListNode firstNode;
private ListNode lastNode;
private String name; // String like "list" used in printing
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; // retrieve the 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; // retrieve the data
if ( firstNode.equals( lastNode ) )
firstNode = lastNode = null;
else {
ListNode current = firstNode;
while ( current.next != lastNode ) // not last node
current = current.next;
// move to next node
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 ListTest {
public static void main( String args[] ) {
List objList = new List(); // create the List container
Boolean b = Boolean.TRUE;
Character c = new Character( '$' );
Integer i = new Integer( 34567 );
String s = "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() );
}
}
}
Output:
The list is: true
The list is: $ true
The list is: $ true 34567
The list is: $ true 34567 hello
$ removed
The list is: true 34567 hello
true removed
The list is: 34567 hello
hello removed
The list is: 34567
34567 removed
Empty list
Stacks
A stack is a constrained version of a linked list: new nodes can be added or removed from the stack
only at the top. The method to add is called push, while the method to remove the top element is
called pop. A stack is refered to as a last-in, first-out (LIFO) data structure.
class StackInheritance extends List {
public StackInheritance() { 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 StackInheritanceTest {
public static void main( String args[] ) {
StackInheritance objStack = new StackInheritance();
Boolean b = Boolean.TRUE;
Character c = new Character( '$' );
Integer i = new Integer( 34567 );
String s = "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() );
}
}
}
Output:
The stack is: true
The stack is: $ true
The stack is: 34567 $ true
The stack is: hello 34567 $ true
hello popped
The stack is: 34567 $ true
34567 popped
The stack is: $ true
$ popped
The stack is: true
true popped
Empty stack
Queues
Another common data structure is the queue. A queue is similar to a checkout line in a
supermarket: the first person in line is served and leaves, while a new person joins the line at the
end. Similarly, queue nodes are removed only from the head of the queue and are inserted only at
the tail of the queue. For this reason a queue is referred to as first-in, first-out (FIFO) data structure.
The insert and remove operations are known as enqueue and dequeue.
class StackComposition {
private List s;
public StackComposition() { 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(); }
}
public class StackCompositionTest {
public static void main( String args[] ) {
StackComposition objStack = new StackComposition();
Boolean b = Boolean.TRUE;
Character c = new Character( '$' );
Integer i = new Integer( 34567 );
String s = "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() );
}
}
}
Output:
The queue is: true
The queue is: true $
The queue is: true $ 34567
The queue is: true $ 34567 hello
true dequeued
The queue is: $ 34567 hello
$ dequeued
The queue is: 34567 hello
34567 dequeued
The queue is: hello
hello dequeued
Empty queue
Trees
Trees are non-linear data structures. Tree nodes contain two or more links. In the case of binary
trees each node has two links, which refer to two children. If one of the children is missing the
corresponding root reference will be null.
The following program instantiates an empty Tree object and then it generates 10 random integers
which are inserted into the Tree object. The program then performs preorder, inorder and postorder
traversals of the tree. The steps for the inorder traversal are:
1) traverse the left subtree with a call to inorderHelper
2) process the value in the node (i.e. print)
3) traverse the right subtree with call to inorderHelper
Note that the inorder traversal of a binary search tree prints the node values in ascending order.
This is because the process of creating a binary search tree actually sorts the data.
Also, note that the insert method of the TreeNode class eliminates (i.e. does not insert) duplicates.
class TreeNode {
TreeNode left; // left node
int data;
// data item
TreeNode right; // right node
public TreeNode( int d ) {
data = d;
left = right = null; // this node has no children
}
// Insert a TreeNode into a Tree that contains nodes. Ignore duplicate values.
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 {
private TreeNode root;
public Tree() { root = null; }
public void insertNode( int d ) {
if ( root == null )
root = new TreeNode( d );
else
root.insert( d );
}
public void preorderTraversal() { preorderHelper( root ); }
private void preorderHelper( TreeNode node ) {
if ( node == null )
return;
System.out.print( node.data + " " );
preorderHelper( node.left );
preorderHelper( node.right );
}
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 );
}
public void postorderTraversal() { postorderHelper( root ); }
private void postorderHelper( TreeNode node ) {
if ( node == null )
return;
postorderHelper( node.left );
postorderHelper( node.right );
System.out.print( node.data + " " );
}
}
public class TreeTest {
public static void main( String args[] ) {
Tree tree = new Tree();
int intVal;
System.out.println( "Inserting the following values: " );
for ( int i = 1; i <= 10; i++ ) {
intVal = ( int ) ( Math.random() * 100 );
System.out.print( intVal + " " );
tree.insertNode( intVal );
}
System.out.println ( "\n\nPreorder traversal" );
tree.preorderTraversal();
System.out.println ( "\n\nInorder traversal" );
tree.inorderTraversal();
System.out.println ( "\n\nPostorder traversal" );
tree.postorderTraversal();
System.out.println();
}
}
Possible output (Note: Math.random() will produce different values each run):
Inserting the following values:
85 47 65 90 61 2 36 0 89 56
Preorder traversal
85 47 2 0 36 65 61 56 90 89
Inorder traversal
0 2 36 47 56 61 65 85 89 90
Postorder traversal
0 36 2 56 61 65 47 89 90 85