Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

AhmadLang / Java, How To Program, 2004

.pdf
Скачиваний:
626
Добавлен:
31.05.2015
Размер:
51.82 Mб
Скачать

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;

48// constructor initializes an empty Tree of integers

49public Tree()

50{

51root = null;

52} // end Tree no-argument constructor

53

54// insert a new node in the binary search tree

55public void insertNode( int insertValue )

56{

57if ( root == null )

58

root = new TreeNode( insertValue ); // create the root node here

59else

60root.insert( insertValue ); // call the insert method

61} // end method insertNode

62

63// begin preorder traversal

64public void preorderTraversal()

65{

66preorderHelper( root );

67} // end method preorderTraversal

69// recursive method to perform preorder traversal

70private void preorderHelper( TreeNode node )

71{

72if ( node == null )

73return;

74

 

 

 

 

 

75

System.out.printf( "%d ", node.data ); // output node data

76

preorderHelper( node.leftNode );

//

traverse

left

subtree

77

preorderHelper( node.rightNode );

//

traverse

right

subtree

78

} // end method preorderHelper

 

 

 

 

79

 

 

 

 

 

80// begin inorder traversal

81public void inorderTraversal()

82{

83inorderHelper( root );

84} // end method inorderTraversal

86// recursive method to perform inorder traversal

87private void inorderHelper( TreeNode node )

88{

89if ( node == null )

90return;

91

 

 

 

 

 

92

inorderHelper( node.leftNode );

//

traverse

left

subtree

93

System.out.printf( "%d ", node.data ); // output node data

94

inorderHelper( node.rightNode );

//

traverse

right

subtree

95

} // end method inorderHelper

 

 

 

 

96

 

 

 

 

 

97// begin postorder traversal

98public void postorderTraversal()

99{

100postorderHelper( root );

101} // end method postorderTraversal

103// recursive method to perform postorder traversal

104private void postorderHelper( TreeNode node )

105{

106if ( node == null )

107return;

108

 

 

 

 

 

 

 

109

postorderHelper(

node.leftNode

);

//

traverse

left

subtree

110

postorderHelper(

node.rightNode

);

//

traverse

right

subtree

111System.out.printf( "%d ", node.data ); // output node data

112} // end method postorderHelper

113} // end class Tree

Figure 17.18. Binary tree test program.

(This item is displayed on page 843 in the print version)

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)