Access Modifiers
The access modifiers regulate the access of java classes, fields and methods. There
are four different access modifiers.
1. public
2. private (An outer java class cannot be declared private. But
inner classes can be declared private).
3. protected (An outer java class cannot be declared protected.
But inner classes can be declared protected).
4. No modifier is specified.
A minimal java class declaration may be done as follows: Note the absence of
//*************************************************
package name! This
class MyClass means that class is in
{ an unknown package
and is not accessible
//---------------- outside it!
}
Listing 1.1
//**********************************************
In defining the class in Listing 1.1 no modifier is used before the word class. Thus
the class MyClass becomes what is called “package-private” or package visible.
Listing 1.2
//**********************************************
Why is above form useful? Because now the class MyClass2 being declared as public
becomes accessible to rest of the world by virtue of being part of a named package1.
All Sun Java Classes are defined in this manner (All public, all members of some
named package.)2
Modifiers private and protected are not used for the outer
classes, but they can be used for the inner or nested classes. We
will discuss additional modifiers for classes after we have discussed the topic of
inheritance.
1
The package naming requires that name must be unique. Sun uses the system of reverse domain
notation. For example the packages from Oracle Corporation may have names such as
com.oracle.packagename. Since the name com.oracle will be unique, all packages become unique,
with out any chance of naming conflict and confusion.
2
The details of compiling packages are given at the Web site below:
http://www.yoda.arachsys.com/java/compiling.html
FIG. 1.1
It is clear from the Listing 1.3 and 1.4 that:
We also must understand that static variables have only one copy
for all the instances of a class, whereas non-static variables will
have number of copies, equal to the number of instances for that
class.
3
Methods and variables must be package-private(no access modifier used), public, or protected in
order to be accessed inside another class in the same package. Public methods and variables are
accessible everywhere.
FIG. 1.2
As shown in Figure 1.2, the public field in the class “a class” is visible inside all of its
subclasses as well as inside all the client4 classes.
Protected fields and methods are visible and accessible in the derived classes and in
the classes in the same package, but not in any other classes. We discuss the
protected fields in more detail after we discuss the inheritance. The Figure 1.3 below
shows the visibility of protected fields in more detail.
4
Client classes are the one which may make use of “a class” either as a local variable or as a class
level variable.
FIG. 1.3
As shown in Figure 1.3, the protected fields and methods are visible to all sub classes
and classes in the same package, but not to the clients that are outside the package.
A sub class may lie inside or outside the package.
Private fields and methods are not visible outside the class they are declared. Private
fields can only be accessed, outside their native class by using a public method to
allow access to them. Figure 1.4 shows the visibility of private fields and methods in
more detail. The sub-classes inherit the private members of super
class, but have no access to them. To access the private but inherited
members, the super class must have a public method available, which can get the
access to the private fields.
a class
A rea of
Client 1 visibility.
C lients and
Subclass 2 subclasses,
Client 2 both m ust
Subclass 1 lie inside
the
package
FIG. 1.5
Inheritance
When we write a Java classes, we try to model a part of the reality of the world. For
example, when we write a complex number class, we model the behavior of complex
numbers in the class. However, in reality the things are related. By modeling the
related things together, we can see that how modeling one thing, can
help us model something related to it. Using Java’s mechanism of establishing
inheritance relationship between classes can model reality more effectively. Let us
take an example of vehicles in the world. Every vehicle has at least one property –
speed. We can write a class called vehicle, which has one data member called speed.
However, there are many kinds of vehicle in the world. For example, the vehicle
may be a wheeled vehicle, which will move on roads and surfaces or it may be an
airplane that will fly in air. Both vehicles such as airplane and vehicle with wheels
have a relationship with general concept vehicle. We can say that an airplane is-a
vehicle. We can also say that a vehicle with wheels is-a vehicle. When objects have
this “is-a” type relationship between each other, then an inheritance relationship
Microsoft PowerPoint
Presentation
FIG. 1.6
Now, at each level of above inheritance hierarchy, new properties are added to the
system of related objects. For example, we may ask the question, as to what is the
most common property to all vehicles? Obvious answer is that all vehicles have
speed. So now, we take this system of inheritance relationships and start filling it
with properties. This is dynamically presented below.
Microsoft PowerPoint
Presentation
We can see that all vehicles have a property speed, and all airplanes have a property
called “engine thrust”. We keep adding one or more new property at each level of
inheritance derivation in our system. The Figure 1.7 below shows the result.
FIG. 1.8
At each level of inheritance, the class, which is the source of inheritance, is called a
base class or a superclass. Moreover, the class that results from inheritance is called
a derived class of subclass. The phrase subclass should not be taken to mean that
somehow the derived class is “lesser” in some way. In fact, if anything it has more
data members. In the present system that we have built here, the Vehicle class is the
FIG. 1.9
FIG. 1.10
FIG. 1.11
Generally in java classes that are not expected to be extended, the data members are
declared private. One characteristic of private data members is that, they are visible
to the methods in the class, but invisible to rest of the world. The establishment of
inheritance, when all the data members of base class are private, complicates the
matter somewhat. Java allows another form of access specifier for the data
members of a class, which makes them invisible to world outside the class and its
package, but visible to derived classes. This specifier is called “protected”. As shown
in Figure 1.3, the protected data members are visible in the derived classes and in
the client classes in the same package.
FIG. 1.12
By making the field of class Vehicle, for example in this case, the speed protected,
has the affect of class Airplane having two fields, speed and numberOfwings.
Similarly, the class Jet has three fields, speed, numberOfwings, and enginethrust.
What we do now is that we add fields, constructors, and a method called describe ( )
to all classes in the Figure 1.7. This results in the code given in Listing 1.5 below.
In Listing 1.5, the function describe ( ) for each class has a single output statement
describing the type of vehicle the class represents. For example the function
describe ( ) for class Vehicle is coded as follows , and we also add the constructors
for class vehicle.
public Vehicle()
{
this(0.0);
System.out.println ("From Vehicle CTOR #1");
}
public Airplane()
{
this(0);
System.out.println("From Airplane CTOR #1");
}
public Proplane()
{
this(0);
System.out.println("From Proplane CTOR #1");
}
public VehicleWithWheels()
{
this(0);
System.out.println("From VehicleWithWheels CTOR #1");
}
public Car( )
{
this(0.0);
System.out.println("From Car CTOR #1");
}
public Car( double init_speed)
{
this(init_speed, 0);
System.out.println("From Car CTOR #2");
}
public Car( double init_speed,int nWheels)
{
this(init_speed, nWheels, 0.0);
System.out.println("From Car CTOR #3");
}
Listing 1.6
We create an instance Temp of class AnyClass by calling the operator new and
calling the default java provided constructor. Then we call an instance method
hashcode( ), to print the hash code for the Temp. The resulting output is shown in
the Figure 1.13 below.
FIG. 1.13
The class AnyClass does not have a method called hashcode. Therefore, where did
the method hashcode come from? It actually came from the class Object, which is
the super-class for AnyClass. Moreover, the Object part of AnyClass instance Temp
is built, when during the constructor call new AnyClass( ), the Object class
5
If derived class constructor does not make an explicit call to the super class constructor, then Java
will automatically call the default super class constructor.
Therefore, in order to build an instance of Jet class, the constructor calls are made
in the following sequence (Figure 1.15).
Jet part is
Jet Constructor call created Last!
FIG. 1.15
The Figure 1.14 shows the output statements coded in the constructors of Vehicle,
Airplane, and Jet class, when the Jet class constructor in the Listing 1.17 is called.
These output statements are:
From Vehicle CTOR #2
From Airplane CTOR #3
From Jet CTOR #4
There is no output statement in the Object class constructor so we do not see a
similar statement from that. However, the sequence of other three-constructor call
is similar to the sequence shown in the Figure 1.15. General conclusion drawn is as
follows:
In java constructor calls are made in the sequence of inheritance
hierarchy of an object. The java Object class constructor will
ALWAYS be called first, followed by the sequence of inheritance
hierarchy of an object being created.
I'm a Jet.
I am propelled by a fast jet engine.
The mechanism where JVM will correctly bind an object to “its” method at run
time is called dynamic binding or late binding.
6
All the static or class method calls are resolved at the compile time.
FIG. 1.16
One can see that all Jet class methods are called properly and they give proper
results by following the procedure described in the Listing 1.8.
Preventing Inheritance
Some times for security and other reasons it is necessary that another class never
inherits a class. Defining a class final prevents it from another class inheriting it. All
classes in java.lang package are final classes. The syntax of defining a class to
become final is as follows:
Listing 1.9
Anonymous Classes
Anonymous or no name classes are declared on the fly, by using the operator new.
They behave just like locally defined classes, except they are for one time use only as
they have no name. For example in Listing 1.9, the program statement below can
create a no-name instance of class AnyClass:
new AnyClass( );
In a form such as shown by the above statement, they can only show an output from
the constructor being called. Anonymous classes are more useful when they are
instantiated and used to return as a value from a method, or provide some other
“one time” functionality. The rules as to which local and class level parameters
anonymous classes can access locally are same as for the local classes discussed
above. In Listings 1.10 and 1.11 below, we contrast the use of named local inner
class and anonymous class to achieve the same objective, which is used to close a
7
An initialization block is located at the class level as a stand-alone block (bounded with curly
braces). The initialization blocks may be marked as static or may be unmarked. Code in all static
blocks is executed “once” when program is run first time. Non-static or unmarked blocks are
executed each time a class constructor call is made.
8
Local constant reference types can be declared as follows:
final AnyClass Var1 = new AnyClass( );
Note that in this case the reference Var1 is constant, which means that Var1 cannot be used to point
to another object of type AnyClass later in the program sequence. This does not however make the
object to which Var1 is pointing to immutable (or unchangeable). The java objects are immutable
only if their class is defined as final.
FIG. 1.14
However, if only one window is to be created in the program, then we can use an
anonymous class, which extends the Java class WindowAdapter for one time use.
The Listing 1.11 shows the code for such anonymous class and its use. The results of
Listings 1.10 and 1.11 are identical.
Object oriented languages as Java and C++ use a concept of abstract classes, so that
an object of certain type can be conceptualized and then extended further. For
example, take the concept of round shapes in geometry. The roundness is a property
that is present in many different forms in real objects. The circle, spheres, and,
elliptical objects have roundness. However, the concept roundness is a bit abstract,
so if we wrote a class to capture this concept, we may never be able to truly find a
real object for which the class Roundness can be a template. The concepts and
behaviors, that apply to other objects, but are too abstract to make concrete, are
often written in the form of abstract classes. In java, the abstract classes have
following main properties.
Abstract Class
1. The abstract class has at least one abstract method.
An abstract method is a method, which has no body or
definition. It just has a signature and return type.
Now there can be many types of shapes. A point is a shape, which is infinitesimal in
size. However, it has properties such as coordinates x and y in the Cartesian system.
Therefore, the Class Point can extend the general concept of Shape to a concrete
form. Hence, we derive the class Point from Shape.
A Square has a center point just like a Point and a class Square can inherit the
coordinates x and y from class Point. Nevertheless, Square shape has additional
property like a length or edge. Therefore, we derive the class Square from Point.
Finally, a Cube has the same properties or fields as the Square does – like a central
point, and an edge, but has a volume, which square does not have. Yet, no new fields
are needed if we were to derive the class Cube from class Square.
Implemented
Methods
Abstract
Method
Listing 2.1
The class Shape is an abstract class because it has one un-implemented (abstract)
method getName( ). The syntax requires that the keyword “abstract” be included in
the declaration of method signature.
class Point
The source code for the class Point is given below in the Listing 2.2.
class Square
The source code for the class Square is given below in the Listing 2.3.
public class Square extends Point
{
protected double edge; New field
edge is
added. It inherits
public Square() x and y.
{
this(0.0);
}
class Cube
The source code for the class Square is given below in the Listing 2.4. Does not need
any new fields.
public class Cube extends Square
{
public Cube()
{ Constructors
this(0.0);
}
public Cube(double edge1)
{
this(edge1,0,0);
}
The UML(Unified Modeling Language) diagram for all these classes is given on next
page.(Figure 2.3).
Inner Classes:
A class is an inner class, when it is declared inside another class. Some time such
classes are also called nested classes. However, the Java Specification document
points out a subtle difference between the inner classes and nested classes. First, let
us discuss few more definitions. A stand-alone class is not allowed to be declared
static. For example:
However, a nested class can be declared static. For example, the declaration like
below is OK.
Static inner
class/nested
class
Listing 2.5
The above program creates a window with blue background color and it can be
closed by clicking on the icon X on right hand corner or by pressing alt + F4.
The nested class WindowClosing is instantiated in the main method by the
statement below, even though the class CloseableWindow is never instantiated in the
whole program.
WindowClosing Object_WindowClosing = new WindowClosing( );
Important question however, is that what would happen if the inner class
WindowClosing is made a non-static class. In Listing 2.5, a whole bunch of extra
code will be needed to achieve the same results if the nested class WindowClosing is
made non-static. This code is shown for the class ColseableWindow2 in Listing 2.6.
Instance of class
CloseableWindow2 is
now needed because
with out it the inner
class WindowClsoing
cannot be
instantiated.
Instance.new
is required to
call the
constructor for
inner class
Listing 2.6
Note that when an inner class is non-static then an instance of outer class will be
needed, before an instance of inner class can be created. Also, note the syntax of the
constructor call for the inner class:
Even more code would be needed if the Frame type class variable My_Window is
made non-static in the Listing 2.6. Affect of this are shown in the Listing 2.7 below.
import java.awt.*;
import java.awt.event.*;
public class CloseableWindow3
{
private Frame My_Window ; A constructor to instantiate the object
Frame is now needed. Otherwise the inner
public CloseableWindow3( ) class WindowClosing will not know as to
{ which My_Window to dispose.
this.My_Window = new Frame();
}
CloseableWindow3.WindowClosing Object_WindowClosing =
Instance.new WindowClosing();
Instance.My_Window.addWindowListener(Object_WindowClosing);
}
} Listing 2.7
In last two laboratory exercises, we made a good use of the JoptionPane. Therefore,
now it is time for us to move further. In testing the classes of Figure 2.2, and Listing
2.1 to 2.4, while we will be using the JoptionPane for input, we would display all
data in the Window Frame using objects of class java.awt.Label, added to the
window Frame. In the last section on inner classes, we showed that in order for a
colosebale Window to show up, we need the following objects and steps.
1. A static java.awt.Frame object constructed at the class
level.
2. Set the bounds of this Frame object.
3. Set the background color of this Frame object.
4. Make the Frame object visible.
5. Close and dispose the Frame object, when user clicks on
window closing icon or presses alt + F4.
The last step requires that we give our frame, created in steps 1 to 4 a capability to
“listen” and respond to user clicks or keystrokes.
Asynchronous Programming
When we give a graphical component in a user interface the capability to respond to
user actions (like keystrokes or mouse actions), such programming is called
asynchronous programming. It is so because the user action will not follow the
sequential flow and execution that program source code has. The program may be
doing ten different things, but if user presses certain keys or clicks mouse then the
program has to respond to it. The whole process requires interaction of three
agents:
1. User
2. Operating System (in our case Windows)
3. The program
When user takes an action, like pressing a key or clicking certain active area of the
program window, the operating system fires an object (called windows event) to the
The listeners are added to graphical or GUI components by invoking one of their
method which has a name similar to:
addSomeListener ( ).
In addition, a method that adds a listener will take as its argument an object, which
can invoke an event handling method or invoke an event handler. The process of
adding a listener is called “registering” a GUI component with the event listener.
This listener is a java class that has the event handling method in it.
When the window from Listing 2.4 is in front of user and the user clicks the mouse
to close it, then at that point the following sequence of events takes place.
The class implementing a java interface uses keyword implements to implement it.
Syntax of implementing a java interface is given below.
}
public interface SomeInterface extends Interface1, Interface2
{
……
}
Note that keyword “extends” is used when an interface extends another
interface.
Listing 3.1
Student Voter
StudentVoter
FIG. 3.1
C++ allows classes to be derived as a result of multiple inheritance, therefore in C++
all, Person, Student, Voter and StudentVoter can be classes. The class StudentVoter
may inherit the fields and virtual functions from all of its super classes. This at times
may cause ambiguities and special care is needed to remove them. Java however,
would not allow the inheritance shown in Figure 3.1 if all the entities shown are
coded as classes. One allowable scenario in java is that entities Person and Voter are
declared as interfaces, and then Student and StudentVoter can be declared as
classes (Figure 3.2).
StudentVoter
FIG 3.2
The advantage of such multiple inheritance is that an object of StudentVoter Class
may be represented by a reference of any of its three classes – one of the key
advantage of multiple inheritance. Limitation however is that unlike C++ in java,
Person and Voter being interfaces, they cannot contain any protected fields. Java
in this sense only allows “behavioral” multiple inheritance.
Codes for classes and interfaces in Figure 3.2 are shown in Listing 3.2. The UML
diagram for Figure 3.2 is shown in the Figure 3.3.
String getName( );
int getAge( );
}
Listing 3.2 Part A
Interface Voter extends interface Person. That means that the methods in the
Person interface are also automatically inherited by the interface Person. We
however, include some static constant strings in the Voter interface to hard code the
possible party a student voter may be member of. We also include a method
getParty( ) in the Voter interface, which returns the party of the student voter as a
string. The coded Voter interface is shown in the Listing 3.2 Part B.
String getParty( );
}
Listing 3.2 Part B
After coding the two interfaces, now we can code the necessary classes. First, we
code the class Student, which derives from interface Person. Class students has
name, age and gpa as fields, and we have shown all the necessary chained
constructors. Since Person interface is implemented by the class Student, we provide
the implementation of the methods getAge( ) and getName( ). We also code some
helper methods such as getGpa( ), and toString( ). The method getGpa( ) is unique
to class student, as only a person, who is a student can have gpa. The toString ( )
method overrides the corresponding method in the Object class. The code for the
class Student is shown in the Listing 3.2 Part C.
}
public StudentVoter(String Init_Name, int init_age, double init_gpa,
int init_lastvote, String Init_Party)
{
super(Init_Name, init_age,init_gpa);
this.lastvote = init_lastvote;
if(Init_Party.equals(StudentVoter.PARTY1))
this.Party = PARTY1;
else if(Init_Party.equals(StudentVoter.PARTY2))
this.Party = PARTY2;
else if(Init_Party.equals(StudentVoter.PARTY3))
this.Party = PARTY3;
else if(Init_Party.equals(StudentVoter.PARTY4))
this.Party = PARTY4;
else if(Init_Party.equals(StudentVoter.PARTY5))
this.Party = PARTY5;
else
{
System.out.println("Bad party name. Setting party to none.");
this.Party = PARTY5;
The polymorphic
method selectionSort
sorts the Person type
array alphabetically.
FIG. 3.4
The method selectionSort is a standard sorting method, which works based on a
well-known selection sort principle of array sorting technology. The method looks
for the object, which has the largest member (in this case the name) and bubbles it
to the end of the array. The maximum number of passes required to sort the array
are one less than the array size.
We can see very clearly from the results that the polymorphic method selectionSort
works nicely as it takes an array of mixed Student and StudentVoter type objects
and sorts them in the alphabetical order by the name.
}
}
static
{ Class defined within a stand-alone
class Class2 static block.
{
}
}
public LocalClasses( )
{
class Class3
{ Class defined within a constructor.
}
public static void main(String[ ] args)
{
class Class4 Class defined within a static method. Same
{ way the classes can be defined within non-
static methods as well.
}
}
}
Listing 3.3
1
Local definition means defining with in a stand-alone block, constructor, or a method.
}
}
Listing 3.4
The nested interface (in this case NestedInterface1) may use the modifiers public,
protected, private and static as needed. Static and public nested interface acts like a
stand-alone interface, but the enclosing class defines its name and accessibility. A
class cannot implement the interface it encloses within itself. However, other classes
can implement an interface nested inside another class. For example, a code similar
to the Listing 3.5 is allowed.
Listing 3.5
Also, note that the Class1 could implement the nested interface NestedInterface1
because it was not defined private. Privately defined nested interfaces can only be
implemented with in their member class. For example, a code similar to Listing 3.6
is allowed.
Listing 3.6
Interfaces can nest other interfaces and classes as well. The members of nested
interfaces are also all public. A class that implements an enclosing ( or outer
interface is only responsible for implementing the methods in the enclosing
interface. For example, in Listing 3.7, the class MyClass only need to implement
method1 ( ). It is not responsible to implement method2 ( ). On the other hand the
class TheirClass, which implements both, the nested and outer interface, must
implement both method1 ( ) and method2 ( ).
interface NestedInterface2
{
void method2( );
class ClassInInterface
{
}
}
Only responsible to
class MyClass implements InterfaceNesting implement the method1( ).
{
public void method1( ){ }
}
Listing 3.7
You are already familiar with some of the data structures provided by Java. For
example, Array is one kind of data structure, which can be used to store large
amount of data in the program memory. The goal of certain type of data structure
provided by a language is to facilitate certain type of operations on data during the
course of use of a program. Using the data structure designed for a programming
protocol fulfills that protocol easier. Take the example of arrays. If our
programming protocol requires that:
• We have random access to the elements of a data structure
• We should be able to swap two members of the data structure
easily
• Data structure knows its capacity in terms of maximum elements
it can hold
• We can iterate through the data structure sequentially if we like
Then all the above protocols boil down to the design of array data structure, which
we use in java.
Now knowing these operational details we can go a step further in the logical design
of our stack data structure. During the discussions of java interfaces, we indicated
that the interfaces are expression of pure design (or at least the expression of pure
“behavioral” design). We may in the process of writing a functional stack class
consider writing an interface which will capture the entire essence of the “behavior”
of a stack, and then have the stack class implement this interface. Why is the use of
interface important? Because by designing the interface that governs the behavior
of a stack data structure, we can communicate to the code writer very precisely as to
what our design specifications are. The Listing 5.1 below gives us the code for the
StackInterface, which the code writer for the class Stack can implement.
Listing 5.1
From the Listing 5.1 one would notice that we handle the situation where the stack
is empty and the method pop is invoked, by throwing an exception
StackUnderflowException. Similarly if a push ( ) or top ( ) operation is
A simple test class to test the ArrayStack class is shown in the Listing 5.4. Figure 5.3
shows the test results from Listing 5.4.
FIG. 5.3
From the Figure 5.3 one would notice that the last element pushed on the stack was
character ‘T’. Therefore a call to method top ( ) shows the element T. Then we call
the method pop ( ) which discards the element T. The call to method top ( ) then
prints the current top element on the stack 9.992.
void function2( )
{
cout<<"In function2\n";
function3();
}
void function3( )
{
cout<<"In function3\n";
function4();
}
void function4( )
{
cout<<"In function4\n";
function5();
}
void function5( )
{
cout<<"In function5\n";
}
Listing 5.5
The program has a main function and five other C++ functions called function1 ( ),
function2 ( ) etc. The key thing is that main function calls the function1 ( ), then
function1 ( ) calls the function 2( ) , function 2 ( ) calls function 3 ( ) and so on. How
does compiler keeps track as to where to return when one function call completes
itself? Well it builds a stack structure as it piles up the locations where it was in each
function. When the compiler is executing the function 5, the function call stack looks
like the given figure 5.4.
FIG. 5.4
In Figure 5.4 the yellow arrow shows as to which line compiler will execute, when
programmer moves execution forward (This is done by pressing F10 or F11 as the
case may be). The call stack also shows as to where compiler is. Figures 5.5 A to E
show as to how the call stack is popped by the compiler as the execution progresses
beyond the state shown in Figure 5.4.
FIG. 5.5 A
FIG. 5.5 B
FIG. 5.5 C
Call to function2 ( ) is popped off the
stack. Control returns to function1( )
Fig. 5.5 D
Fig. 5.5 E
We see that compiler keeps track of as where to return in the program structure by
looking at the function call stack it built, and then popping that stack as the calls to
each function is completed. As execution of a certain function completes, it is taken
off the stack and the control returns to the next function on the stack. A similar
process is followed when a call stack is built up during recursion. We shall show this
when we discuss recursion.
The algorithm for the evaluation of the arithmetical POSTFIX expression is clearly
demonstrated by the following example using the stacks data structure. Let us
revisit our example of a POSTFIX arithmetical expression such as: 5 7 + 6 2 - * = ?
We scan from left to right and we find number ‘5’. We push this number into the
stack as shown in the figure 5.6 A:
Stack
5
FIG. 5.6 A
Our topindex pointer is at point just after number 5. We show the location of the
pointer by an
apostrophe as follows: topindex location shown by using ‘
5’ 7 + 6 2 - * = ?
Then we move the pointer further and we find and read number 7. We push ‘7’ also
on to the stack. The pointer and stacks now look as follows:
5 7’ + 6 2 - * = ?
7
5
FIG. 5.6 B
Now we move the pointer to the right again to read the next character. This
character is a binary arithmetical operator ‘+’. We now pop two operands ‘7’ and
‘5’ from the stack and apply the operator ‘+’ to them as follows: We add 5 to 7 and
push the result back in the
stack as shown in the figure 5.6C. Our pointer now is placed after the binary
operator ‘+’ as follows: 5 7 +’ 6 2 - * = ? Then we move the pointer once again to
the right, and we read the number 6. We push the ‘6’ on to the stack as given by the
figure 5.6 D:
6
12
FIG. 5.6 D
Our pointer is now placed after the number ‘6’ as follows: 5 7 + 6’ 2 - * = ? Then we
start reading again and this time we read number ‘2’. We push number ‘2’ on to the
stack, as shown by the figure 5.6 E:
2
6
12
FIG. 5.6 E
Our pointer is now placed just after the number ‘2’ as follows: 5 7 + 6 2’ - * = ?
As we resume reading the next we read is the operator ‘-’. At this point we pop ‘2’
and ‘6’ from the stack and apply the binary subtraction to them as shown in the
figure 5.6 F:
6 4
12 - 2 12 6-2=4 12
Our pointer now is placed just after the subtraction operator ‘-’ as follows:
* 4 12 * 4 = 48
12
First Pop Second Pop If Stack is Empty
Operation Operation output the result
FIG. 5.6 G
The entire expression has now been read, and stack is empty, so the result ‘48’ is
outputted to the screen or any other relevant output device. We may write our
POSTFIX equation as follows: 5 7 + 6 2 - * = 48. The simple rule to remember here
is that as we pop a value to apply a binary operator, we put the operator in front of
the first popped value, and then we pop the second value and put it in the front of
the binary operator.
33 * X + (Y - 12) - Z
output
Stack is
Empty
FIG. 5.7 A
The gray color in the figure above shows as to how much of the input expression has
been read.
2. We read again to right and next we read the multiplication operator ‘*’. We push
this operator into the stack as shown by the figure 5.7B. The output remains
unchanged.
33* * X + (Y - 12) - Z
output
3
*
Stack
FIG. 5.7 B
3. We read to right again and we read the operand X, which is outputted. The stack
remains unchanged. (Figure 5.7 C).
33** X + (Y - 12) - Z
output
3X
*
Stack
FIG. 5.7 C
33** X + (Y - 12) - Z
output
3X *
+
Stack
FIG. 5.7 D
5. Then as we resume reading we read the left parenthesis ‘(‘. We push that on to
the stack (Rule 1). The output remains unchanged. (Figure 5.7 E).
3* X+ ( Y - 12 ) - Z
output
( 3X *
+
Stack
FIG. 5.7 E
6. We resume reading and we read the operand Y, which is outputted. The stack
remains unchanged. (Figure 5.7 F).
output
( 3X * Y
+
Stack
FIG. 5.7 F
7. Next we read the binary operator ‘-’. Now according to Rule 3, we examine the
contents of stack. Since stack has only left parenthesis on the top, we do not output
any thing. Rather we push the binary operator ‘-’ on to the stack. (Remember that
the left parenthesis is to be taken out and discarded only when a right parenthesis
‘)’ is read.). The situation is shown by the figure 5.7G:
3 * X + ( Y - 12 ) - Z
output
-
( 3X * Y
+
Stack
FIG. 5.7 G
8. Then we read the operand ‘12’ which is outputted(Rule 2). (Figure 5.7H).
3 * X + ( Y - 12 ) - Z
output
-
( 3 X * Y 12
+
Stack
FIG. 5.7 H
9. Continuing reading to right, we read the right parenthesis ‘)’. According to Rule
4, we Pop and output the operator ‘-’ from the stack and then we Pop and discard
the left parenthesis. The right parenthesis is discarded as well. This is shown in the
figure 5.7 I:
output
)
3 X * Y 12 -
(
+
Trash
Stack
FIG. 5.7 I
10. Then we read the binary operator ‘-’. As per Rule 4, we examine the contents of
our stack and output any operators of equal or higher precedence (until we reach
the bottom or a left parenthesis or an operator of lower precedence). This require
Popping and outputting the ‘+’ operator, and then we push the binary operator ‘-’
that was just read, on to the stack. (Figure 5.7J).
3* X+ ( Y - 12 ) - Z
output
)
3 X * Y 12 - + (
_
Trash
Stack
FIG. 5.7 J
11. We finally read the last operand in our expression ‘Z’. We output it (Rule 2).
The stack remains unchanged. (Figure 5.7 K).
3 * X + ( Y - 12 ) - Z
output
)
3 X * Y 12 - + Z (
_
Trash
Stack
FIG. 5.7 K
12. As a last operation we output any operators left in the stack, so we output the
operator ‘-’ from the stack, and stack is now empty, as well as our conversion from
Trash
Stack Post Fix Form
Empty
FIG. 5.7 L
Method Summary
boolean empty()
Tests if this stack is empty.
Object peek()
Looks at the object at the top of this stack without removing it from
the stack.
Object pop()
Removes the object at the top of this stack and returns that object
as the value of this function.
Object push(Object item)
Pushes an item onto the top of this stack.
int search(Object o)
Returns the 1-based position where an object is on this stack.
Table 5.1
The method peek ( ) is similar to the method top( ) discussed in Listing 5.3. If stack
is empty then methods peek ( ) and pop ( ) will throw an EmptyStackException
object. The Stack class in java uses a Vector. Therefore, it avoids the problem of
stack being full, since Vector will keep expanding as more elements are added.
public ReversePolishNotation(String s)
{
inputString = s;
}
if (s.equals("["))
operatorPriority = 0;
else if (s.equals("("))
operatorPriority = 1;
else if (s.equals("+"))
operatorPriority = 2;
else if (s.equals("-"))
operatorPriority = 2;
else if (s.equals("*"))
operatorPriority = 3;
else if (s.equals("/"))
operatorPriority = 3;
else if (s.equals("^"))
operatorPriority = 4;
return(operatorPriority);
}
Then rule three (page 17) is enforced by determining the priority of the next
character in comparison to the priority of the characters (operators, left and right
parentheses, and square bracket ) in the stack. The method priority ( ) helps to
enforce the rule three as shown in Table 5.2.
Operator Priority
Right square bracket or ] -1
Right parenthesis or ) -1
Left square bracket or [ 0
Left parenthesis or ( 1
Plus ( + ) 2
Minus ( - ) 2
Multiply ( * ) 3
Divide ( / ) 3
Exponentiation (^) 4
Table 5.2
The loop given below is entered only if the priority assigned to the character read is
lower or equal to the character on the top of the stack.
while (priority(nextCharacter) <= priority(topOfStack))
{
topOfStack = (String)myStack.pop( );
this.outputString+=" ";
outputString += topOfStack;
topOfStack = (String)myStack.peek( );
}
Notice that rule three will be enforced only if the next operator is one of the
followings: +, -, *, /, ^ . This is because all other situations, where the character is
other than mathematical operator are taken care by other rules. Therefore the
method priority ( ) will return a value of two if the next character is + or -, a value of
three if the next character is *, or /, and a value of four if the next character is an
exponentiation ^. The while loop then pop the characters on the stack until an
operator of lower precedence is seen. The last line of the code to enforce rule three
pushes the next character on to the stack as follows:
FIG. 5.8 A
Postfix
form
outputted.
FIG. 5.8 B
FIG. 5.8 D
FIG. 6.1
One everyday example of a FIFO queue is the line in the stores where people line up
to pay for the merchandise they purchase (Figure 6.2).
Queue Interface
We communicate our design of a queue data structure by writing the specification
for a queue interface. These specifications are shown in the listing 6.1.
//----------------------------------------------------------------------------
// QueueInterface.java
Listing 6.1
Listing 6.2 A
public class QueueUnderflowException extends RuntimeException
{
public QueueUnderflowException( )
{
this(“”);
}
Listing 6.2 B
As was the case for Stack interface, the exceptions used for queue are also
“unchecked” exceptions, which are only implemented at runtime.
Microsoft PowerPoint
Presentation
FIG 6.4 A
FIG 6.4 C
FIG 6.4 E
FIG 6.4 G
FIG 6.4 I
Circular Queue
We use the java applet shown on the following link on Internet to understand the
operation of array based circular queue.
Circular Queue Java Applet
The Figure 6.5 shows the sequence of operation in the circular or wrap around
array based queue. First we show the empty queue (Figure 6.5 A).
FIG. 6.5 B
FIG. 6.5 C
FIG. 6.5 D
FIG. 6.5 E
Now the operation
enqueue (G) adds the new
element to index zero and
rear moves to index 1.
FIG. 6.5 F
As elements are added, the front stays fixed at zero but back pointer keeps moving.
Finally, when the queue is full the front and back pointers are at the same location
(Figure 6.5 D). As we know that front moves only when dequeue is performed. We
remove three elements ( A, B, and C), so then front moves to location 3 (Figure 6.5
E). Now if we perform enqueue operation, then the new element is added at the first
empty location zero and the rear pointer is moved to location 1. In wrap around or
circular implementation, the rear pointer may be at the index less than the front
pointer, but the queue still follows the FIFO protocol precisely. The advantage is
that unlike linear implementation, we do not have to move and readjust the entire
queue to reuse the empty front space. The wrap around logic is implemented by
adjusting the front and rear pointers as follows (Listing 6.1):
Initially the rear pointer is set to zero. When the first element is added the rear
pointer moves to one and the first element is added to the location zero. Therefore
the location of rear pointer can be adjusted by the expression:
dequeued elements.
Exception thrown
FIG. 6.6
when last dequeue
operation is done on
the empty queue.
O
w
FIG. 6.8 A
The Figure 6.8B shows the same string entered into a queue character by character.
W O N T O N N O T N O W
Rear
Front
FIG 6.8B
One can see that if one pops the stack of Figure 6.8A and dequeues the queue of
Figure 6.8B, and compares the characters obtained, then if they are all the same,
then the string is a palindrome. This principle has been extended in the form of a
program in the Listing 6.5.
The main algorithm for the program is given below in the Figure 6.9.
Main Algorithm
Initialize expression counts
Read the first input line
While there are still lines to process
Increment the total number of strings
Echo print the strings to the output file
If the string is longer than the maximum length allowed
Increment the number of oversize string count
Write “String too long” to the output file
Else
Process the current string //See algorithm in Figure 6.8
If the string is not a palindrome
Increase the count of non-palindrome
Write “Not a Palindrome” to the output file
Else
Increase the count of palindromes
Write, “Is a Palindrome” to the output file
Write the summary information to the output frame
Topic
FIG.6 6.9
FIFO Queue Page 18 of 23
The code to process a string requires the use of queue and stack data structures. The
algorithm for that is given in the Figure 6.10 below.
Process the current string
Create a new stack
Create a new queue
For each character in the string
If the character is a letter
Change the character to a lower case
Push the character onto a stack
Enqueue the character onto the queue
Assume that the string is a palindrome and set the Boolean variable
stillpalindrome to true
While string is still palindrome and has more characters to process
Pop a character from the stack
Dequeue a character from the queue
If above two characters are not same
Set stillpalindrome to false
FIG. 6.10
Listing 6.5 below shows the code for the class Palindrome, which when run takes
the input and output file name on the command line. As indicated before the
program reads the strings from the input file and outputs them to an output file
indicating whether the string is a palindrome or not, or too long to process.
//----------------------------------------------------------------------------
// Palindrome.java
//
// Checks for palindromes
// Input file consists of a sequence of strings, one per line
// Output file contains, for each string:
// Whether or not the string is a palindrome ... blanks are ignored
// Input and output file names are supplied by user through command line
parameters.
// Output frame supplies summary statistics
//----------------------------------------------------------------------------
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.text.DecimalFormat;
stillPalindrome = true;
charCount = 0;
if (!stillPalindrome)
{
nonPalindromes = nonPalindromes + 1;
outFile.println(" Not a palindrome ");
}
else
{
palindromes = palindromes + 1;
outFile.println(" Is a palindrome.");
}
}
outFile.println();
line = dataFile.readLine(); // set up processing of next line
}
dataFile.close();
outFile.close();
// set layout
infoPanel.setLayout(new GridLayout(5,1));
// show information
outputFrame.show();
}
}
Listing 6.5
Figure 6.11 shows the output file, which is obtained because of processing the input
file shown in the Figure 6.7.
String 005: a
Is a palindrome.
FIG. 6.11
The program does not process the strings larger than 180 characters. For all other
strings ignoring space, punctuations and capitalization, it determines whether a
particular string is a palindrome or not. Then it prints the string number, the string,
and the results of the processing (Figure 6.11). The program also outputs a frame
showing the summary of overall processing results (Figure 6.12).
FIG. 6.12
Microsoft PowerPoint
Presentation
FIG. 7.1
In Figure 7.1 the statement
int ival = 5;
creates an atomic or simple container which has a name ival and it stores a value
of five in it. On the other hand, the statement
String Name;
creates another atomic or simple type container called “Name”, whose contents
are unknown but it can store the address of another structured container of type
String. Once “Name” is assigned a value, it will store the address of structured
container it is assigned to point to. This is shown in Figure 7.2.
FIG. 7.3
The structured containers can also be totally empty, whereas the atomic
containers can never be – or at the very least they will contain some unknown
value (Figure 7.1). Figure 7.4 shows an example of empty structured container.
FIG. 7.4
The atomic container Cls1 stores the address of the empty structured container
of type SomeClass. In java an array is also a structured container. The Figure
7.5 shows the example of an array containing only simple or atomic containers.
Finally we show an example of a class which has instance variables as fields. The
definition of class Person is given in Listing 7.1.
public class Person
{
private int age;
private String Name;
public Person(int data1, String Name1 )
{
age = data 1;
Name = Name1;
}
}
Listing 7.1
FIG. 7.6
In Figure 7.6 once the constructor call
Person Aperson = new Person(23, P_Name);
is complete, then the “Name” container of Aperson stores the address of a String
type object or structured container, which stores string “Randy”. The container
age contains a value of 23. Notice the linkages that Aperson points to nameless
container, which contains “Name, which in turn points to a container which
stores “Randy”. The linkages allow us to travel between the containers and
access the data, subject to the restrictions placed by the access modifiers for the
class fields. Understanding the linkages between the references is the key skill to
understand linked lists. The Figure 7.7 summarizes the conclusions drawn from
the discussions above.
import javax.swing.*;
public class IntList Stores a reference to
{ an IntList type
//Instance variables object.
private int data;
private IntList Link;
//Chained Constructors
public IntList()
{
this(0);
}
FIG. 7.8 A
FIG. 7.8 B
The program asks for input of integers by popping an input box similar to the
Figure 7.8 A. The data input continues unless user ends the input by typing word
Microsoft PowerPoint
Presentation
Dummy
Node.
FIG. 7.9 A
The program statement
IntList Head = new IntList (0,null);
creates a node of type IntList, with the data field being equal to zero and the Link
field being set to null. The reference Head points to this node. The statement
IntList Iterator = Head;
sets the IntList type reference Iterator to point to the same node where the reference
called "Head" is pointing. Notice that the node with value zero is the dummy node
in the list. It is a member of the list, but we ignore it and do not use it.
When a node is created, at that instant, the list may have only dummy node in it or
it may have many members. The task at that point is to travel the list and find the
last node, to which we can attach the newly created node. The traveling of the list is
done by a while loop which is executed as illustrated by Figures 7.9C to 7.9E .
In Figure 7.9C first the Iterator is set to point to where Head node is pointing. This
is to be done every time when the loop is executed to get user input.
FIG. 7.9 C
The first node is schematically shown to have an address of 1000, therefore both
references Head and Iterator store the value of address 1000 in them. (See also
Figure 7.9 A). Then program does the loop pretest,
while (Iterator.Link !=null)
In Figure 7.9 D the Iterator.Link and Head.Link , both refer to the Link part of the
dummy node’s Link data member, which is not null. Rather it is schematically
shown to store an address of 2000. Therefore, the loop pretest condition is true and
loop is entered.
FIG 7.9 D
When the loop is entered the statement
Iterator = Iterator.Link;
is executed. This moves the Iterator to point to the next node (Figure 7.9E).
Dummy
Node.
FIG 7.9 E
Dummy
Node
FIG. 7.9 F
Now the loop pretest condition evaluates to false and loop is exited. We have now
located the node, to which the new node generated in the figure 7.9B can be
attached. The program executes the statement
Iterator.Link = Node;
And the new node is attached to the list as the Link member of the last node no
longer points to null. Rather it now stores 5000, the address of the new node. (Figure
7.9G).
FIG. 7.9 G
Printing the Linked List
The movement of Iterator to print the linked list is similar to when we built the
linked list. First, we set the Iterator to point to same node where head is pointing.
Then we enter a loop and we do the same loop pretest,
while (Iterator.Link !=null)
Loop tasks now are different. Inside the loop, we build an output string as
Output+=(Iterator.Link).toString ( );
The toString ( ) method of IntList class returns a String which merely prints the
data member of class IntList and adds a line feed. Then the statement below does
loop update
Iterator = Iterator.Link;
Loop executes until the loop update places the Iterator at the last node. However,
before than happens the data member of the last node is already added to the output
string.
/**
* Function: Observes the List to see if it is empty or not.<BR>
* Effect: returns true if List is empty else returns false.<BR>
* @return true if <code>List</code> is empty else returns false.
*/
public boolean isEmpty();
/**
* Function: Observes and determines the number of objects in the <code>
* List</code> container<BR>
* Effect: Returns the number of elements in the List<BR>
* @return The number of elements in the list as integer value.
*/
public int size();
/**
* Function: Transforms the <code>List</code> by adding an object to its
front<BR>
* Effect: List size is increaded by one<BR>
* Postcondition: List size = List Size + 1 <BR>
* @param obj added to the front of the <code>List</code>
*/
public void addFront(Object obj);
/**
* Function: Transforms the <code>List</code> by adding an element to
* its rear.<BR>
* Effect: List size is decreaded by one<BR>
* Postcondition: List size = List Size + 1 <BR>
/**
* Function: Transforms the <code>List</code> by removing an element from
* its front<BR>
* Effect: List size is decreaded by one<BR>
* Postcondition: List size = List Size - 1 <BR>
*/
public void removeFront();
/**
* Function: Transforms the <code>List</code> by removing an element from
* its rear<BR>
* Effect: List size is decreaded by one<BR>
* Postcondition: List size = List Size - 1 <BR>
*/
public void removeRear();
/**
* Function: Observes the <code>List</code> and returns the front
* element without altering the list.<BR>
* Effect: The <code>List</code> is unaltered. The front element is
* returned.NoSuchElementExceptionis thrown if <code>List</code> is empty.
* @return the front element with out altering the <code>List</code>
* @exception NoSuchElementException
*/
public Object front() throws NoSuchElementException;
/**
* Function: Observes the <code>List</code> and returns the rear
* element without altering the list.<BR>
* Effect: The <code>List</code> is unaltered. The rear element is
* returned. NoSuchElementException is thrown if <code>List</code> is empty.
* @return the rear element with out altering the <code>List</code>
* @exception NoSuchElementException
*/
public Object rear()throws NoSuchElementException;
/**
**
* Function: Creates a facility to iterate through the <code>List</code><BR>
* Effect: The <code>List</code> is unaltered. An Iterator ( a java class)
* is returned to iterate through the <code>List</code><BR>
* @return Iterator to the <code>List</code>.
/
public Iterator elements ( );
Listing 7.3
Node class:
A linked list class that would implement the List interface (Listing 7.3) would need
Node objects to be its data member. In our IntList, class the node consisted of an
integer data and an IntList type of reference. However, we need the linked list
classes we plan to develop to be of purpose that is more general. Therefore, it makes
sense to have the data member as an Object. This is accomplished by creating a
class called Node, whose field and constructor summary is given in Table 7.2. The
Node class has no methods as none are needed.
Field Summary
private item
java.lang.Object The field item is a java Object Type.
private Node next
The field next is the reference to another
Node, thereby making a Node as a self
referential structure.
//constructors
/**
* The argument-less constructor will initialize the item and link field
* both to null.
*/
public Node()
{
this(null);
}
/**
* One argument constructor will take a list item as an argument
* and the item field of the Node will be initialized to it. The next
* field is set to null.
* @param init_item is the initial value of item.
*/
Field Summary
protected front
SinglyLinkedList.Node
protected int numberElements
protected rear
SinglyLinkedList.Node
Constructor Summary
SinglyLinkedList()
Figure 7.10 shows the relationship between List interface and the class
SinglyLinkedList in the UML diagram.
import java.util.*;
import java.io.*;
import javax.swing.*;
public class SinglyLinkedList implements List{
//The Node is declared as an inner class with protected data
//members and constructors
protected class Node implements Serializable{
/**
* The field item is a java Object Type. This allows the
classes
* using Node as data member to store any Java object.
*/
protected Object item;
/**
* The field next is the reference to another Node, thereby
making a Node
* as a self referential structure.
*/
protected Node next;
//constructors
/**
* the argument-less constructor will initialize the item
/**
* One argument constructor will take a list item as an
argument
* and the item field of the Node will be initialized to
it. The next
* field is set to null.
* @param init_item is the initial value of item.
*/
protected Node(Object init_item)
{
this(init_item, null);
}
/**
* Two argument constructor, constructs Node with the
specified
* item and link to another Node.
* @param init_item is the initial value of item.
* @param link is the initial value of next.
*/
protected Node(Object init_item, Node link)
{
this.item = init_item;
this.next = link;
}
}//end of inner class Node
//Other fields
protected Node front;
protected Node rear;
protected int numberElements;
if(this.numberElements == 0)
rear = null;
}
}
while(previous.next != rear)
previous = previous.next;
previous.next = null;
rear = previous;
}
this.numberElements--;
}
while(current != null)
{
V.addElement(current.item);
current = current.next;
}
return V.iterator();
}
/** Insert obj after target object in the list */
public void addAfter(Object obj, Object target)
{
Node itemNode = getNode (target);
if(itemNode == null)
throw new NoSuchElementException(
"From addAfter method. The target node does not exist\n");
else if (!this.contains(obj)){
Node newNode = new Node (obj, itemNode.next);
// Queries
// Internal methods
/**
* For internal use only.
* This function, available only within this class
* returns the node associated with value. If value is
* not present in the list getNode returns null
*/
protected Node getNode (Object value) {
Node node = front;
Node result = null;
while (node != null) {
if (node.item.equals(value)) {
result = node;
break;
}
node = node.next;
}
return result;
}
/**
* For internal use only.
* This function, available only within the class
* returns the node just before someNode. If someNode is null or
* the only node present in the list, this function returns null.
*/
protected Node nodeBefore (Node someNode) {
if (someNode != null && someNode != front) {
Node previous = front;
while (previous.next != someNode)
previous = previous.next;
return previous;
}
else
return null;
}
}
//Listing 7.5
Microsoft PowerPoint
Presentation
FIG. 7.11
In the condition shown in Figure 7.11, the front is storing an address of 1000, so it is
not null. Therefore, the loop is entered. Inside the loop a fresh Node called previous
is created to point to where front is pointing (Figure 7.12).
FIG. 7.14
FIG. 7.15
Then the garbage collector collects the first node. This process is repeated two more
times to delete the second and third node (Figure 7.16).
FIG. 7.16
FIG. 7.17
Next two program statements set the front and previous both to null. The loop is
exited, but the reference rear is still pointing to the last node. The first statement
outside the loop sets the rear to null and the last node is then garbage collected
(Figure 7.18).
Microsoft PowerPoint
Presentation
When an object is to be added to the list, then at that time list may be empty or it
may not be. When the method addRear ( ) is invoked, first a newNode is created
which has the Object obj as its data member item and the next field of newNode is
set to null (Figure 7.19).
FIG. 7.20
The next statement sets the rear reference to point to newNode as well (Figure 7.21).
FIG. 7.22
The first statement in the else clause makes the rear.next point to where newNode is
pointing (Figure 7.23).
FIG. 7.24
Method removeRear ( )
Method removeRear ( ) takes no arguments and simply removes the rear element
from the list. The PowerPoint presentation below shows the mechanism of method
removeRear ( ) dynamically.
Microsoft PowerPoint
Presentation
The method has three if / else blocks. Obviously if list is empty, then there is no
element available for removal. Thus, the first block throws a
NoSuchElementException (Figure 7.25).
FIG. 7.25
Now the removal scheme is different depending upon whether the list has only one
element or more than one. If list has only one element, then the block 2 is entered
(Figure 7.26).
FIG. 7.26
In block 2, the front is set to null and then rear is set to null. These two executions
remove all the references from the only node in the list, and the node is garbage
collected (Figure 7.27).
FIG. 7.27
If the list has more than one element than the block 3 is entered (Figure 7.28).
FIG. 7.29
FIG. 7.30
The purpose of the loop is to set the reference previous at the node before the last
node. In Figure 7.30, the reference previous.next is not at the last node. Therefore,
the loop is entered. Inside the loop, the reference previous is moved by one node
(Figure 7.31).
Method elements ( )
The method elements ( ) returns an Iterator to the linked list, which can be used to
traverse the list. Java has an interface called Iterator which has the methods
summarized by the table 7.4.
Method Summary
boolean hasNext()
Returns true if the iteration has more elements.
Object next()
Returns the next element in the iteration.
void remove()
Removes from the underlying collection the last
element returned by the iterator (optional operation).
Table 7.4
The method hasNext ( ) returns a true if the class implementing the Iterator
interface has more elements as the collection of elements in the class is iterated. The
method next ( ) returns the next element in the collection as an Object. Method
remove can remove the last element returned by the Iterator. Proper exceptions are
The elements( ) method which implements the above algorithm is shown below in
the Figure 7.33.
Method elements ( )
Vector V
declared.
public Iterator elements( ){
Vector V = new Vector( );
Node current = front; All SinglyLinkedList
while(current != null) { elements loaded into the
V.addElement(current.item); Vector V
current = current.next;
}
return V.iterator( ); Method iterator returns an
Iterator to Vector V.
}
FIG. 7.33
The Iterator returned by the method elements ( ) can now invoke the methods listed
in Table 7.4 on the SinglyLinkedList.
As shown by Figure 7.34 the method addAfter ( ) has three if /else if /else blocks.
FIG. 7.34
Inside the method, first the method getNode ( ) is called with target node reference
as an argument. itemNode would be null if target node does not exist. Otherwise,
itemNode will actually be the reference to the target node. If itemNode is null then
first block is executed, which throws a NoSuchElementException .
The second block tests whether the node to be added, obj, is already in the list. If
node is not in the list, then program execution enters the block2. The first statement
in block2 creates a new node called newNode, which takes obj as its item field and
itemNode.next as its next field (Figure 7.35).
If by some chance, the target not would be the last node in the list, then the rear
reference would still be pointing to the target node (Figure 7.37).
MethodaddBefore.pp
t
The program enters block2 if the node obj is not in the list. The first statement in
the block2 creates a new node, which takes the obj for its item field and its next field
points to target or itemNode. Remember that itemNode is a reference to the target
node (Figure 7.40).
System.exit(0);
}
}
Listing 7.6
The beauty of Object based linked list lies in the fact that in order to add, remove,
or print an element in the list we do not have to worry about its data type. Almost
all objects can be added, removed or printed easily. The first while loop adds the
objects to the front of the list, whereas the second one to the rear. We then get an
Iterator to the list and print it. Finally, we test few other list methods for their
User enters
strings:
best , is, list,
This and
then clicks
cancel.
FIG. 7.45B
Figure 7.45C shows the resulting list.
FIG. 7.45C
FIG. 7.45D
FIG. 7.45E
FIG. 7.45F
Linked Stack
In order to design the linked version of stack, we could implement the interface
StackInterface, the way we did in the array version. There is one difference,
however. Since linked version does not have an upper limit on the size, we may not
need the method isFull ( ) and the class StackOverflowException. Therefore, our
design of StackInterface must change slightly. This altered design is given in Listing
8.1.
public interface StackInterface
{
public void push(Object item);
// Effect: Adds item to the top of this stack.
// Postcondition: If (this stack is full)
// an unchecked exception that communicates
// 'push on stack full' is thrown
// else
// item is at the top of this stack.
//constructors
/**
* the argument-less constructor will initialize the item and link field
* both to null.
*/
protected Node()
{
this(null);
}
//Other fields
protected Node top = null;
protected int size = 0;
}
Listing 8.2
Compared to the array implementation, we add an extra field size in the
LinkedStack class, to indicate the size of stack at a particular time. We also add the
method getSize ( ), which returns the size of the stack. The Listing 8.3, class
TestLinkedStack.java tests the functioning of linked stack.
import javax.swing.*;
public class TestLinkedStack
{
public static void main(String [ ] args)
{
LinkedStack Stk1 = new LinkedStack();
boolean done = false;
String Input = "";
while(!done)
{
Input = JOptionPane.showInputDialog("Please enter the "
+ " string, characters and numbers to push on the stack " +
" Click cancel when done.");
if(Input== null)
done = true;
else
{
Figure 8.1
"Method push.ppt"
After entering the required data the user clicks, cancel button to stop data entry. In
the call to push ( ) method, each time an item is pushed on to the stack, first a
newNode is created as
Node newNode = new Node (item, top);
Figure 8.2
At that point the next field of newNode stores a null as top also stores a null. Then
next program line makes the top point to where newNode is pointing (Figure 8.3).
Figure 8.3
If stack already has some elements in it, then the situation may look like Figure 8.4.
Figure 8.4
The next field of newNode will then point to where top reference is pointing. Then it
is just a matter of making the top reference point to where newNode is pointing
(Figure 8.5).
The main method in class TestLinkedStack prints the list size after the list is built.
(Figure 8.6).
FIG. 8.6
The top element of the stack is then displayed (Figure 8.7). Since the element 2002
was added last, that is the one displayed call to method top ( ) (Figure 8.7).
FIG. 8.7
FIG. 8.8
Since the word Santa Monica was added as a unit the first, it is the last element on
the stack. The last pop statement will throw a StackUnderflowException.
//constructors
/**
* the argument-less constructor will initialize the item and link field
* both to null.
*/
protected Node()
{
this(null);
}
/**
* One argument constructor will take a list item as an argument
* and the item field of the Node will be initialized to it. The next
* field is set to null.
* @param init_item is the initail value of item.
*/
protected Node(Object init_item)
{
this(init_item, null);
}
/**
* Two argument constructor, constructs Node with the specified
* item and link to another Node.
* @param init_item is the initial value of item.
* @param link is the initail value of next.
*/
protected Node(Object init_item, Node link)
//Other fields
int getSize()
{
return this.numItems;
}
}
Listing 8.5
The class LinkedQueue has three fields, which represent the number of elements in
the queue at any time and the reference to the front and rear node. Figure 8.8 shows
the schematic view of the Linked Queue with three elements in it.
Schematic View of Linked
Queue
3000
2000
1000
3 null
2 3000
1 2000
item next
item next
item next
1000
front
3000
numItems = 3 rear
FIG. 8.8
Obviously, the blank queue with zero elements in it will have the front, rear
references set to null, and numItems set to zero. Let us now discuss the methods
enqueue and dequeue for the LinkedQueue data structure. The following
PowerPoint presentation displays the dynamics of the method enqueue.
The method enqueue ( ) has two blocks as if and else control structure (Figure 8.9).
FIG. 8.9
The block#1 is executed if the LinkedQueue is empty. Otherwise the block#2 is
executed. When LinkedQueue is empty and program control is on the first
statement inside the block#1, then the situation is shown by Figure 8.10.
Method dequeue ( )
Method dequeue ( ) is required to throw a QueueUnderflow exception if the
LinkedQueue is empty. Otherwise, it must remove the element from the front of the
queue and readjust the front reference. If queue has only one element when dequeue
is called, then it must readjust the rear reference as well. The PowerPoint
presentation below shows that how the method dequeue ( ) works.
The method dequeue ( ) has two blocks of code embedded in if/else control structure
(Figure 8.14).
User enters
A October 21
2002 Day in
California
FIG. 8.20
Figure 8.21 prints the size of the queue.
FIG. 8.22
Deque
Deque is a double-ended queue data structure. In contrast to FIFO queue, deque
allows addition or removal of elements from both front and rear. This property
allows the deque to be used either as a FIFO queue or as a stack. To use a deque as a
FIFO queue, one can add the elements to rear and remove from front. However, to
use the deque as a stack one can add and remove only from front of rear. The Deque
class can be written as a restricted Singly Linked List. If we remove all the methods
from SinglyLinkedList class we discussed in topic seven, which add, or remove
elements at any location other than the front and rear, then we end up with the code
for a Deque. Listing 8.7 shows the code for the Deque class.
import java.util.*;
import java.io.*;
public class Deque implements List
{
//The Node is declared as an inner class with protected data
//members and constructors
protected class Node implements Serializable
//constructors
/**
* the argument-less constructor will initialize the item and link field
* both to null.
*/
protected Node()
{
this(null);
}
/**
* One argument constructor will take a list item as an argument
* and the item field of the Node will be initialized to it. The next
* field is set to null.
* @param init_item is the initail value of item.
*/
protected Node(Object init_item)
{
this(init_item, null);
}
/**
* Two argument constructor, constructs Node with the specified
* item and link to another Node.
* @param init_item is the initial value of item.
* @param link is the initail value of next.
*/
protected Node(Object init_item, Node link)
{
this.item = init_item;
this.next = link;
}
}//end of inner class Node
//Other fields
if(this.numberElements == 0)
rear = null;
}
}
while(previous.next != rear)
previous = previous.next;
previous.next = null;
rear = previous;
}
this.numberElements--;
}
while(current != null)
{
V.addElement(current.item);
current = current.next;
}
return V.iterator();
}
}//end of class
Listing 8.7
The restricted nature of deque as a limited Singly Linked List is clear from the code
in Listing 8.7. Only transforming methods left are the addFront ( ), addRear ( ) ,
removeFront ( ), and removeRear ( ). The Deque can be used as a stack by using
either the methods addFront ( ) and removeFront ( ) or addRear ( ) and
removeRear ( ). It can also be used as a FIFO queue by using combination of
addRear ( ) and removeFront ( ) methods. Listing 8.8 shows a class and
corresponding code to test the Deque as a stack and FIFO queue both.
}
Listing 8.8
Note that it become client’s responsibility to call the methods addFront ( ),
addRear ( ), removeFront ( ) and removeRear ( ) in proper combination to use the
Deque either as a Stack or as a FIFO queue.
FIG 8.24
The node “Jones” points to nodes “Baker” as well as to “Smith”. In order to remove
node “Jones”, we would need to have the next field of “Baker” point to “Smith”,
and have the before field (the left arrow) of “Smith” point to “Baker”. Then we
would need to break the linkage of “Jones” with both “Baker” and “Smith” (Figure
8.25).
FIG. 8.25
The similar deletion in a singly linked list will require breaking or establishing only
half as many links. The corresponding reference changes when inserting a node in a
doubly linked list are shown in Figure 8.26.
FIG. 8.26
Listing 8.9 shows the code for a doubly linked list class and a main method to test
the key class methods.
import java.util.*;
import java.io.*;
import javax.swing.*;
public class DoublyLinkedList implements List
{
//The Node is declared as an inner class with protected data
//members and constructors
protected class Node implements Serializable
{
//constructors
/**
* the argumentless constructor will initialize the item and link field
* both to null.
*/
protected Node()
{
this(null);
}
/**
* One argument constructor will take a list item as an argument
* and the item field of the Node will be initialized to it. The next
* field is set to null.
* @param init_item is the initail value of item.
*/
protected Node(Object init_item)
{
this(init_item, null);
}
/**
* Two argument constructor, constructs Node with the specified
* item and link to prdecessor Node.
* @param init_item is the initial value of item.
* @param link_prev is the initail value of before.
*/
protected Node(Object init_item, Node link_next)
{
this(init_item,link_next, null);
}
/**
* Three argument constructor, constructs Node with the specified
//Other fields
protected Node front;
protected Node rear;
protected int numberElements;
/** Insert obj after target object in the list */
public void addAfter(Object obj, Object target)
{
Node itemNode = getNode(target);
if (itemNode==null)
throw new NoSuchElementException("addAdter: The node
does not exist");
else
{
// Backlink to itemNode and forward link to itemNode.next
Node newNode = new Node (obj, itemNode.next, itemNode);
if (itemNode.next != null)
itemNode.next.before = newNode;
else
rear = newNode;
itemNode.next = newNode;
numberElements++;
}
}
// Queries
// Internal methods
/**
* For internal use only.
if(this.numberElements == 0)
rear = newNode;
else
front.before = newNode;
front = newNode;
//Increase the number of elements by one
this.numberElements++;
}
front = front.next;
this.numberElements--;
if(this.numberElements == 0)
rear = null;
}
}
this.numberElements--;
}
while(current != null)
FIG. 8.27
For example for the method in Listing 9.2, the parameter Num would have a
sentinel value of zero. When Num reaches a zero value, the recursive call is not
made. Rather at that point, the method must return the factorial of zero, which is
one. We can make the Listing 9.2 a well-behaved recursion by incorporating the
property #2 and re-writing it as Listing 9.3.
Only executed for
public int factorial(int Num)
non-sentinel values of
{
if(Num > 0) Num
return Num * factorial(Num – 1);
else
return 1; Argument to the method factorial ( ) reduces by
} one with every recursive call, thereby each time
bringing it closer to the sentinel value of zero.
Listing 9.3
In Listing 9.3, the property #2 is enforced by testing for a sentinel value for the
parameter Num. Method factorial calls itself only for the non-sentinel values for the
parameter Num. At the same time, with each recursive call, the argument to the
method is brought closer to the sentinel value of zero, since the recursive call is
made with argument Num-1.
Property # 3
The third property for a well-behaved recursion is closely related to the property #2
and has already been included in the Listing 9.3. It can be stated as: A recursive
method, algorithm, or implementation must drive the value of its sentinel parameter
in a way to eventually stop the recursion.
The property #3 is applied in Listing 9.3, because the each recursive call to
factorial ( ) is made with the value of Num reduced by one, pushing the parameter
Num successively towards the sentinel value of zero. When the Num reaches the
value of Zero, the else part of the method factorial ( ) is executed and recursion is
stopped. Sometimes the sentinel value is also referred to as a base case.
Recursion Level: Each recursive method call has its own level. The first recursive call
is when we enter the recursion at level one, the second call at level two and so on.
Going in/Backing out: When we enter the recursion at certain level we call that
“going in”. Entry to each subsequent level is going in at a deeper level. The “going in”
stops when the sentinel or base case has been reached. Then the process of “backing
out” from the piled up recursion calls begins. A well-behaved recursion goes into n
levels (one level at a time) and then “backs out “ of it (one level at a time).
void main()
{
int Num = 0;
cout<<"Please enter the positive integer, whose factorial you need ";
cin>>Num;
int result = factorial(Num);
cout<<"The factorial of "<<Num<<" is "<<result<<endl;
}
Listing 9.4
Figure 9.1 shows the program status, just before the call to function factorial is
made from the main ( ). The bottom left pane shows the variable values, and the
function call stack is shown on the bottom right pane.
FIG. 9.2
The function call stack on the bottom right pane clearly shows that the control is
now transferred to function factorial. Figure 9.3 shows the situation, when we are
ready to “go into” the second level of recursion.
FIG. 9.3
FIG. 9.4
Going in at level 3.
Num = 2
Going in at level 4.
Num = 1.
Going in at level 5.
Num = 0. The
sentinel is reached.
FIG. 9.10
In process of backing out, each return value is used at each level on the stack that
was there while going in. For example the level 4 which had Num = 1, gets a return
value of one as we back out from level 5 (Figure 9.11).
The figures 9.12 to 9.14 show the state where the recursion is ready to back out of
levels 3, 2, and 1. The return value keeps changing at each level.
When the recursion finally backs out of level one, the program control is given back
to main (Figure 9.15) and a value of 24 for the factorial of four is returned.
Recursion
level
FIG. 9.16
In Figure 9.16, the call number on vertical left axis gives us the level of recursion.
The downward arrows from top left corner to the bottom right corner indicate the
progress of “going in” into the recursion. The arrows from bottom right towards the
top left corner indicate the direction of “backing out” from the recursion, when at
level five, the sentinel has been reached for the parameter number.
Double Recursion
When a recursive method makes two recursive calls to itself that is called a double
recursion. In Listing 9.8, inside class Recursion2, we have two methods,
inOrderPrint ( ) and permute ( ) which include two calls to themselves.
/** A simple class to illustrate double recursion*/
import java.io.*;
if (size > 1)
{
permute(inArray, index);
for (int i = index - 1; i >= 0; i--)
{
temp = inArray[index];
inArray[index] = inArray[i];
inArray[i] = temp;
permute(inArray, index);
temp = inArray[index];
inArray[index] = inArray[i];
inArray[i] = temp;
}
}
else
{
for (int j = 0; j < inArray.length; j++)
System.out.print(inArray[j]);
System.out.println();
}
}
IOP1/2 = 0)
IOP(3/2 = 1)
IOP(7)
IOP(7/2 = 3)
prints
prints prints Index = 1
Index = 7 Recursion
Index = 3 stops
IOP(1/2 = 0)
IOP(7/2 = 3)
IOP(1/2 = 0)
IOP(3/2 = 1)
prints
Index = 1
IOP(1/2 = 0)
FIG. 9.17
In Figure 9.17, the recursive calls to inOrderPrint ( ) are shown from left to right
and progress is shown with red arrows. Green arrows show the process of backing
out of recursion. The calls to method inOrderPrint ( ) is abbreviated as IOP ( ).
When first call is made to inOrderPrint ( ) with an argument of seven, the call is
“going in “ the recursion, shown by top left box IOP( 7 ). Since seven is greater than
IOP1/2 = 0)
IOP(3/2 = 1)
IOP(7)
IOP(7/2 = 3)
FIG. 9.18
Now the first set of recursion has stopped, therefore the process of backing out of it
must begin. Note that in “backing out”, the program must get
back to the code line where it left to pursue recursion! This
means that the backing out must begin by executing the code
line:
System.out.println ("Index = " + index);
The green arrows show the process of backing out (Figure 9.19). Since the index was
(3/2 = 1) one when the last recursion was done, the program first prints “Index = 1”.
Following the printing of “Index = 1”, the next statement makes the downward
recursive call to inOrderPrint with an argument of (1/2 = 0). This call is shown
IOP1/2 = 0)
IOP(3/2 = 1)
IOP(7)
IOP(7/2 = 3)
prints
prints prints Index = 1
Index = 7 Index = 3
IOP(1/2 = 0)
FIG. 9.19
by the blue box with IOP (1/2 = 0 ) in it. This recursion also stops, since method
argument is zero. Then in process of backing out of inOrderPrint (3/2), shown by
top blue box, the program gets back to where it left of to make recursive call
IOP(7/2 = 3). Then the statement
IOP(7/2 = 3)
prints
Index = 3
IOP(1/2 = 0)
IOP(3/2 = 1)
prints
Index = 1
IOP(1/2 = 0)
FIG. 9.20
The recursive call after the print statement is made with an argument of three
shown by the amber box with IOP(3/2 = 1) in Figure 9.20. This will lead to a
recursive call inOrderPrint (1/2 = 0). This is shown with the box right to the box
with IOP(3/2 = 1) in it. That again stops the recursion. In backing out from the
recursion shown by the rightmost red box in Figure 9.20, one gets back to the
program line where it left. That leads to backing out and printing “Index = 1”
shown by bottom most amber box in Figure 9.20. The further recursive call with the
argument IOP (1/2 = 0) shown by the bottom most red box stops the recursion
again.
Program then backs out of the recursive call IOP(7/2 = 3), made by top amber box
in Figure 9.20. The Figure 9.21 shows this process separately.
IOP(7)
IOP(7/2 = 3)
prints prints
Index = 7 Index = 3
FIG. 9.21
IOP(7/2 = 3)
Examples of Trees:
Most common example of a tree data structure is a directory tree in a Windows or
Unix filing system. Starting from the root directory, the folders with in folders span
the directory tree. (Figure 10.1)
Level 1
FIG. 10.1
Level 2
Level 3
Because of inheritance relationship, the classes in java are also arranged in a tree-
like manner. One example of the tree-like nature of java classes is shown in Figure
10.2.
FIG. 10.2
FIG. 10.3
From the examples shown before, we notice that in tree data structure the root is on
the top. Therefore, one can look at it as an inverted tree. Figure 10.4 shows a generic
tree where each node is marked by an alphabet.
B D
C
FIG. 10.4
E F G H J
Root: The root of a tree is the node, which has no parent or no predecessor. In Figure 10. 4,
the node A is the root of the tree.
Children Nodes: All nodes other than the root node are also children nodes because they
have parent or predecessor.
Parent Node: A node that has children is called a parent node. In Figure 10.4, the nodes A,
B, and D are parent nodes.
Note that a parent node can be a child to another node. For example, the node B
is a child of A, but parent of E and F. In a generic tree, a parent node can have
any number of Children.
Siblings: The siblings are the children from same parent node. Nodes B, C, and D are
siblings.
Leaf Nodes or External Nodes: The nodes without children are called leaf nodes. The
nodes C. E. F. G. H, and J are leaf nodes.
Internal Node: A node that has at least one child is called internal node. Nodes A, B, and D
are internal nodes.
Degree of a Node: Degree of a node is the number of children the node has. For example,
the degree of node A is three.
Level or Depth of a Node: The numbers of edges to be traversed from the root to reach a
node indicate its level or depth. The level of root node is zero. The level of node E is two.
Height of tree: The maximum depth or levels possible in a tree give its height. The height of
tree in Figure 10.4 is two.
TreeDefinitions.ppt
Every node of a tree may be considered a sub-tree. For example, Figure 10. 5 and
10.6 show left and right sub-trees of the root node for the tree of Figure 10.3.
FIG. 10.5
Binary Tree:
A general tree shown in Figure 10.4 is simplified further by introducing a
constraint, that each node in the tree can only have maximum, two children. This
results in a binary tree. Figure 10.6 shows a typical binary tree.
Right
Left child Binary Tree child of A
of A
A
null
B C
FIG. 10.6
D
E F Leaf nodes.
All leaf nodes
have two null
G H J children.
Listing 10.1
In-order Traversal
In words, the process of in-order traverse is as follows:
1. Traverse the left sub-tree of a node
2. Visit the node itself
3. Traverse the right sub-tree of the node.
The algorithm for in-order travel is as follows:
traverseInOrder (left sub-tree)
visit (node)
traverseInOrder (right sub-tree)
The java applet shown on the Internet link below shows the dynamics of in-order
travel for a binary tree.
Tree Traverse Java Applet
We cannot go to step 1.32, unless we complete the step 1.31 first. We expand the step
1.31 as follows:
1.311 Traverse left sub-tree of G of LG = { null } . So traversal stops.
1.312 Visit G. This means print G if that is what the visit involves.
1.313 Traverse right sub-tree of G of RG = { null }. So traversal stops.
All the sub-steps from step one are now completed. So now, we go to step 2, which is
simply:
2. Visit A. This means print A if that is what the visit involves.
Now we go to step 3 which included traversing the sub-tree C. The step 3 will break
down as follows:
We have now completed the step 3.31, so we can now process to step 3.32.
3.32. Visit F. This means print F if that is what the visit involves.
Now we complete the step 3.33. The step 3.33 expands as follows:
3.331 Traverse left sub-tree of J, which is null, so the traverse stops.
3.332 Visit J. This means print J if that is what the visit involves.
3.333. Traverse right sub-tree of J, which is null, so the traversal stops.
If we distill the visit order for the in-order traversal then applied to binary tree of
Figure 10.6, we get the following order.
DBGEACHFJ
Figure 10.7 on next page shows the process of in-order traverse for the binary tree
of Figure 10.6 graphically.
The link below shows the animated GIF for in-order travel.
inorder.html
IOT( sub E)
IOT(sub G) IOT(null)
IOT( sub C) IOT(null)
Visit or IOT(null)
Visit or Print E Visit or
Print C Print G
IOT(null)
IOT(null)
IOT(sub F)
IOT(sub H) IOT(null)
Visit or
Print F Visit or
Print H
inOrderTravel (left sub-tree)
IOT( sub J)
IOT(null)
Visit the node
inOrderTravel (right sub-tree)
Visit or
IOT(null)
Print J DBGEACHFJ
IOT(null)
Binary Tree
A
null
B C
D
E F
G H J
The process described above continues and the in-order traverse yields a print order
or visit order as: D B G E A C H F J.
Post-order Traversal
In words the post-order traversal can be described as follows:
1. Traverse left sub-tree of a node.
2. Traverse right sub-tree of a node
3. Visit the node.
The post-order traverse algorithm is as follows:
traversePostorder (left sub-tree)
traversePostorder (right sub-tree)
Visit the node itself.
The java applet shown on the Internet link below shows the dynamics of post-order
travel for a binary tree.
Tree Traverse Java Applet
We can draw a figure similar to the Figure 10.7 to show the post-order tree
traversal. However, a slightly different pictorial representation may clarify the post-
order traversal better. Figure 10.8 A, and B shows the post-order traversal for the
binary tree of Figure 10. 6. The link below shows the animated GIF for post-order
traversal.
postorder.html
It is clear from the post-order algorithm, that for each leaf node or for a sub-tree
with no children, it simply results in visiting the node.
Binary Tree
A
null
B C
D
E F
G H J
POT(B)
POT(D) POT(E)
null POT(G)
null
null
Visit/print D null
null
Visit/print G
Visit/print E
Visit/print B
POT(C)
null POT(F)
POT(H)
POT(J)
null null
null null
Visit/print H
Visit/print J
Visit/print F
Visit/print C
FIG. 10.8 B
Topic 10 recursion Page 15 of 63
Visit/print A
In Figure 10.8 B, each colored ellipse is a copy of the post-order travel method
call. As we enter the deeper into inner ellipses, we enter into deeper levels of
recursion. The post-order algorithm requires that for each tree or sub-tree we
recursively traverse the left sub-tree, then the right sub-tree and then finally
visit the node, whose sub-trees were traversed. Referring to Figure 10.8 A and B,
we start with the root of tree the node A. This is shown by the call POT(A) in
Figure 10.8 B. The left sub-tree of A is B; therefore, we call traverse post-order
the sub-tree B (shown as POT (B)). However, the left sub-tree of B is D, so we
traverse post-order the sub-tree D (shown as POT (D)). Node D is a leaf node,
which has null left and right sub-trees. Therefore recursion stops. This puts the
program control on the last non-recursive line, which requires visiting the node
D or printing it. This completes the traversal of left sub-tree of B. Then the
second line of post-order traversal algorithm requires traversing the right sub-
tree of B. This is shown by the POT(E) in Figure 10.8 B. The sub-tree E has a
leaf node G as its left sub-tree. Therefore, the traversal of leaf node G results in
visiting or printing G next.
The right sub-tree of E is null, therefore the recursion stops, and the third
algorithm visits or prints the node E.
Now we have completed the right sub-tree of B, so the last line of algorithm visits
or prints the node B. This completes the traversal of the left sub-tree of A. Then
the second line of algorithm requires the post-order traversal of the right sub-
tree of A. This traversal is shown by the lower half of the Figure 10.8 B. This
traversal begins with the pre-order traversal of sub-tree C (POT( C ) ). The left
sub-tree of C is null, therefore the recursion stops in that direction. But the right
sub-tree of C is F, therefore we begin post-order traversal of F, shown by
POT(F). However, F has a left leaf node H, and POT(H) results in visit to node H
or printing it. Then the right sub-tree of F is a leaf node J, therefore POT(J)
results in visiting or printing J. After we finish the traversal of left and right
nodes of sub-tree F we visit or print the node F. Finishing POT(F) completes the
traversal of right sub-tree of C, and finally the last algorithm line visits or prints
C. The end of call POT( C ) finishes the traversal of sub-tree C, and then the last
line of algorithm visits or prints node A. Looking at all the highlighted node visit
sequence, for the post-order traverse, the tree traversal sequence is:
D G E B H J F C A. Using Figure 10.8 B, we can easily understand this
sequence if we just follow the blue and green algorithm progress arrows in
Figure 10.8 B. Each circle in Figure 10.8 B shows the scope of each recursive
method call. Obviously, the last step in each method call has to be visiting or
printing the node, which was passed as a method argument to begin with. The
post-order call to leaf nodes simply results in visiting or printing them. Number
of recursive call inside each circle depends upon the number of descendents each
node has.
Pre-Order Traverse
Binary Tree
A Pre-order Visit sequence:
ABDEGCFHJ
null
B C
D
E F
FIG. 10.9 A
G H J
POT(B
Visit or print B
POT(E
POT(D)
Visit or print E
POT(G)
Visit or print D
null
null
null
POT(C
POT(F)
Visit or print C
Visit/print F
POT(J)
null POT(H)
Visit/print H Visit/print J
FIG. 10.9 B
Now to complete the second recursive call using sub-tree B, we apply the algorithm
to the right sub-tree of B, which is E. This results in first visiting or printing E.
However, E has a left sub-tree G, which is a leaf node. The application of pre-order
algorithm to G simply results in visiting or printing G.
This completes the recursive call to the left sub-tree of A. Now we progress to the
last line of algorithm for the tree A, which includes applying the pre-order
algorithm to the right sub-tree of A, which is C. This results first in visiting or
printing C. However, C has null left sub tree, so we directly go to its right sub-tree,
which is traversing sub-tree F. This results in visiting or printing F. However, F has
a leaf node H as its sub-tree, which simply is visited or printed. In addition, the
process finally completes by visiting or printing the right leaf node of F, which is J.
Figure 10.10 shows various stages of inserting data values in the binary
search tree.
The first node to be added is 50, which is added as a root node (Figure
10.10A).
FIG. 10.10 A
The next node to be added is 40, which is less than 50. Therefore, the node 40
must be added as a left sub-tree to root node 50 (Figure 101.10B).
Added nodes 50
and 40
FIG. 10.10 B
Node 60 is added as the right sub-tree to 50, and node 70 is added as the right
sub-tree to 60. Node 20 is added as the left sub-tree to 40 (Figure 10.10C).
FIG. 10.10C
Following the procedure that starting from root node if the value to be added
is lower than any node then we keep traveling to left until we reach a leaf
node. If the value is lower than the leaf node then we add it to its left.
Otherwise, we add it to its right. The reverse procedure is to be followed if
the value is higher than the root node. This procedure completes the
construction of binary search tree (Figures 10.10 D and E).
Inserted all
items
except 90
and 15.
FIG. 10.10 D
FIG. 10.10 E
Class SearchTreeNode
A java class, which would follow the properties of a binary search tree node,
is given in Table 10.2 and Listing 10.2.
Field Summary
(package private) contents
java.lang.Comparable contents is the object in the SearchTreeNode, which
contains the data part of the node.
(package private) left
SearchTreeNode left is the self-referential reference pointing to the
left child of the node.
(package private) right
SearchTreeNode right is the self-referential reference pointing to the
right child of the node.
Constructor Summary
(package SearchTreeNode()
private) The argument-less constructor initializes all fields to null creating a
null tree.
(package SearchTreeNode(java.lang.Comparable obj)
private) The single argument constructor takes an object of type
Method Summary
(package private) isInternal()
boolean Tests whether a node is internal or not.
java.lang.String toString()
Over-rides super class java.lang.Object class method
toString( ) to return the string representation of contents.
Table 10.2
import java.io.*;
class SearchTreeNode implements Serializable // Has package access only
{
/**
• contents is the object in the <code>SearchTreeNode</code>,
• which contains
* the data part of the node.
*/
Comparable contents;
/**
* left is the self-referential reference pointing to the
* left child of the node.
*/
SearchTreeNode left;
/**
* right is the self-referential reference pointing to the
* right child of the node.
*/
SearchTreeNode right;
/**
* The argument-less constructor initializes all fields to null
creating a
* null tree.
*/
SearchTreeNode()
{
this(null);
}
/**
* The single argument constructor takes an object of type
Constructor Summary
TreeIterator(SearchTreeNode root)
The constructor takes the root - an object of type SearchTreeNode and places its
elements in the Vector v, in the order required by a particular Iterator.
Method Summary
protected buildVector(SearchTreeNode node)
abstract void Abstract method buildVector is to be implemented by
the subclass.
protected void finalize()
Method finalize removes all elements from the vector v
and prepares for the garbage collection.
boolean hasNext()
Returns true if the iteration has more elements.
java.lang.Object next()
Returns the next element in the iteration.
void remove()
The remove method from the interface java.util.Iterator
is not needed, so it has a null implementation.
Table 10.3
Listing 10.3 gives the java code for the abstract class TreeIterator.
import java.util.*;
import java.io.*;
protected Vector v;
/**
* Field index keeps the location up to which the elements from
* Vector v have been returned when calling the method next().
*/
protected int index;
/**
* Field size gives the number of elements in the Vector v
*/
protected int size;
/**The constructor takes the root - an object of type
* <code>SearchTreeNode</code> and places its elements in the
* <code>Vector</code> v, in the order required by a particular
* Iterator.
* @param root is the root node for the tree to which the
* Iterator is assigned.
*/
public TreeIterator(SearchTreeNode root)
{
v = new Vector();
buildVector(root);
v.trimToSize();
index = 0;
size = v.size();
}
/**
* The remove method from the interface java.util.Iterator is
* not needed, so it has a null implementation.
*/
public void remove()
{
//Not used. Null implementation
}
/**
* Returns true if the iteration has more elements.
* (In other words, returns true if method next( ) would return
an
* element rather than throwing an exception.)
* @return true if the iterator has more elements.
*/
public boolean hasNext()
{
return index<size;
}
/**
* Returns the next element in the iteration.
* @return the next element in the iteration.
* @throws NoSuchElementException - iteration has no more
Listing 10.3
Constructor Summary
TreeInorderIterator(SearchTreeNode root)
The constructor takes the root of the binary search' tree as an
argument and passes to its super class to complete the constructor
call.
Method Summary
protected buildVector(SearchTreeNode node)
void The method buildVector build the in-order vector for the tree
whose root node is passed as an argument to the constructor.
Methods inherited from class TreeIterator
finalize, hasNext, next, remove
Listing 10.5
class TreePreorderIterator extends TreeIterator
{
/**
* The constructor takes the root of the binary search'
* tree as an argument and passes to its super class to
* complete the constructor call.
* @param root is the root node for the tree for which the
* iterator is generated.
*/
public TreePreorderIterator(SearchTreeNode root)
{
super(root);
}
/**
* The method buildVector build the preorder vector for the tree
whose
* root node is passed as an argument to the constructor.
* @param node is the root node of the tree for which
* preorder traverse iterator is created.
*/
protected void buildVector(SearchTreeNode node)
{
if(node !=null)
{
v.addElement(node.contents);
buildVector(node.left);
buildVector(node.right);
}
}
}
Listing 10.6
It is important to note that in coding the method buildVector ( ), the three tree
iterator classes follow the respective algorithms discussed for the in-order, post-
order, and pre-order traverse.
Constructor Summary
BinarySearchTree()
Argument-less constructor for
BinarySearchTree.
Listing 10.7
It is important to understand that how and when different if/else blocks are
executed in the method insertNode. insertNode takes the root of the BST or root of a
sub-tree and the item to be added as arguments. The block#1 is executed when the
root of the tree is null. This would happen when the item is being attached to the leaf
node or the root of the tree is being created. The block#1 represents the sentinel or
the base case for the recursive method insertNode. The block #2 is executed when
item being attached is smaller than the item Object in the node. The left sub-tree of
node is then transformed. The block#3 executes when the item being attached is
larger than the item Object in the node. The right sub-tree of the node is then
modified. Block #4 executes when item is already present in the BST.
Imagine that the first node to be added has the item value equal to 300 (Figure
10.11).
FIG. 10.11
The block #1 is then executed and BST with the root containing item as 300 is
created. Block #2 will execute if the next item to be added is less than 300 and this
item will be added as the left child of the root. Alternatively, the block #3 will
execute to add an item larger than 300 as the right child of the root node. Each of
blocks #2 and #3 make a recursive call to method insertNode. The overall
mechanism may be illustrated well if we show the process, by adding a new item to
the BST with some nodes already attached (Figure 10.12).
FIG. 10.13
300>260. Execute
root = IN (node-300, 260); block#2
return modified tree-300 or
root
200<260. Execute
block#3
300.left = node-200 = IN (node-200, 260);
return modified subtree-200
250<260. Execute
block#3 200.right = node-250 = IN (node-250, 260);
return modified subtree-250
275>260. Execute
block#2
250.right = node-275 = IN (node-275, 260);
return modified subtree-275
Base case
reached.
260 gets
275.left = IN (null, 260); attached
275.left = node-260 as left
node to
node-275.
Backing out
Going in
FIG. 10.14
In Figure 10.14 IN indicates the call to the method insertNode. The first call is made
to the method insertNode from with in the method add. This call is shown in the top
most box as;
root = IN (node-300, 260);
FIG. 10.15
The items larger than the root node 300 will be added to the right branch of tree
and the process would be similar to the recursion described above. An exception is
thrown if attempt is made to add an item already present in the tree.
FIG. 10.16
In above Figure 10.6, in order to delete leaf node Z, one simply set the reference
from R to Z to null, thus clipping the node Z from the tree.
FIG. 10.17
To delete node R, we first assign a temporary reference to R. Then we break the
linkage of R from its parent Q, and make Q point to its grandchild Z. Finally we set
temporary reference to R to null, thus deleting R.
FIG. 10.19
FIG. 10.20
• Now copy the value of leaf node “275” into the item field of root
node “300”. This makes the “275” a root node (Figure 10.21).
FIG. 10.22
deleteNodeMethod.p
pt
Method deleteNode has four if – else type blocks. They execute as follows (See
Listing 10.9):
• The block #1 executes if tree to be deleted is null. In that case, a
NoSuchElementException is thrown.
• If item to be deleted is smaller than the root of the tree, then block
#2 is executed. In this case the deleteNode is called on left tree
branch recursively. This makes sense because elements smaller
than the root are located in the left sub-tree.
• If item to be deleted is larger than the root of the tree, then block
#3 is executed. In this case the deleteNode is called on right tree
branch recursively. This is because elements larger than the root
are located in the right sub-tree.
• Block #4 is executed when the node has the item to be deleted.
Figure 10.23 shows the details of block #4.
FIG. 10.23
FIG. 10.24
We mentioned before that the replacement value for any non-leaf node is the value
“just smaller” than the node to be replaced. The method rightmost finds this value
and returns a reference to it. Then we replace the root node with this replacement
value, which in this case happens to be 275 (Figure 10.25).
FIG. 10. 26
Figure 10.26 shows the final form of the tree, with root 300 deleted.
Method rightmost (Listing 10.9) takes a tree or sub-tree root as an argument and
returns the right most node of that tree. If the right child of root is null, then
rightmost returns the contents of the root. Otherwise, the method calls itself
recursively with the right child of the root as its argument. In Figure 10.24, the
following call is made to the method rightmost:
Comparable replaceWithValue = rightmost (node-200);
Figure 10.27 shows the recursive calls stacked up when method rightMost(RM) is
called with the node 200 as its argument.
Base case
reached.
Return RM (null) 260 gets
return 275 attached
as left
node to
node-275.
FIG. 10.27
The base case or sentinel is reached, when method rightmost is called with the node
275 as its argument. Since right child of node 275 is null, the method returns the
Comparable object with value 275 in it. Backing out, three levels of recursion, a
value of 275 is provided for the parameter replaceWithValue.
Method deleteRightMost
The method deleteRightMost takes a tree or sub-tree root as its argument, and
deletes the right most node of that tree. In deleting the root node 300 from Figure
10.24, the call to deleteRightMost is made with an argument node 200. The method
deletes the right most node of sub-tree with node 200 and returns a new reference to
the root of sub-tree with node 200 as the root. The method deleteRightMost calls
itself recursively with second call being similar to:
node.right = deleteRoghtMost (node-250). Figure 10.28 shows the recursive calls to
the method deleteRightMost(DRM).
FIG. 10.28
The base case is reached when the call is made with the method argument being
node-275. Then the last recursive call returns the node 260. This effectively replaces
275 with 260, and sets both children of 260 to null. Then each backing out call
returns the new value of the right sub-tree root, from where the call originated.
Method destroy
The method makeEmpty calls the method destroy ( ) to delete all nodes in the tree
and render its root as null. The tree destruction is done in post-order traversal.
Hence the method destroy follows the post-order traversal algorithm. The code for
method destroy is reproduced below in Listing 10.10.
private void destroy(SearchTreeNode node)
{
if(node !=null)
{
destroy(node.left);
destroy(node.right);
node.contents = null;
node = null;
}
}//Listing 10.10
The post-order logic of destruction of tree shown in Figure 10.19 is shown in Figure
10.29.
D(200)
D(250)
D(100)
D(275)
null
null
null
D(260)
Set Node 100 to
null
null
null null
D(400)
null
null
FIG. 10.29
Set node300 to null
Method avgPathLength ( )
The method avgPathLength calculates the average path length for the BST. The
average path length (APL) for a binary tree with n nodes is given as:
APLn = TPLn/n ……………………………………. Equation 10.0
The TPLn is the total path length for all the nodes in the binary tree defined as:
The total path length is found by summing over the depth of all nodes. The
parameter d[ i ] in equation 10.1 is the depth of the node number i. You would recall
that the depth of a node is its level. For example, the root node has a depth of zero.
To illustrate the average path calculation, we apply the equations 10.1 and 10.2 to
the tree of Figure 10.19. In Figure 10.19, we have the various nodes at various levels
as given in Table 10.8.
Node Depth or level
300 0
200 1
400 1
100 2
250 2
275 3
260 4
Table 10.8
For tree of Figure 10.19, the total path length is given as:
TPLn = ( 0 + 1 + 1 + 2 + 2 + 3 + 4 ) = 13
Then according to Equation 10.1, the average path length is given as:
APLn = 13 / 7 = 1.857.
A shorter path length for the same tree generally results in a tree that is more
balanced, which also allows for the faster searching of a key in it. Imagine that we
insert the data of BST in Figure 10.19 in the following order: 100, 200, 250, 260, 275,
300, and 400. This will result in a BST, which is no better than a linked list (Figure
10.30).
250
260
275
300
400
FIG. 10.30
The tree in Figure 10.30 looses all advantages of fast searching of a node that a more
balanced form like Figure 10.19 would provide. We can calculate the average path
length for the tree in Figure 10.30, to show that such length has increased compared
to the value calculated earlier for the form in Figure 10.19. The average path length
is given as:
APLn = (0 + 1 + 2 + 3 + 4 + 5 + 6 )/7 = 21/7 = 3
The above average path length of three is almost 1.6 times of the path length
calculated earlier for the more balanced tree given in Figure 10.19. The result is
longer searching times. In fact, we try to improve upon the binary search tree shown
in Figure 10.19, and try to make it more “balanced” – noting that a balanced tree is
“more bushy” or, it sprouts equally in all directions. A more balanced form of the
BST in Figure 10.19 is shown in Figure 10.31.
260
200 300
275 400
100 250
FIG. 10.31
The average path length for the tree in Figure 10.31 is given as:
APLn = ( 0 + 1 + 1 + 2 + 2 + 2 + 2 ) / 7 = 10/7 = 1.43
Therefore, one can draw the conclusion, that for a given number of nodes in a BST,
the shorter average path length will lead to a more balanced BST, which will allow
faster search.
Both return
100, 250, 275,
CPL(node-260,0) 5 each
and 400 are all
leaf nodes.
Returns
10
return 0 + CPL(node-200,1) + CPL(node-300,1)
FIG. 10.32
The computePathLength method is called with the argument root node 260, and
zero. If block is executed and the return value becomes the zero plus the two
recursive calls to computePathLength with arguments as root nodes 200 and 300,
and one. The third box from the top in the Figure 10.32 shows the further recursive
calls. The calls CPL (node-100, 2), CPL (node-250, 2), CPL (node-275, 2), and CPL
(node-400, 2) – all return a value of two, because they all take the leaf nodes as the
first argument. When the computePathLength is called with leaf node as the first
argument, it simply returns the second parameter as the return value. Therefore, in
backing out from the third box from the top in Figure 10.32, a value of five plus five
is returned. Finally the value returned to the first call is ten – same value that we
calculated on page 60 earlier.
}
}//Listing 10.12
The main method in the class first adds the nodes 10, 12, 5, 7.5 etc., and then prints
them. The selected nodes are then removed and the tree is printed again. Figure
10.33 shows the results.
It may be impractical, however, to have employee ID that run from zero to 100. It is
more common to have a uniform system of assigning ID’s – for example a five-digit
ID number. In such a system the largest ID, number would be 99999. If we tried to
keep each employees record in the array element, with index equal to employee ID
number, then for 100 employees, we would need to create an array with 100000
elements. Using a data structure 1000 times larger than actual need is certainly
1
This would be the case when the aircraft is not on the radar; rather the information is received over
radio control.
Notice that in this case the records are not sorted numerically based on the ID
number. Rather they are sorted based on some function of ID number or their key
value. Obviously, we would need a function, which would take the ID number as an
argument, and return this key, which is a function of the ID number. A function or
method that returns a key for an object of class is called a hashing function. The
formal definition of a hash method is as follows:
For example in the if the class Employee, would have a hashing method hash( ), such
that the value returned by the method hash is simply last two digits of the employee
ID. We write such a hash method in Listing 11.1.
public class Employee
{
//Other fields and methods
public int hash( )
{
return (id_num% max_items);
}
}
//Listing 11.1
The Figure 11.1 shows the action of hashing method written in Listing 11.1.
Method Summary
int compareTo(java.lang.Object value)
Compares this object with the specified object for order.
int hash()
Method hash returns the integer which can be used as a key to the
Hashable object.
Field Summary
private int id_num
id_num is the integer which stores the employee
identification number
private List
static Hashable[] List is the static array of Hashable items.
private max_items
static int max_items is the maximum number of items in the hash table
private Name
java.lang.String Name field stores the name of the employee.
private num_items
static int num_items gives the number of items currently in the hash
table
Constructor Summary
Employee()
Argument-less constructor sets id_num to zero and Name to blank.
Employee(int init_id)
The one argument constructor sets the id_num equal to the constructor argument
and the Name to blank.
Employee(int init_id, java.lang.String Init_Name)
The two-argument constructor sets the id_num equal to the first argument and
Name equal to the second argument.
Table 11.2
The complete java documentation for class Employee class are given in the
file Employee.html.
The source code for the class Employee is given in Listing 11.3.
import java.util.*;
import javax.swing.*;
public class Employee implements Hashable
{
private int id_num;
static
{
for(int index=0; index<max_items; index++)
List[index] = null;
}
public Employee()
{
this(0);
}
while(!done)
{
String Input = JOptionPane.showInputDialog ("Please enter the " +
"Employee 5 digit ID number . ");
Input = Input.trim();
int val=0;
try
{
val= Integer.parseInt(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal ID number entered");
continue;
}
if(val>=0)
Temp.id_num = val;
else
{
JOptionPane.showMessageDialog(null, "Negative ID number entered");
continue;
}
boolean done_name = false;
while(!done_name)
{
Input = JOptionPane.showInputDialog("Please enter the " +
"Employee Name . ");
Input = Input.trim();
if(Input.equals("") || Input == null)
{
JOptionPane.showMessageDialog(null, "No name entered.");
done_name = false;
//Main method
public static void main(String [] args)
{
Employee Temp = new Employee();
boolean done = false;
while(!done)
{
//get the Employee
Temp = Employee.getEmployee();
//Insert the Employee in Hash table
Temp.insert(Temp);
String Input = JOptionPane.showInputDialog("More Employees? " +
"Enter [Y]es or [N]o . ");
Input.trim();
if(Input.equals("Y") || Input.equals("y"))
done = false;
else if(Input.equals("N") || Input.equals("n"))
done = true;
else
{
JOptionPane.showMessageDialog(null, "Unexpected Response.");
continue;
}
}
//print the hash table
print();
//Search the hash table
findEmployee();
System.exit(0);
}
//Listing 11.3
The method hash( ), which returns the hash code for any Employee object has
already been discussed. The method convert may be needed to convert the five-digit
employee ID number to the employee key, and it uses the code similar to the method
FIG. 11.3 A
FIG. 11.3 B
Program prompts
user to input more
data. If user types Y,
then process of data
entry continues.
FIG. 11.3 C
FIG. 11.3 D
Search Results
displayed.
FIG. 11.3 F
First, the user is prompted to supply the five-digit ID number (Figure 11.3A). If the
valid ID number is entered, then the user is prompted to enter the employee name
(Figure 11.3B). User is prompted to enter more data (Figure 11.3D), and if the data
of Table 11.3 are entered, then the size and contents of hash table are printed
(Figure 11.3D). For searching an employee, the program prompts user to enter a
five-digit ID number (Figure 11.3E). If the Employee is found, then Employee
record is printed (Figure 11.3F).
Collisions
One would notice that in Table 11.3 no two employees had same last two digits for
their ID number. What would have happened if we wish to enter another employee
with following information (Table 11.4)?
ID Number Name
62902 Heather
In that case, the method hash would have returned a value of 02 as the hash code for
employee Heather, and the insert method would have over-written the record of
Heather over the record of employee Cynthia with ID number 52202. When the
hashing procedure tries to store two or more distinct objects, at the same location in
FIG. 11.4
OpenAddressing.ppt
Techniques called open addressing and liner probing may be used to resolve the
collisions and insert, retrieve, and delete Objects from the hash table. The linear
probing works as follows:
• Put one object in each array slot.
• When there is collision, then find the next available
empty array cell and put the object there.
Assume that the hash table is in the state shown by Figure 11.5. If data of Table 11.3
are the only one’s entered so far, then no collisions occurred yet.
Now we wish to enter employee Heather with ID number 69202 (Figure 11.6).
The hash method returns the same key as was for Cynthia, whose array cell is
already occupied. The method insert looks for next empty spot past array index [02]
and inserts the data for Heather in the first available empty spot at index [03]
(Figure 11.7).
The methods insert, retrieve, and delete from class Employee now need to be
modified according to the technique of linear probing discussed above. Figure 11.8
gives the pseudo-code and Listing 11.4 gives the source code for the method insert.
return (Hashable)List[location];
}
//Listing 11.5
Listing 11.6 gives the modified delete method.
public void delete(Hashable Item)
{
int location = Item.hash();
while(List[location].compareTo (Item) != 0)
location = (location +1) % max_items;
if(List[location] != null)
{
List[location] = null;
num_items--;
}
else
System.out.println("Item to be deleted is not in the list\n");
}//Listing 11.6
static
{
for(int index=0; index<max_items; index++)
List[index] = null;
}
public Employee3()
{
this(0);
}
while(!done)
{
String Input = JOptionPane.showInputDialog("Please enter the " +
"Employee 5 digit ID number . ");
Input = Input.trim();
int val=0;
try
{
val= Integer.parseInt(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal ID number entered");
continue;
}
if(val>=0)
Temp.id_num = val;
else
{
JOptionPane.showMessageDialog(null, "Negative ID number entered");
continue;
}
boolean done_name = false;
while(!done_name)
{
Input = JOptionPane.showInputDialog("Please enter the " +
"Employee Name . ");
Input = Input.trim();
if(Input.equals("") || Input == null)
{
JOptionPane.showMessageDialog(null, "No name entered.");
done_name = false;
}
else
{
Temp.Name = Input;
done_name = true;
}
}
done = true;
}
return Temp;
}
//Main method
public static void main(String [] args)
{
Employee3 Temp = new Employee3();
boolean done = false;
while(!done)
{
//get the Employee
Temp = Employee3.getEmployee();
//Insert the Employee in Hash table
Temp.insert(Temp);
String Input = JOptionPane.showInputDialog("More Employees? " +
"Enter [Y]es or [N]o . ");
Input.trim();
if(Input.equals("Y") || Input.equals("y"))
done = false;
else if(Input.equals("N") || Input.equals("n"))
done = true;
else
{
JOptionPane.showMessageDialog(null, "Unexpected Response.");
continue;
}
}
//print the hash table
while(!done)
{
Temp = Employee3.getEmployee();
Temp.delete(Temp);
String Input = JOptionPane.showInputDialog("More Employees to delete? "
+ "Enter [Y]es or [N]o . ");
Input.trim();
if(Input.equals("Y") || Input.equals("y"))
done = false;
else if(Input.equals("N") || Input.equals("n"))
done = true;
else
{
JOptionPane.showMessageDialog(null, "Unexpected
Response.");
continue;
}
}
}
/**
* Method findEmployee finds and prints the Employee information
based
* on the ID number provided by the user.
*/
public static void findEmployee()
{
Employee3 Temp = new Employee3();
boolean done = false;
while(!done)
{
Temp = Employee3.getEmployee();
Hashable Emp = Temp.retrieve(Temp);
if( Emp != null)
JOptionPane.showMessageDialog(null, " The
Employee found is " +
Emp);
else
}
//Listing 11.7
Though the class Employee3 is fully capable of handling collisions, and can work as
well as any other hash table, the performance suffers after the collisions takes place.
The reason is that once the collision takes place, the insertion, retrieval, and
deletions – all follow an algorithm much slower than O ( 1) due to linear probing.
Therefore the performance of most hash tables lie some where between O ( 1 ) to
slower than O ( 1 ).
The efficiency of hash table operations such as inserting, retrieving and deleting
becomes inconsistent due to clustering. For example in Figure 11.12, one would need
three probing to retrieve the number 659, but only one probing to find a number in
the areas, where no clustering took place. As more elements are added and the load
factor of the hash table grows, the existing clusters grow and new clusters are
formed – all resulting in degraded performance of the hash table.
Re-hashing
Re-hashing, where if the collision occurs, then the hash method computes a new
hash value by using the previously calculated value as an input, may reduce some of
the problems of linear probing. For example in Figure 11.13 the insertion of
number, 14001 will create a hash code of 01, an array index, already occupied by the
number 44001. Therefore we re-hash using the formula:
(hash value + 3 ) % array size resulting in the new location being 4,
which is also occupied. One more re-hashing would calculate the location for 14001
to be 07, which is also occupied. Finally (7 + 3 ) % 100 yields a location of 10, which
is available.
However, none of these solutions are as effective as a technique called buckets and
chaining – which we discuss next.
FIG. 11.15
If the objects with the same key are placed in a linked list at the same key location,
then the resulting structure is called a chain structure (Figure 11.16).
FIG. 11.16
Class Hashtable stores data in key, value pairs. It also requires that for every object
to be stored, the key be unique. The key must be a java object. The class used as a
key must have the equals and hashCode methods from java.lang.Object class
overridden “properly”. The objects of the class being stored as value in the hash
table do not need to have equals and hashCode methods overridden, although there
is no harm in doing so.
It is “required” that the object being used as key does not change, once the key –
value pair has been placed in the hash table. Understand as to what could happen if
key were altered after being placed in the hash table? This means that when you
search for the value associated with the key that was altered, you would not be able
to find that key and hence the value associated with it. Therefore, it is imperative
that the classes whose objects are used as a key do not have any methods to alter the
data members after the object is created. Better yet, the class for the key must be
declared final, so the key objects are immutable. Table 11.4 shows the summary of
constructors and methods for the class java.util.Hashtable.
Constructor Summary
Hashtable()
Constructs a new, empty hashtable with a default initial capacity (11) and load
factor, which is 0.75.
Hashtable(int initialCapacity)
Constructs a new, empty hashtable with the specified initial capacity and default
load factor, which is 0.75.
Hashtable(int initialCapacity, float loadFactor)
Constructs a new, empty hashtable with the specified initial capacity and the
specified load factor.
Hashtable(Map t)
Constructs a new hashtable with the same mappings as the given Map.
Method Summary
void clear()
Clears this hashtable so that it contains no keys.
Overriding the methods hashCode ( ) and equals ( ) for the class used as
key in the hash table
Putting objects in the Hashtable requires that the class associated with keys override
the java.lang.Object class methods hashCode ( ) and equals ( ) properly. Details of
what constitutes proper overriding of java.lang.Object class is given in the java API
for the class Object. It is required that the equals method has the following five
properties:
1. It is reflexive – meaning that for a reference x, x.equals (x) must
return true.
2. It is symmetric – meaning that for any reference values x and y,
y.equals (x) must return true only if x.equals (y) also returns true.
3. It is transitive – meaning that for any reference values x, y, and z;
if x.equals (y) returns true and y.equals (z) returns true, then
x.equals (z) must return true.
4. It is consistent – meaning that for any values of x and y multiple
invocations of x.equals (y) must return true or false, provided that
objects x and y are not modified between the calls to the method
equals.
5. For a null reference value, the x.equals (null) must return false.
public Employee2()
{
this(0);
}
public Employee2(int init_id)
{
this(init_id,null);
}
public Employee2(int init_id, String Init_Name)
{
this(init_id,Init_Name,0);
}
public Employee2(int init_id, String Init_Name, long init_phone)
{
Data = new EmployeeKeyNoHashCodeMethod(init_id,Init_Name);
phone = init_phone;
}
try
{
val= Integer.parseInt(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal ID number entered");
continue;
}
if(val<0)
{
JOptionPane.showMessageDialog(null, "Negative ID number entered");
continue;
}
return Temp;
}
public static long getPhone()
{
boolean done = false;
long temp_phone = 0;
String Input = "";
while(!done)
{
Input = JOptionPane.showInputDialog("Please enter the " +
"Employee phone number as xxxyyyy . ");
Input = Input.trim();
if(Input.equals("") || Input == null)
{
JOptionPane.showMessageDialog(null, "No phone # entered.");
done = false;
}
else
{
try
{
temp_phone = Long.parseLong(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal phone number entered");
done =false;
}
if(temp_phone <0)
{
JOptionPane.showMessageDialog(null, "Negative phone number entered");
done = false;
}
else
{
done = true;
}
}
}
return temp_phone;
}
//Listing 11.9
The class Employee2 has two instance variables for an employee. These are:
EmployeeKeyNoHashCodeMethod and phone number. The class
EmployeeKeyNoHashCodeMethod has two instance variables – Name and ID
number. Therefore, by the composition relationship, the class Employee2 ends up
including the instance variables of class EmployeeKeyNoHashCodeMethod as well.
Note that the class EmployeeKeyNoHashCodeMethod has an equals method, but no
hashCode ( ) method. The class EmployeeKeyNoHashCodeMethod is used as a key
for the objects of class Employee2. We write a driver class to test the classes in
Listing 11.8 and 11.9. This class is EmployeeTest1 given in Listing 11.10.
import java.util.*;
import javax.swing.*;
public class EmployeeTest1
{
public static void main(String [] args)
{
Hashtable Employee2_List = new Hashtable();
Employee2_List.put(new EmployeeKeyNoHashCodeMethod(31300,"Adam"),
new Employee2(new EmployeeKeyNoHashCodeMethod(31300,"Adam"),
1112222));
Employee2_List.put(new EmployeeKeyNoHashCodeMethod(49001,"Bertha"),
new Employee2(new EmployeeKeyNoHashCodeMethod(49001,"Bertha"),
2223333));
Employee2_List.put(new EmployeeKeyNoHashCodeMethod(52202,"Cynthia"),
new Employee2(new EmployeeKeyNoHashCodeMethod(52202,"Cynthia"),
3334444));
//Print the hashtable. Prints both the Key and values
System.out.println("Now printing the entire Hashtable you built.");
System.out.println(Employee2_List);
//Print only the values, not the keys
System.out.println("Now printing only the values in the Hashtable.");
System.out.println(Employee2_List.values());
//Searching the hash table
System.out.println("Now printing the size of Hashtable you built.");
System.out.println("Size of list = " + Employee2_List.size());
Employee2.findEmployee(Employee2_List);
System.exit(0);
}
}//Listing 11.10
FIG. 11.18
Problem comes when we try to search the hash table for a given employee. Even
with the correct data for the key, that employee is not found. We enter the employee
ID number for Bertha (49001) (Figure 11.19 A), and name Bertha (Figure 11.19B).
However, the results are horrid, as the message comes that the employee Bertha
with ID number 49001 is not found in the hash table (Figure 11.19C).
FIG. 11.19A
FIG. 11.19C
This is even more distressing, because when we print the hash table we clearly see
the employee we are searching for is there.
The reason is simple. The key for employee Bertha is mapped to a different hash
location each time – first when we put it in the table, and second when we try to
search the employee. This is so because in the absence of hashCode method in the
class EmployeeKeyNoHashCodeMethod, the hash code is computed by the method
in the Object class, which returns a different hash code even for the objects, which
are identical. The solution is deceptively simple. Add a hashCode method in the
class EmployeeKeyNoHashCodeMethod – even one as bad as the one shown in
Listing 11.11.
public int hashCode( )
{
return 59878489;
}
//Listing 11.11
The hashCode ( ) method such as above will return the same hash code for all keys.
This will degrade a hash table into a linear linked list, because all the values will be
stored in the same bucket, but it will at least work. Let is call the above hashCode
method as minimal hashCode method, and the modified Employee key class which
contains the method in Listing 11.11 as the class
EmployeeKeyMinimalHashCodeMethod. Since this class is exactly like the class in
FIG. 11.20
Field Summary
private int id_num
Identification number of each employee.
2
Data could be int, float, short, char or any other java primitive type.
Constructor Summary
EmployeeKey()
No argument constructor.
EmployeeKey(int init_id)
One argument constructor.
Method Summary
boolean equals(java.lang.Object obj)
Overrides the equals method in the java.lang.Object
class
int hashCode()
Overrides the hashCode method in class
java.lang.Object.
java.lang.String toString()
Overrides the toString method in java.lang.Object
class.
Table 11.6
Field Summary
private Data
EmployeeKey Data is the Employee Key, which includes the employee name
and ID number.
private phone
Constructor Summary
Employee5()
Argument-less constructor sets id_num to zero and Name to blank and
phone number to zero.
Employee5(int init_id)
The one argument constructor sets the id_num equal to the constructor
argument and the Name to blank and phone number to zero.
Method Summary
static void deleteEmployee5(java.util.Hashtable List)
Takes the hash table for the Employee5 as argument,
and deletes the employee from the table.
EmployeeKey getKey()
Returns the Data part of the Employee5 object, which
is of type EmployeeKey.
java.lang.String toString()
Returns a string representation of the Employee.
Table 11.7
import java.util.*;
import javax.swing.*;
FIG. 11.21
If an employee with certain key already exists in the hash table, that employee is not
added and a message to that affect is displayed (Figure 11.22)
FIG. 11.22
The hash table built is displayed when user chooses to print it (Figure 11.23).
Appendix
import java.util.*;
import javax.swing.*;
public Employee4()
{
this(0);
}
public Employee4(int init_id)
{
this(init_id,null);
}
public Employee4(int init_id, String Init_Name)
{
this(init_id,Init_Name,0);
}
public Employee4(int init_id, String Init_Name, long init_phone)
{
Data = new
EmployeeKeyMinimalHashCodeMethod(init_id,Init_Name);
phone = init_phone;
try
{
val= Integer.parseInt(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal ID number entered");
continue;
}
if(val<0)
{
JOptionPane.showMessageDialog(null, "Negative ID number entered");
continue;
}
Temp = new
EmployeeKeyMinimalHashCodeMethod(val,Temp_Name);
done = true;
}
return Temp;
}
public static long getPhone()
{
boolean done = false;
long temp_phone = 0;
String Input = "";
import java.util.*;
import javax.swing.*;
public class EmployeeTest2
{
public static void main(String [] args)
{
//Creates a hash table of default size
Hashtable Employee2_List = new Hashtable();
Employee2_List.put(new
EmployeeKeyMinimalHashCodeMethod(31300,"Adam"),
new Employee4(new EmployeeKeyMinimalHashCodeMethod(31300,"Adam"),
1112222));
/**
* Argument-less constructor sets id_num to zero and Name to
blank and
* phone number to zero.
*/
public Employee5()
{
this(0);
}
/**
* The one argument constructor sets the id_num equal to the
constructor
* argument and the Name to blank and phone number to zero.
* @param init_id is the value of the field id_num.
*/
public Employee5(int init_id)
{
this(init_id,null);
}
/**
* The two argument constructor sets the id_num equal to the
first argument
* and Name equal to the second argument. The phone number is set
* to zero.
* @param init_id is the value of the field id_num.
* @param Init_Name is the value of field Name.
*/
public Employee5(int init_id, String Init_Name)
{
this(init_id,Init_Name,0);
}
/**
* The three arguemnt constructor initializes all three, the ID
* number, name and phone number.
* @param init_id is the value of the field id_num.
* @param Init_Name is the value of field Name.
* @param init_phone is the value of the field phone.
*/
public Employee5(int init_id, String Init_Name, long init_phone)
{
try
{
val= Integer.parseInt(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal ID number entered");
continue;
}
if(val<0)
{
JOptionPane.showMessageDialog(null, "Negative ID number entered");
continue;
}
return Temp;
}
/**
* Helper method to get the employee phone number.
* @return the employee phone number from user input.
*/
public static long getPhone()
{
boolean done = false;
long temp_phone = 0;
String Input = "";
while(!done)
{
Input = JOptionPane.showInputDialog("Please enter the " +
"Employee phone number as xxxyyyy . ");
Input = Input.trim();
if(Input.equals("") || Input == null)
{
JOptionPane.showMessageDialog(null, "No phone # entered.");
done = false;
}
else
{
try
{
temp_phone = Long.parseLong(Input);
}
catch(NumberFormatException ex)
{
JOptionPane.showMessageDialog(null, "Illegal phone number entered");
done =false;
}
if(temp_phone <0)
{
JOptionPane.showMessageDialog(null, "Negative phone number entered");
done = false;
}
//Make sure that Employee Temp's Key is not already in the hash table
if(List.get(Temp.getKey()) == null )
{
List.put(Temp.getKey(),Temp);
JOptionPane.showMessageDialog(null, "The employee inserted into the "
+" hash table\n");
}
else
{
JOptionPane.showMessageDialog(null, "The Key for the employee just "
+" entered was already in the hash table. The employee is not” +
“ entered.\n");
continue;
}
References:
1. Hash Table animations:
Hashing Animation Tool
Animation of Hash Table using a Text file
Worst case, average case, and best case scenario for algorithms:
All algorithms may be analyzed for their average, worst case, and best-case
efficiency. The worst-case efficiency analysis is also called an asymptotic analysis, as
this would pretty much be the case when the software based on algorithms would
need to deal with large amount of user data. We illustrate the three efficiency cases
by using the problem of finding a key in a sorted array of size N.
1
Art of computer programming volumes 1,2, & 3. Addison Wesley Publishers. (www.awl.com).
For the algorithm ALG1, three efficiency related scenarios are given in Table 12.1
below.
Case Array index at which the Time to search is
Key is located proportional to:
Best case 0 Number close to zero.
Average case N/2 N/2
Worst case N-1 Approximately N
Table 12.1
It is possible that by some chance the key to be searched is located in the first array
cell. In that case, the search is almost instantaneous and the search time is
proportional to a very small number. In worst case, the key to be searched is last
value in the array, in which case the search time is nearly proportional to the array
size. However, on an average for large number of searches, the search time would be
N/2, the average search time. We could say that a mathematical function, which
could represent the search time using the ALG1 algorithm for an array, may be
given by an expression such as:
FIG. 12.1
Therefore in terms of Big O analysis of algorithms, we assume that the execution
time of an algorithm may be represented by a polynomial function such as:
Comparison Of Algorithms:
To select the best algorithm to be used in designing a software product, it is
imperative that we are able to compare the algorithms in terms of their Big O
values. In addition, if we can actually measure the time taken by one algorithm vs.
another then we can contrast our experimental findings with the theoretical analysis
we may have done. In writing the algorithm ALG1, we used the brute force method
of linear search of an array, even though we were told that the array has been
sorted numerically in the ascending order. Can we make use of the sorted state of
the array to speed up our search algorithm and make it faster than O ( N )? Indeed,
we can and such an algorithm is called the binary search. The idea of binary search
is similar to the process of finding the meaning of a word in a thick dictionary. To
look up the meaning of a word, we open the dictionary right in the middle. Either
word is found on the middle pages or it will be in the first half or in the second half.
If it is in the first half, then we break the first half into halves and then look again.
We keep repeating this process, until we find the page where the word is located. In
binary search of a sorted array, we look at the middle value for the key we are
searching. If key matches the middle value, the search is over. Otherwise, it must be
either in the first half or in the second half. Each time we can eliminate half of the
left over array to be searched. The process is repeated until we have found the value
CS20B: Topic 12: Analysis of Algorithms 1/10/2008 Page 3 of 87
or confirmed that the value is not in the array. The algorithm for the binary search
ALG2 is given below.
ALG2
Set a Boolean variable found to false,
Assume key to be found = Key
Set lowest_index = 0, The lowest index in the array where key can be.
Set highest_index = length –1, where length is the array length, and highest_index
Be the largest array index where the key can be.
Define middle_index to be the middle cell of the array portion where we are
searching for the key to be present.
return found
The function that describes the execution time for ALG2, or binary search is
approximately given as:
…………………………………
F(N) = log2 (N) Expr 12.4
Therefore the binary search may be called an O ( log2N ) algorithm. Even a simple
comparison may show that an O ( log2N ) algorithm would be much faster than an
Assume that we search an array of 106 elements, and we provide a value to search,
which is larger than the largest element in the array. In that case, the search
algorithm will be forced to search the entire array for both algorithms. Then the
values of tlinear and tbinary are given as follows:
Using expressions 12.7 and 12.8, one can see that the linear search time would be
about 50175 larger than the binary search time for an array of million elements.
Actual time measurements show that the binary search is even faster than the
theoretically calculated numbers.
Experimental Measurements
We write a class called BinarySearch, which has two methods in it, one called
binSearch ( ), and other linearSearch ( ). Both methods take an array and a key to
be searched as arguments and return a boolean parameter indicating whether key
was found or not. Notably, both algorithms ALG1 for linear search and ALG2 for
binary search have a loop, which consumes most of the time in the search process.
We use a java class System, along with its static method currentTimeMillis ( ) to get
the system time before the loops are executed and then just after it. The difference
between these times is the time spent in executing the loops. The program is set up
2
You can find the value of log2 (106) as follows:
6
Assume X = log2 (10 ). Then it is also true that 106 = 2x . Taking the logarithm to the
base 10 of both sides, we get
6 = X * log10 ( 2) = X*0.30103. This gives us:
X = 6/0.30103 = 19.93
CS20B: Topic 12: Analysis of Algorithms 1/10/2008 Page 5 of 87
in the way that user can specify the size of the array to be created and then the value
to searched in it. The summary of class BinarySearch is shown in Table 12.3.
Constructor Summary
BinarySearch()
Method Summary
static boolean binSearch(int[] Array, int key)
Takes the array and a key to be searched by
binary search process.
Table 12.3
Listing 12.1 gives the code for the class BinarySearch.
try
{
Input = BinarySearch.getInput("Please enter the length of the"
+" array to be created.");
}
catch(NullPointerException ex)
{
BinarySearch.showOutput("No value entered.");
done = false;
continue;
}
length = 0;
try
{
//Listing 12.1
When the program in Listing 12.1 is run it asks for the length of the array to be
created (Figure 12.2).
FIG. 12.2
On our machine3, where experiment was done one could enter the array size up to
15 Million. For array size larger than that, the java virtual machine gave an out of
memory error as java.lang.OutOfMemoryError. The original array contains values
from zero to N-1, where N is the length of the array entered in the dialog box in
Figure 12.2. The system then prompts the user to enter an integer to be searched. In
all cases, we gave a value of –1, so that the program is forced to search the entire
array, as the value is not in the array (Figure 12.3).
3
A Windows 98SE machine with 500 MHZ Pentium III processor, with 440MB RAM.
FIG. 12.4
In order to get a reasonable value of the search time for each array size, four
readings were taken for each array size. The data are shown in the Table 12.4.
Array Size Measurement1 Measurement2 Measurement3 Measurement4 Average
1000000 50000 50000 50000 60000 52500
2000000 60000 60000 50000 50000 55000
3000000 110000 50000 110000 110000 95000
4000000 110000 110000 110000 170000 125000
5000000 170000 160000 160000 160000 162500
6000000 220000 220000 220000 160000 205000
7000000 220000 220000 220000 220000 220000
8000000 220000 220000 220000 220000 220000
9000000 280000 280000 320000 270000 287500
10000000 330000 330000 330000 280000 317500
11000000 320000 390000 330000 330000 342500
Table 12.44
4
All measurements are not shown in the table 12.4.
4.00E+05
Linear Search Time = 0.0302* Array Size + 7954.5
s 3.50E+05
d
n
o
c 3.00E+05
e
s
o
r 2.50E+05
c
i
m
n
i 2.00E+05
e
m
i 1.50E+05
T
h
c
r 1.00E+05 Average
a Linear (Average)
e
S 5.00E+04
0.00E+00
0.00E+00 2.00E+06 4.00E+06 6.00E+06 8.00E+06 1.00E+07 1.20E+07
Array Size
FIG. 12.5
On an average, a good linear search behavior is indicated by the graph in Figure
12.5. It may be instructive to note as to why the binary search time was close to zero
even for the array size of 11 million. The number of iteration performed by
the loop in method binSearch, even for the array size of 11 million were
just 23, as compared to the 11million iterations for the loop in linear
search! If we use the linear regression equation in Figure 12.5, then 23
loop iterations amounts to barely 8000 micro-seconds of computer time
– which is too small to be measured by the technique we used in this
experiment.
Table 12.6
According to Table 12.6, the slowest growing algorithm would be the one, which
flows the Big O of log2N, and the fastest growing one would be the 2N. Others, such
as O ( N ), O ( N* log2N ), O ( N2 ), etc. would have growth rates between the log2N
and 2N. One may not be able to appreciate these data very much unless we show the
impact they may have on actual data processing times. Assume that a computer is
capable of executing 1 billion (109) instructions per second. Then the Table 12.7
shows us as to how long it would take to complete the processing of data of size N,
for algorithms following different Big O functions.
12
10
0
0 2000 4000 6000 8000 10000
Input Size (N )
FIG. 12.6
5
The age of the universe is calculated to be about 20 billion years.
Selection Sort
A java applet at the link below shows the animation of the selection sort and other
sorting algorithms.
Sorting Algorithm Applet
In selection sort, we start scanning an array from the element at index = 0, and we
look for the largest element in the array. You would recall from previous
programming courses that the largest element in an array might be easily found by
a method such as given in Listing 12.2, for finding the largest value in an integer
array.
Once we find the largest element, we place it at the location N-1 in the array. In first
such an attempt, the largest element in the array has been placed at the location,
where it would be in the sorted array. This reduces the size of the array to be sorted
and scanned by one. Next time we repeat the same process for the remaining array
from index zero to N-2. When we find the largest element this time, we place it in
the location with index N-2. We carry on this process, until the array is fully sorted.
The above analysis shows that in selection sort the number of times an array to be
sorted would be scanned is N-1. However, the size of the array to be scanned,
decreases by one each time. If we sum up the sizes of the array to be scanned each
time, the total number of array elements to be scanned in all scans will be given by
the arithmetical series,
N + (N-1) + (N-2) + (N-3)+ .................................................................... + 1.
The basic algebra tells us that the sum of the above series would be N (N + 1) /2.
The asymptotic form of the function,
F(N) = N(N+1)/2
Would be
F(N) = N2
Therefore one would conclude that selection sort a is an O ( N2 ) algorithm or it has
a Big O of N2. The Figures 12.7A to 12.7I show the mechanism of selection sort for
an array of integers.
FIG. 12.7B
FIG. 12.7D
FIG. 12.7E
In third pass we scan the array for the largest value from index 0 to index 2. The
largest value in this part is 12, which is to be swapped with the value 10 – the last
cell in the unsorted part of the array (Figure 12.7F).
FIG. 12.7F
FIG. 12.7G
Thus at the end of third pass the last three elements of the array are sorted. Now in
the fourth and last pass we scan the array from index 0 to index (5-4 = 1). This time
the largest value found is 10 (Figure 12.7H).
FIG. 12.7H
FIG. 12.7I
From the above discussion of the selection sort, the following facts are clear:
• An array of size N will require N-1 passes through it.
• The portion of the unsorted array decreases by one with each
pass.
• Each pass scans the array from index 0 to index
(N – pass Number).
• With each pass, the largest value is placed in the array cell with
index = (N- pass number). This is done by swapping the value in
location (N- pass number) with the location, where the largest
value is found.
Based on these facts the algorithm for selection sort may be written as follows:
We code the selection sort algorithm as follows using the class SelectionSort (Listing
12.3). We however add a method swap, which would pretty much do the last part of
the ALG4 algorithm.
The array, which was passed as 36, 24, 10, 6, and 12, is
now sorted. The recursive version of selection sort is
shown in appendix A12 with source code for class
SelectionSortRecursive shown in Listing A12.1.
FIG. 12.8
if(((Comparable)(Array[index])).compareTo(Array[index+1])>0)
swap(index, index+1, Array);
}
}
}
The results of Listing 12.4 for sorting an Integer and a String array are given in
Figure 12.9.
FIG. 12.9
Although the mechanism of bubble sort becomes amply clear by the java applet we
saw early on, we also present the following PowerPoint presentation to emphasize it.
BubbleSort.ppt
A typical array, similar to the one we presented for selection sort is sorted by the
bubble sort (Figure 12.10A).
FIG. 12.10C
CS20B: Topic 12: Analysis of Algorithms 1/10/2008 Page 29 of 87
One starts scanning again from the array index zero, but this time one excludes the
sorted portion from the scan. This would bubble the next largest value to the index
one before last (Figure 12.10D).
FIG. 12.10D
Notices, that though, the value 12 is now sorted, but the algorithm cannot claim it
part of the sorted array yet, since in the third scan, the array from index zero to
index two would be used. Incidentally, this array is in sorted state after three passes,
but the actual process would take four scans, though last scan would not make any
changes. The sorted array after four scans is shown in Figure 12.10E.
Insertion Sort
Once again, we revert to sorting algorithm java applet to understand the
mechanism of insertion sort.
Sorting Algorithm Applet
In insertion sort, starting from the beginning of the array, we look at the value at
the index zero, and if this value is smaller than the value in index one, then we
consider the first two elements already sorted. However, if the value in index zero is
larger than the value in the index one, then we swap them. Then we look at the value
in the index two and find its best place in the “sorted” sub-array, and insert it there.
We keep taking the first value from the unsorted sub-array, find its best location in
the sorted sub-array, and insert it there. All the values after the best location would
be moved up by one array index, thereby increasing the size of sorted array by one.
The PowerPoint presentation below shows the process in more detail further.
InsertionSort.ppt
FIG. 12.11B
FIG. 12.11D
FIG. 12.11E
Then we move 24 to the location vacated by 36 and that results in growth of sorted
sub-array by one more element (Figure 12.11F).
FIG. 12.11F
FIG. 12.11G
Using the same mechanism, entire array is sorted as the elements 6 and 12 are also
inserted in their proper location in the sorted sub-array (Figure 12.11H).
Algorithm ALG6 gives the algorithm for the insertion sort and the Listing 12.5 gives
the source code for it.
FIG. 12.12
If we have an array of size 100, then the using an O (N2) algorithm to sort entire
array at once will take time proportional to 1002 = 10,000. However, if we divide the
array into two parts, each being 50 elements, then the time to sort them reduces to a
number proportional to, 502 + 502 = 2500 + 2500 = 5000. Of course, the sorted halves
have to be merged. Merging two arrays is an algorithm of O (N). Therefore, overall
time will be proportional to 5100. The time differential between divide and conquer
sorts and straight O (N2) sorts become substantial as the array size increases, and if
arrays to be sorted is divided further in smaller portions.
Merge Sort
If array is divided into portions as small as two elements each, and then merged to
form the sorted array, then instead of O (N2), it becomes an O (Nlog2N) algorithm.
We must be clear about the strategy for merging, as parts that are sorted separately
FIG. 12.13
Imagine that we have two sorted sub arrays, one containing 1, 3,7,11,12, and other
2,4,6. Now to merge them together and still get a final sorted list, we use the
following strategy.
• The smallest element would be either in the first cell of first array or in the
second array. In Figure 12.2, it is in the first cell of first array, so we move it
into the first cell of the final array.
• The algorithm marks the copied element as copied, so it is not copied again.
• Then we compare the elements 2 and 3, since 2 is smaller than 3, we move
that to the next slot in the final array, and element 2 is marked copied.
• The process goes on and finally when one array is finished, the remaining
elements from the unfinished array are copied to the last slots in the final
array.
The following PowerPoint presentation shows the merge process clearly.
Error! Not a valid link.
FIG. 12.14A
Comparing the first cells of two sub arrays, the smallest value is found in the first
cell of the first sub array (Figure 12.14A). This value is copied to the first cell of
final array, and the copied cell is marked as read (Figure 12.14B).
FIG. 12.14B
CS20B: Topic 12: Analysis of Algorithms 1/10/2008 Page 40 of 87
Then the next cell from the left sub array is compared with the first cell from the
right sub array. Since 2 is smaller than 3, it is copied to the next unfilled cell of the
final array and copied element is marked as copied (Figure 12.14C and Figure
12.14D).
FIG. 12.14C
FIG. 12.14D
FIG. 12.14E
The remaining elements from the array with un-copied elements are now copied to
the final array (Figure 12.14F).
FIG. 12.14F
The recursive algorithm for merge sort is deceptively simple (ALG7).
CS20B: Topic 12: Analysis of Algorithms 1/10/2008 Page 42 of 87
ALG7 : Merge sort (recursive)
mergeSort ( )
{
Cut array is half
mergeSort the left half
mergeSort the right half
Merge the two-sorted halves into one sorted array
}
One would note that method mergeSort above would be another example of double
recursion. The prototype of method mergeSort will need the array to be sorted as
well as the indices of two ends of the array, which will form the sorted portion. The
prototype is given below.
public static void mergeSort (Comparable [ ] values, int first, int last);
values is the array of objects, which implement Comparable interface. First is the
lower index of the array, where sorting must begin, and last is the index, higher than
first, where sorted array ends. This gives the size of the array to be sorted as
(last – first +1). Since this a recursive algorithm (ALG7), we must know what the
sentinel or base case is. Obviously, when array is split so that a size of one element
is reached, then there is nothing to sort. Therefore the base case is:
FIG. 12.15
Based on Figure 12.15, we write the prototype of method merge as follows:
Figure 12.16 repeats the process shown in Figure 12.14, as to how the sorted sub
arrays are first copied to a temporary array, and then to the final array called
values.
use a local array tempArray of same size and type as the values
Set the index to leftFirst
Set saveFirst equal to the leftFirst //this tells, where to begin copy back in values array
While more items in left half and more items in right half
If values [leftFirst] <values [rightFirst]
Set tempArray [index] = values [leftFirst]
Increment leftFirst
Else
Set tempArray [index] = values [rightFirst]
Increment rightFirst
Increment index
(only one of the following two will execute in one call cycle)
Copy remaining elements from left sub-array to the tempArray
Copy remaining elements from right sub-array to the tempArray
Copy the sorted elements from tempArray back to the values array.
The termination condition of the first while loop is that loop must exit as soon as
elements are finished copying from left sub-array or from right sub-array. That will
happen as soon as the leftFirst becomes larger than the leftLast or rightFirst
becomes larger than rightLast. This termination condition may be shown as follows:
Last three copy loops are simple. The first loop copying remaining elements from
left sub-array must terminate as soon as leftFirst becomes larger than leftLast,
therefore the loop pretest condition is simply leftFirst<= leftLast. The second loop
index++;
}
while(leftFirst<=leftLast)
{
tempArray [index] = values [leftFirst];
leftFirst++;
index++;
}
while(rightFirst<=rightLast)
{
tempArray [index] = values [rightFirst];
rightFirst++;
}
}//end of class MergeSort
//Listing 12.7
Output of Listing 12.7 is identical to Figure 12.9.
FIG. 12.19
Using quick sort an array may be sorted in its entirety, but to make the method
more versatile, it is coded so that the method will accept a lower index and a higher
index and sort the array bounded by two indices. Therefore, the signature of
method quickSort will be as follows:
The algorithm indicates that the process of splitting arrays must go on until there
are only two elements left in the array. This is simply so, because in an array of size
one, there is nothing to sort. Therefore the base case in the recursive call to the
method quickSort is enforced by making the recursive calls only if array size is two
elements or larger. That would require that the method executes only when6
(high_index – low_index) > 0.
Since the array also needs to be partitioned each time, before quickSort can be
called recursively on two partitions, a method returning the index, where array is
partitioned is needed as well. Following these guidelines, the structure of the method
quickSort will look like as follows (Figure 12.20):
6
For an array of size two, the high_index would be one and low_index would be zero.
There is considerable debate upon the best choice of a pivot. Here we will use a
location or value for the pivot, which will make our task for explaining algorithm
for the method partition easier. Actual coding of method partition will be done
differently than the algorithm used for explaining it’s functioning. This is done to
avoid all the pit falls a wrong choice of pivot may cause in certain conditions.
Assume that we choose the first element in the array as a pivot. Then array will can
be seen as having three natural divisions: S1 (where all elements are lower than
pivot), S2 (where all elements are higher than or equal to the pivot), and unknown,
where no such certainty exists (Figure 12.21).
FIG. 12.21
Take the concrete example of the array shown in Figure 12.17 (reproduced below
Figure 12.22).
FIG. 12.23
When the partitions are as small as two array elements, then each such segment will
be in the sorted state. In quick sort, no explicit merging is needed, because swapping
array elements with each other by the method partition does all the segmentation,
and sorting of each segment. Getting back to the invariant again:
“The items in the region S1 are all less than the pivot, and items in
region s2 are all greater than or equal to the pivot”, we can actually enforce
the invariant by each subsequent call to partition, by a simple technique. We
initialize (Figure 12.21) the index
lastS1 = first and
firstUnknown = first + 1
By doing so, we are forcing the whole array, except the pivot to be unknown and
this situation is shown in Figure 12.24.
FIG. 12.25
This reduces the length of unknown by one cell with each call to the function
partition. Figure 12.25 shows the possibility that element at firstUnknown is less
than pivot p. In this case, the element at firstUnknown is swapped with the element
at lastS1+1, thus reducing the unknown by one. The other possibility is shown by
Figure 12.26, where the element at firstUnknwon is larger than or equal to P, in
which case the firstUnknown is simply incremented by one.
ALG7
int partition (theArray, first, last)
Call method choosePivot, which chooses a pivot and swaps it with the first array element.
(Since the pivot is in first array location, we know what its value is).
*Initialize
//set P equal to the pivot value
P = theArray [first]
//Set S1 and S2 to be empty.
lastS1 = first
firstUnknown = first + 1
//Creation and expansion of regions S1 and S2
while firstUnknown is less than or equal to last (See Figure 12.27)
if (theArray[firstUnknown] < P)
Move theArray[firstUnknown] into region S1
Else
Move theArray[firstUnknown] into region S2
//end of while
//place the pivot between the S1 and S2 in its proper location
Swap theArray[first] with the theArray[lastS1]
Return lastS1 as the location of the pivot.
The Listing 12.9 shows the code for method partition. We show the code for
choosePivot later.
//*************************************************************
private static int partition(Comparable[ ] theArray, int first, int last)
{
choosePivot (theArray, first, last);
Comparable pivot = theArray [first]; // reference pivot
// Initially, everything but pivot is in unknown
int lastS1 = first; // index of last item in S1
// move one item at a time until unknown region is empty
for (int firstUnknown = first + 1; firstUnknown <= last; ++firstUnknown)
{
if (theArray[firstUnknown].compareTo(pivot) < 0)
{
// item from unknown belongs in S1
++lastS1;
swap(theArray, firstUnknown, lastS1);
}
quickSort(Array2,0,Array2.length-1);
System.out.println("Printing sorted string array.");
for(int index = 0; index<Array2.length;index++)
System.out.println(Array2[index]);
System.out.println();
quickSort(Array4,0,Array4.length-1);
System.out.println("Printing sorted character array.");
for(int index = 0; index<Array4.length;index++)
System.out.println(Array4[index]);
}
}
//Listing 12.10
The results of Listing 12.10 are given in the Figure 12.28 below.
//***********************************************************
Printing unsorted integer array.
36
24
10
6
12
Printing sorted integer array.
6
10
12
24
36
Heap Sort
We need to discuss the concept of a heap before discussing heap sorting.
Concept of a heap
A heap may be a linear or nonlinear data structure, where each element contains a
key, such that key in the element in position k is equal or larger than the key in the
position 2k+1, and in 2k+2 (if exists). One must remember, however, that in java the
array index starts at zero. Therefore, the element with index or subscript k is in
(k+1 )th element in the list. An example of a heap data structure stored in an array is
shown in Figure 12.29.
FIG. 12. 30 Triangle is full binary tree and the cut off triangle is
complete binary tree
We can put elements in a heap by maintaining the order and the shape property.
More than one possible heaps can be generated for the same collection of elements.
Therefore, heap is not as unique as binary search tree, where for elements added in
Understand that either in the array, or in the binary tree representation, more than
one heap configurations are possible. Reversing above rules gives us the strategy for
converting a heap array into binary tree representation. These rules are as follows:
• Put the first element of the array in root position.
• For every root of the tree or sub tree, the left child will be in the
position (index *2) + 1 and right child will be in the position
(index*2) + 2. If those positions or one of those positions is empty,
either then the element is a leaf node or it has one child only.
Using these rules we show as to how the heap array in the Figure 12.29 can be
represented as a binary tree. The highest index in the array is 12. using the formula
12 = index*2 + 2, gives us index = 5. This means that 58 is the right child of element
in index 5. The table below shows the calculations as to the element in which index
has which other element as its left or right child. This will remain standard for any
conversion from the array to binary heap tree. The elements in index numbers one
and two are the left and right children of the root.
Position of the element = Position of left child = Position of right child
index (index*2 ) + 1 =(index*2) +2
1 3 4
2 5 6
3 7 8
4 9 10
5 11 12
6 13 14
7 15 16
7
Since in this work, we only use maximum heap, from now on, unless specified, the heap will mean a
“maximum heap”.
FIG. 12.31
FIG. 12.32
The heap shown in Figure 12.32 has the shape property but not the order property.
Also all the leaf nodes are already heaps. The node containing value 2 is not a heap,
but it can be converted to a heap easily. One way to do that will be to remove the
element 2, which converts the tree with node 2 as a tree with empty root. Then we
can add another item from the tree, to preserve the order property. A method can
be developed to do this. Let us call this method as reheapDown, as it adds an item to
the empty root. Starting from the lowest non-leaf nodes, we can reheapDown, nodes
at each level, up to the root, such that the whole tree will eventually turn into a heap.
Once the reheapDown has been called on the root node, the whole tree must be a
heap. After that, the whole tree will satisfy the order as well as the shape property of
the heap. Process of re-heaping the entire tree may look like the Figure 12.33 below.
Method reheapDown
The method reheapDown must take the array, a beginning index and an end index
for the array portion to be heaped. The prototype of the method is as follows:
if(Vals[root].compareTo(Vals[maxChild])<0)
{
swap(Vals,maxChild,root);
reheapDown(Vals,maxChild,bottom);
}
}
}
Listing 12.11
We show the following PowerPoint presentation to illustrate the mechanism of heap
sort one more time.
IllustratingHeapSort.ppt
We can now implement the steps outlined on page 66 to write the pseudo-code and
algorithm ALG9 for method heapSort.
ALG9
public static void heapSort(Comparable [ ] Vals, int first, int last)
//Convert the incoming array to a heap first. Leaf nodes are already heaps.
Therefore begin from the index last and move down in index.
For index beginning at last, index >=0, index—
reheapDown(Vals, index, last-first) //This converts the array to heap
//Now do the sorting, Since the largest value is already on the top
Set passcount equal to first
while passcount is less than array length
swap( Vals, first, length-passcount-1)//same logic as selection sort
increment passcount
reheapDown(Vals, first, length-passcount-1)
The complete code for class HeapSort, demonstrating the action of method heapSort
is given in Listing 12.12.
import java.io.*;
public class HeapSort
{
private static void reheapDown(Comparable [] Vals, int root, int
bottom)
{
int maxChild = 0;
int leftChild = root*2 +1;
int rightChild = root*2+2;
if(leftChild<=bottom)
{
if(leftChild==bottom)
{
maxChild = leftChild;
}
else
{
if (Vals[leftChild].compareTo(Vals[rightChild])<0
||Vals[leftChild].compareTo(Vals[rightChild])==0 )
maxChild = rightChild;
else
maxChild = leftChild;
}
FIG. 12.35
Note that we must pad the numbers with leading zeros, so that all numbers have
same number of digits. Then we must create buckets for each radix, which in this
case are digits zero to nine. In each bucket, we would need queues, in which we
would place array members. Figure 12.36 shows an empty bucket and queue system.
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
FIG. 12.36
We start scanning the array for the least significant (right most) digit of each
number. The number is placed in a bucket matching the radix of the digit being
scanned. For example, in the first pass through the array in Figure 12.35, we scan
the right most digit of first array element 762. The radix of that digit is 2, so we
place it in the first queue of bucket #2 (Figure 12.37).
FIG. 12.37
Next number is 124, so it gets placed in the first queue of bucket number 4 (Figure
12.38).
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
762 124
FIG. 12.38
We continue scanning until all the numbers have been placed in one or the other
bucket. After first scan, the buckets and queues look like Figure 12.39.
FIG. 12.39
Now we copy back the values to the array starting vertically down from leftmost
bucket and moving rightward towards the higher number buckets. For example the
sequence of copying back to the array will be 800, 100, 761, 001, 762…. 999. After
copying, and after first pass, the array will look similar to Figure 12.40.
FIG. 12.41
FIG. 12.42
Looking at the progress of radix sort, we can say that in each pass it sorts the digits
in the position, which equals the pass number. In pass number zero, the digits in
rightmost location are sorted and so on.
Let us call the method to perform radix sort as radixSort. Based on the discussion
above, the method will need two loops. First loop will iterate number of significant
figures in the largest number. For example, if the largest number in the array is
10,000, then the first loop will need to iterate over five positions, one for each
significant figure. The inner loop will then go look at each element of the array, and
put them into buckets or queues based on the significant figure being sorted. The
signatures of method radixSort are as follows:
ALG10
public static void radixSort ( Object [ ] values, int numValues,
int numPositions, int radix)
Create an array of FIFO queue with array length being equal to the integer radix.
//numPositions is the number of significant digits in the largest number in the array
For position from numPosition-1 to position zero and decrementing position by one
For counter going from zero to less than numValues & incrementing by one
Set the value of whichQueue to the value of the integer at the position
of values [counter]//determines as into which queue the array member will go in
Enqueue the values [counter] member of the array in
queue [whichQueue]
Call the method collectQueues (values, queues, radix)
The source code for the method radixSort, replacing the object array with a
StringBuffer array is shown below in Listing 12.13.
public static void redixSort(StringBuffer []values, int numValues,
int numPositions, int radix)
{
LinkedQueue [] queues = new LinkedQueue [radix];
for(int index=0; index<queues.length; index++)
queues[index]= new LinkedQueue();
int whichQueue = 0;
The purpose of the method collectQueues is to collect the queues populated in each
loop iteration, iterating over the significant positions in the integers, and then fill the
array back by dqueueing them as discussed earlier when we illustrated the
mechanism of radix sort. The signatures of collectQueues method are as follows:
ALG11
private static void collectQueues(Object [ ] values,
LinkedQueue [ ] queues, int radix);
Set index to zero
For counter going from zero to less than radix & incrementing by one
While queues [counter] is not empty
dequeue an item from queues [counter]
copy the dequeued item into values [index]
increment the index by one.
//Since all queues have only number of items equal to the array length, we
//need not control the length of the array.
Listing 12.13 shows the entire source code for the class RadixSort, which include the
supporting methods and the main method.
//***************************************************************
public class RadixSort
{
/**numPositions is the number of positions in the largest digit
in the
* array.
*/
public static void redixSort(StringBuffer []values, int
numValues,
int numPositions, int radix)
{
LinkedQueue [] queues = new LinkedQueue [radix];
for(int index=0; index<queues.length; index++)
queues[index]= new LinkedQueue();
int whichQueue = 0;
return length;
}
//***********************************************************
private static int padStrings(StringBuffer [] Rand_Arr)
{
int length = findLargestNum(Rand_Arr);
for(int index=0; index<Rand_Arr.length; index++)
{
if(Rand_Arr[index].length()<length)
{
doPadding(Rand_Arr[index],length);
}
}
return length;
}
//*******************************************************
private static void doPadding(StringBuffer Str,int length)
{
//pad the string to length length by adding leading zero
Class SelectionSortRecursive
java.lang.Object
SelectionSortRecursive
This class shows the recursive technique applied to selection sort method.
Method Summary
static int indexOfLargest(java.lang.Object[] Array, int lower_bound,
int upper_bound)
Finds the index of largest element in the array and returns it.
static void main(java.lang.String[] args)
Instaintiates an array of integers.
static void selectionSort(java.lang.Object[] Array, int lower_bound,
int upper_bound)
Sorts the entire array of objects or a portion of it passed to it.
static void swap(int index_largest, int swap_from,
java.lang.Object[] Array)
Swaps elements from one index to the other in the array.
Constructor Detail
SelectionSortRecursive
public SelectionSortRecursive()
Creates a new instance of SelectionSortRecursive
Method Detail
main
public static void main(java.lang.String[] args)
Instaintiates an array of integers. Calls recursive selection sort method.
Prints the sorted array.
Parameters:
args - the command line arguments. No command line arguments passed in
this case.
selectionSort
public static void selectionSort(java.lang.Object[] Array,
int lower_bound,
int upper_bound)
indexOfLargest
public static int indexOfLargest(java.lang.Object[] Array,
int lower_bound,
int upper_bound)
Finds the index of largest element in the array and returns it.
Parameters:
Array - Array of objects
lower_bound - lower bound of the array
upper_bound - upper bound of the array
Returns:
the index of largest element with in the lower_bound and upper_bound
swap
public static void swap(int index_largest,
int swap_from,
java.lang.Object[] Array)
Swaps elements from one index to the other in the array.
Parameters:
index_largest - one of the index involved in swap
swap_from - other index involved in swap
/*
* SelectionSortRecursive.java
* Created on April 8, 2006, 8:15 AM
*Reference: Prof. Paul Hilfinger Notes on CS61 B at UC Berkeley
*/
/**
* This class shows the recursive technique applied to selection sort method.
* @author Satish Singhal
*/
public class SelectionSortRecursive
{
/** Creates a new instance of SelectionSortRecursive */
public SelectionSortRecursive() {
}
/**
* Instaintiates an array of integers. Calls recursive selection sort method.
* Prints the sorted array.
* @param args the command line arguments. No command line
arguments
* passed in this case.
*/
public static void main(String[] args)
{
Object [ ] Array = new Integer[]{ new Integer(36),new Integer(24),
new Integer(10), new Integer(6),new Integer(12)};
selectionSort(Array,0,Array.length-1);
for(int index = 0; index<Array.length;index++)
System.out.println(Array[index]);
}
/**
* Sorts the entire array of objects or a portion of it passed to it.
* Requires that lower_bound<=upper_bound.
* @param Array The array of objects to be sorted.
* @param lower_bound the index of lower bound of the array portion to
be sorted.
* @param upper_bound index of upper bound of the array to be sorted.
*/
public static void selectionSort(Object [ ] Array, int lower_bound, int
upper_bound)
{
/**
* Swaps elements from one index to the other in the array.
* @param index_largest one of the index involved in swap
* @param swap_from other index involved in swap
* @param Array the array in which swap is to be performed.
*/
public static void swap(int index_largest, int swap_from, Object [] Array)
{
Object Temp = Array[index_largest];
Array[index_largest]= Array[swap_from];
Array[swap_from]= Temp;
}
}//end of class
//Listing A12.1