You are on page 1of 4

Data Structures Assignment 1

(Implementing a Queue using Stacks)


Due: Aug 17

This assignment is for you to get comfortable with JAVA features. You will get a chance
to use class hierarchy, File I/O, Exception handling, Thread programming, Inter-thread
synchronization etc. Please read the entire assignment carefully, many times over.

The goal is to implement a data structure called “queue” using another data structure
called “stack”. Refer chapter 4 of the Goodrich and Tamassia book for stacks and queues.

Programming problem 1: Implement a stack using an array. Implement the stack


interface defined here. Use generics in Java to make sure that you can use the stack for
any type of data. You may assume that the number of elements inserted into the stack
never exceeds 100,000. Do NOT use the inbuilt Stack class of java. Name your stack
implementation as myStack.

Queue using two Stacks


The goal of this assignment is to implement a queue using two stacks. We will employ
two different ideas for this and try to understand why one idea is better than the other. Let
us first consider a naive implementation.

Implementation 1:
Let S1 and S2 be the two stacks to be used in the implementation of our queue. All we
have to do is to define the enqueue and dequeue operations for the queue.

enqueue(T a)
S1.push(a);

dequeue(){
if (S1 is empty) return(error);
while(S1 is not empty){
S2.push(S1.pop());
}
r <- S2.pop();
while(S2 is not empty){
S1.push(S2.pop());
}
return(r);
}

Programming Problem 2: Use your stack implementation (programming problem 1) to


implement a queue using the above pseudocode. Implement the queue interface
defined here. Name your implementation myQueue1.

Implementation 2:
Again, let S1 and S2 be the two stacks to be used in the implementation of our queue. As
in the previous implementation we have to define enqueue and dequeueoperations.

enqueue(T a)
S1.push(a);

dequeue(){
if (S1 is empty & S2 is empty) return(error);
if (S2 is empty){
while(S1 is not empty){
S2.push(S1.pop());
}
}
return(S2.pop());
}

Programming Problem 3: Use your stack implementation to implement a queue using


the above pseudocode. Implement the queue interface defined here. Name this
implementation myQueue2.

Analysis Problem 1 (not to be submitted): Argue for yourself that the above two
correctly implements a queue using two stacks.

Analysis Problem 2 (not to be submitted): Try to figure out why the second
implementation is much better than the first one.

Parallel Programming
This is an important programming paradigm where you can run multiple “threads” of
execution in parallel. Parallel programming is useful in reducing the running time, or deal
with asynchronous inputs, or sometimes just to model the problem in a better manner.
Here we will use Java threads to get a taste of parallel programming.
Programming Problem 4: You have to create multiple java threads (number of threads
will be specified as input). Each thread will read from a separate file and execute the
operation on a shared queue. You have to use the queue that you have made in
programming problems 2 and 3. The implementation you have to use will be specified in
the input. Note that you should make proper use of synchronization.

Input-output format for programming problems 4: Programming problem 4 should


use your implementations in programming problems 1,2, and 3. We will not specifically
check your implementation of programming problems 1,2, and 3.

Program name: You must write a Simulate.java program which contains the main
method. This is the program that we will run to check your implementations.

Input: Your program should take two command line arguments that are both integers. The
first argument denotes the number of threads your program should run. The second
argument tells you which implementation of a queue (myQueue1 or myQueue2) you
should be using. For example, consider that you run the following command:

java Simulate 5, 1

This means that you must run 5 threads and use your 1st queue implementation (i.e.,
myQueue1).

The queue operations must be read from files named operations-x.dat. For example if
there are 5 threads, thread 1 reads from operations-1.dat file, thread 2 reads from
operations-2.dat file and so on. Each file contains enqueue and dequeue statements in
separate lines. Following is an example of an operations-x.dat file:

enqueue: <a>
enqueue: <b>
enqueue: <c>
dequeue
dequeue
enqueue: <d>
dequeue

<a>, <b>, etc. denote arbitrary strings.

Output: Your program should output two files. First it should produce a file
named operations-out.dat which should contain the enqueue/dequeue operation and the
thread number which executes this operation. For example suppose thread #1
executed enqueue(a), dequeue(), enqueue(b), and then thread #2 executed dequeue().
Then your operations-out.dat file should be the following:

1, enqueue: <a>
1, dequeue
1, enqueue: <b>
2, dequeue

Your second file should be named output.dat and it should contain the output of
the dequeue operations performed on the shared queue and the thread number performing
the operation. For example, corresponding to the above operations-out.dat file, your
output.dat file should be the following:

1, <a>
2, <b>

Your program must handle the errors in input file format as exception. These must be
caught and reported on the command line.

Empty stack errors: In case of errors, for example, the queue is empty and there is
a dequeue request. The output.dat file should contain the appropriate exception name for
that request. For example, there is a single thread and the input file operations-1.dat is the
following:

1, enqueue: <a>
1, dequeue
1, dequeue
1, enqueue: <b>
1, dequeue

Then the output.dat file should look like:

1, <a>
1, EmptyStackException
1, <b>

You might also like