AhmadLang / Java, How To Program, 2004
.pdf
5 popped
The stack is: 1 0 -1
1 popped
The stack is: 0 -1
0 popped
The stack is: -1
-1 popped Empty stack
com.deitel.jhtp6.ch17.EmptyListException: stack is empty
at com.deitel.jhtp6.ch17.List.removeFromFront(List.java:81)
at com.deitel.jhtp6.ch17.StackInheritance.pop(StackInheritance.java:22) at StackInheritanceTest.main(StackInheritanceTest.java:29)
Class StackInheritanceTest's method main (Fig. 17.11) creates an object of class StackInheritance called stack (line 10). The program pushes integers onto the stack (lines 13, 15, 17 and 19). Once again, note that autoboxing is used here to insert Integer objects into the data structure. Lines 2732 pop the objects from the stack in an infinite while loop. If method pop is invoked on an empty stack, the method throws an EmptyListException. In this case, the program displays the exception's stack trace, which shows the methods on the program execution stack at the time the exception occurred. Note that the program uses method print (inherited from List) to output the contents of the stack.
[Page 835]
Stack Class That Contains a Reference to a List
You can also implement a class by reusing a list class through composition. Figure 17.12 uses a private List (line 7) in class StackComposition's declaration. Composition enables us to hide the List methods that should not be in our stack's public interface. We provide public interface methods that use only the required List methods. Implementing each stack method as a call to a List method is called delegationthe stack method invoked delegates the call to the appropriate List method. In particular, StackComposition delegates calls to
List methods insertAtFront, removeFromFront, isEmpty and print. In this example, we do not show class
StackCompositionTest, because the only difference is that we change the type of the stack from StackInheritance to StackComposition (lines 3 and 10 of Fig. 17.11). The output is identical using either version of the stack.
Figure 17.12. StackComposition uses a composed List object.
1 |
// |
Fig. |
17.12: StackComposition.java |
2 |
// |
Class StackComposition definition with composed List object. |
|
3 |
package |
com.deitel.jhtp6.ch17; |
|
4 |
|
|
|
5public class StackComposition
6{
7private List stackList;
8
9// no-argument constructor
10public StackComposition()
11{
12stackList = new List( "stack" );
13} // end StackComposition no-argument constructor
15// add object to stack
16public void push( Object object )
17{
18stackList.insertAtFront( object );
19} // end method push
20
21// remove object from stack
22public Object pop() throws EmptyListException
23{
24return stackList.removeFromFront();
25} // end method pop
26
27// determine if stack is empty
28public boolean isEmpty()
29{
30return stackList.isEmpty();
31} // end method isEmpty
32
33// output stack contents
34public void print()
35{
36stackList.print();
37} // end method print
38} // end class StackComposition
[Page 836]
17.8. Queues
Another commonly used data structure is the queue. A queue is similar to a checkout line in a supermarketthe cashier services the person at the beginning of the line first. Other customers enter the line only at the end and wait for service. Queue nodes are removed only from the head (or front) of the queue and are inserted only at the tail (or end). For this reason, a queue is a first-in, first-out (FIFO) data structure. The insert and remove operations are known as enqueue and dequeue.
Queues have many uses in computer systems. Most computers have only a single processor, so only one application at a time can be serviced. Each application requiring processor time is placed in a queue. The application at the front of the queue is the next to receive service. Each application gradually advances to the front as the applications before it receive service.
Queues are also used to support print spooling. For example, a single printer might be shared by all users of a network. Many users can send print jobs to the printer, even when the printer is already busy. These print jobs are placed in a queue until the printer becomes available. A program called a spooler manages the queue to ensure that, as each print job completes, the next print job is sent to the printer.
Information packets also wait in queues in computer networks. Each time a packet arrives at a network node, it must be routed to the next node along the path to the packet's final destination. The routing node routes one packet at a time, so additional packets are enqueued until the router can route them.
A file server in a computer network handles file-access requests from many clients throughout the network. Servers have a limited capacity to service requests from clients. When that capacity is exceeded, client requests wait in queues.
Figure 17.13 creates a queue class that contains an object of class List (Fig. 17.3). Class Queue (Fig. 17.13) provides methods enqueue, dequeue, isEmpty and print. Class List contains other methods (e.g., insertAtFront and removeFromBack) that we would rather not make accessible through the public interface of class Queue. By using composition, these methods in the public interface of class List are not accessible to clients of class Queue. Each method of class Queue calls an appropriate List methodmethod enqueue calls List method insertAtBack, method dequeue calls List method removeFromFront, method isEmpty calls List method isEmpty and method print calls List method print. For reuse purposes, class Queue is declared in package com.deitel.jhtp6.ch17.
[Page 837]
Figure 17.13. Queue uses class List.
(This item is displayed on pages 836 - 837 in the print version)
1// Fig. 17.13: Queue.java
2// Class Queue.
3package com.deitel.jhtp6.ch17;
5public class Queue
6{
7private List queueList;
9// no-argument constructor
10public Queue()
11{
12queueList = new List( "queue" );
13} // end Queue no-argument constructor
15// add object to queue
16public void enqueue( Object object )
17{
18queueList.insertAtBack( object );
19} // end method enqueue
20
21// remove object from queue
22public Object dequeue() throws EmptyListException
23{
24return queueList.removeFromFront();
25} // end method dequeue
26
27// determine if queue is empty
28public boolean isEmpty()
29{
30return queueList.isEmpty();
31} // end method isEmpty
32
33// output queue contents
34public void print()
35{
36queueList.print();
37} // end method print
38} // end class Queue
Class QueueTest (Fig. 17.14) method main creates an object of class Queue called queue. Lines 13, 15, 17 and 19 enqueue four integers, taking advantage of autoboxing to insert Integer objects into the queue. Lines 2732 use an infinite while loop to dequeue the objects in first-in, first-out order. When the queue is empty, method dequeue throws an EmptyListException, and the program displays the exception's stack trace.
Figure 17.14. Queue processing program.
(This item is displayed on pages 837 - 838 in the print version)
1// Fig. 17.14: QueueTest.java
2// Class QueueTest.
3import com.deitel.jhtp6.ch17.Queue;
4import com.deitel.jhtp6.ch17.EmptyListException;
6public class QueueTest
7 |
{ |
8 |
public static void main( String args[] ) |
9{
10Queue queue = new Queue();
12// use enqueue method
13queue.enqueue( -1 );
14queue.print();
15queue.enqueue( 0 );
16queue.print();
17queue.enqueue( 1 );
18queue.print();
19queue.enqueue( 5 );
20queue.print();
21
22// remove objects from queue
23try
24{
25Object removedObject = null;
27while ( true )
28{
29 |
removedObject = queue.dequeue(); // |
use dequeue method |
30 |
System.out.printf( "%s dequeued\n", |
removedObject ); |
31 |
queue.print(); |
|
32} // end while
33} // end try
34catch ( EmptyListException emptyListException )
35{
36emptyListException.printStackTrace();
37} // end catch
38} // end main
39} // end class QueueTest
The queue is: -1 |
|
The queue is: -1 0 |
|
The queue is: -1 0 1 |
|
The |
queue is: -1 0 1 5 |
-1 |
dequeued |
The queue is: 0 1 5 |
|
0 dequeued |
|
The queue is: 1 5 |
|
1 dequeued |
|
The |
queue is: 5 |
5 dequeued Empty queue
com.deitel.jhtp6.ch17.EmptyListException: queue is empty
at com.deitel.jhtp6.ch17.List.removeFromFront(List.java:81) at com.deitel.jhtp6.ch17.Queue.dequeue(Queue.java:24)
at QueueTest.main(QueueTest.java:29)
[Page 838]
17.9. Trees
Linked lists, stacks and queues are linear data structures (i.e., sequences). A tree is a nonlinear, twodimensional data structure with special properties. Tree nodes contain two or more links. This section discusses binary trees (Fig. 17.15)trees whose nodes each contain two links (one or both of which may be null). The root node is the first node in a tree. Each link in the root node refers to a child. The left child is the first node in the left subtree (also known as the root node of the left subtree), and the right child is the first node in the right subtree (also known as the root node of the right subtree). The children of a specific node are called siblings. A node with no children is called a leaf node. Computer scientists normally draw trees from the root node downexactly the opposite of the way most trees grow in nature.
[Page 839]
Figure 17.15. Binary tree graphical representation.
[View full size image]
In our example, we create a special binary tree called a binary search tree. A binary search tree (with no duplicate node values) has the characteristic that the values in any left subtree are less than the value in that subtree's parent node, and the values in any right subtree are greater than the value in that subtree's parent node. Figure 17.16 illustrates a binary search tree with 12 integer values. Note that the shape of the binary search tree that corresponds to a set of data can vary, depending on the order in which the values are inserted into the tree.
Figure 17.16. Binary search tree containing 12 values.
[View full size image]
The application of Fig. 17.17 and Fig. 17.18 creates a binary search tree of integers and traverses it (i.e., walks through all its nodes) three waysusing recursive inorder, preorder and postorder traversals. The program generates 10 random numbers and inserts each into the tree. Class tree is declared in package com.deitel.jhtp6.ch17 for reuse purposes.
Figure 17.17. treeNode and tree class declarations for a binary search tree.
(This item is displayed on pages 840 - 842 in the print version)
1 |
// |
Fig. |
17.17: Tree.java |
2 |
// |
Definition of class TreeNode and class Tree. |
|
3 |
package |
com.deitel.jhtp6.ch17; |
|
4 |
|
|
|
5// class TreeNode definition
6class TreeNode
7{
8// package access members
9TreeNode leftNode; // left node
10int data; // node value
11TreeNode rightNode; // right node
13// constructor initializes data and makes this a leaf node
14public TreeNode( int nodeData )
15{
16data = nodeData;
17leftNode = rightNode = null; // node has no children
18} // end TreeNode no-argument constructor
19
20// locate insertion point and insert new node; ignore duplicate values
21public void insert( int insertValue )
22{
23// insert in left subtree
24if ( insertValue < data )
25{
26// insert new TreeNode
27if ( leftNode == null )
28 |
|
leftNode = new |
TreeNode( |
insertValue |
); |
|
29 |
else |
// continue |
traversing |
left subtree |
|
|
30 |
|
leftNode.insert( insertValue ); |
|
|||
31 |
} // |
end |
if |
|
|
|
32 |
else |
if |
( insertValue > data ) |
// insert in |
right subtree |
|
33{
34// insert new TreeNode
35if ( rightNode == null )
36 |
rightNode = new TreeNode( insertValue ); |
37 |
else // continue traversing right subtree |
38 |
rightNode.insert( insertValue ); |
39} // end else if
40} // end method insert
41} // end class TreeNode
43// class Tree definition
44public class Tree
45{
46private TreeNode root;
1 // Fig. 17.18: TreeTest.java
2 // This program tests class Tree.
3import java.util.Random;
4import com.deitel.jhtp6.ch17.Tree;
6public class TreeTest
7{
8public static void main( String args[] )
9{
10Tree tree = new Tree();
11int value;
12Random randomNumber = new Random();
14System.out.println( "Inserting the following values: " );
16// insert 10 random integers from 0-99 in tree
17 for ( int i = 1; i <= 10; i++ )
18{
19value = randomNumber.nextInt( 100 );
20System.out.print( value + " " );
21tree.insertNode( value );
22} // end for
23
24System.out.println ( "\n\nPreorder traversal" );
25tree.preorderTraversal(); // perform preorder traversal of tree
27System.out.println ( "\n\nInorder traversal" );
28tree.inorderTraversal(); // perform inorder traversal of tree
30System.out.println ( "\n\nPostorder traversal" );
31tree.postorderTraversal(); // perform postorder traversal of tree
32System.out.println();
33} // end main
34} // end class TreeTest
Inserting |
the |
following |
values: |
||||||
92 |
73 |
77 |
16 |
30 |
30 |
94 |
89 |
26 |
80 |
Preorder |
traversal |
|
|
|
|
||||
92 |
73 |
16 |
30 |
26 |
77 |
89 |
80 |
94 |
|
Inorder traversal |
|
|
|
|
|||||
16 |
26 |
30 |
73 |
77 |
80 |
89 |
92 |
94 |
|
Postorder |
traversal |
|
|
|
|||||
26 |
30 |
16 |
80 |
89 |
77 |
73 |
94 |
92 |
|
|
|
|
|
|
|
|
|
|
|
[Page 842]
Let us walk through the binary tree program. Method main of class TReeTest (Fig. 17.18) begins by instantiating an empty TRee object and assigning its reference to variable tree (line 10). Lines 1722 randomly generate 10 integers, each of which is inserted into the binary tree through a call to method insertNode (line 21). The program then performs preorder, inorder and postorder traversals (these will be explained shortly) of tree (lines 25, 28 and 31, respectively).
Class tree (Fig. 17.17, lines 44113) has private field root (line 46)a TReeNode reference to the root node of the tree. tree's constructor (lines 4952) initializes root to null to indicate that the tree is empty. The class contains method insertNode (lines 5561) to insert a new node in the tree and methods preorderTraversal (lines 6467), inorderTraversa l (lines 8184) and postorderTraversal (lines 98101) to begin traversals of the tree. Each of these methods calls a recursive utility method to perform the traversal operations on the internal representation of the tree. (Recursion is discussed in Chapter 15.)
Class TRee's method insertNode (lines 5561) first determines whether the tree is empty. If so, line 58
allocates a new TReeNode, initializes the node with the integer being inserted in the tree and assigns the new node to reference root. If the tree is not empty, line 60 calls TReeNode method insert (lines 2141). This method uses recursion to determine the location for the new node in the tree and inserts the node at that location. A node can be inserted only as a leaf node in a binary search tree.
TReeNode method insert compares the value to insert with the data value in the root node. If the insert value is less than the root node data (line 24), the program determines if the left subtree is empty (line 27). If so, line 28 allocates a new treeNode, initializes it with the integer being inserted and assigns the new node to reference leftNode. Otherwise, line 30 recursively calls insert for the left subtree to insert the value into the left subtree. If the insert value is greater than the root node data (line 32), the program determines if the right subtree is empty (line 35). If so, line 36 allocates a new treeNode, initializes it with the integer being inserted and assigns the new node to reference rightNode. Otherwise, line 38 recursively calls insert for the right subtree to insert the value in the right subtree. If the insertValue is already in the tree, it is simply ignored.
Methods inorderTraversal, preorderTraversal and postorderTraversal call tree helper methods inorderHelper (lines 8795), preorderHelper (lines 7078) and postorderHelper (lines 104112), respectively, to traverse the tree and print the node values. The helper methods in class tree enable the programmer to start a traversal without having to pass the root node to the method. Reference root is an implementation detail that a programmer should not be able to access. Methods inorderTraversal, preorderTraversal and postorderTraversal simply take the private root reference and pass it to the appropriate helper method to initiate a traversal of the tree. The base case for each helper method determines whether the reference it receives is null and, if so, returns immediately.
[Page 843]
[Page 844]
Method inorderHelper (lines 8795) defines the steps for an inorder traversal:
1.Traverse the left subtree with a call to inorderHelper (line 92).
2.Process the value in the node (line 93).
3.Traverse the right subtree with a call to inorderHelper (line 94).
The inorder traversal does not process the value in a node until the values in that node's left subtree are processed. The inorder traversal of the tree in Fig. 17.19 is
6 13 17 27 33 42 48
Figure 17.19. Binary search tree with seven values.
[View full size image]
Note that the inorder traversal of a binary search tree prints the node values in ascending order. The process of creating a binary search tree actually sorts the data; thus, it is called the binary tree sort.
Method preorderHelper (lines 7078) defines the steps for a preorder traversal:
1. Process the value in the node (line 75)
