Table of Contents
The resolution of this exercise expects that the student learns to implement a stack using linked lists as data structure.
Implement an stack using a linked list as data structure. The stack must allow to store any object type and you must implement at least the following methods:
- Insert an object in the stack (push).
- Retrieve an object from the stack (pop).
- Retrieve the object from the top of the stack, without extracting.
- Find out if there is any object in the stack.
- Return the number of objects in the stack.
Design the class deciding methods name, their parameters and return value. (Tip: Create a Node class which stores the stack values).
Nodes in a linked list, like the one you are going to use in this
exercise, are linked to each other using the
next
attribute of
Node
class. On the other hand, every node uses the
elem
attribute to point to the object that it has to store in it.
Implement a test class so that it reads lines from a file and prints them to console in reverse order. For example, if the file has four lines "A", "B", "C" and "D", it should print lines "D", "C", "B", "A".
In this exercise, you will have to implement a linked list - based queue. Then, further methods will be added so that new functionalities can be achieved in order to reinforce concepts about lists.
A queue is a data structure where insertion and deletion of elements is made following the FIFO (First In First Out) principle. That is, the first element that is extracted from the queue is also the first element that is introduced into the queue.
The queue is represented as a linked list. One of the ends can be considered as the top of the queue (that is, the node referenced as top). The other end represents the last position of the queue (the node whose next node is null). Therefore, extracting an element out the the queue consists on returning the top's object and unlinking it. On the other hand, insertion of a new object consists of locating the last node and linking it to the new object's node. To avoid crossing the whole list to find the last node, it is recommended to have an attribute that points to the last node of the queue. Thik of this linked list as a queue at market. The grocer knows who goes first and who is the last one. Every customer knows who follows him.
Nodes in a linked list, like the one you are going to use in this
exercise, are linked to each other using the
next
attribute of
Node
class. On the other hand, every node uses the
info
attribute to point to the object that it has to store in it.
As it is shown,
LinkedQueue
class mantains two attributes, a
top
that points to the top of the queue and where elements are
extracted from.
The other one is the
tail
attribute, where elements are
introduced
into the queue. The FIFO's principle is carried out in this way.
We are modelling the reception and process of purchase orders in a company.
Let's suppose that the company makes only one product.
The company receives orders from different customers.
Every order has two characteristics: customer and units of product.
The orders are processed by order (FIFO).
Below, you can download the definition of (class Order
).
Order.java
public class Order {
private String customer;
private int qtty;
public Order(int qtty, String customer) {
this.customer=customer;
this.qtty = qtty;
}
public void print (){
System.out.println(" Customer: " + this.getCustomer());
System.out.println(" Quantity: " + this.getQtty());
System.out.println(" ------------");
}
public int getQtty(){
return this.qtty;
}
public String getCustomer(){
return this.customer;
}
}
Implement the
Queue
class that implements the following interface:
public interface QueueInterface { // returns the number of elements in Queue public int size(); // verifies if the Queue is empty. true if empty | false if not empty. public boolean isEmpty(); // returns the first element in Queue. The element IS NOT REMOVED from Queue. Returns null if empty. public Object front(); // adds a new element to the Queue. public void enqueue(Object info); // returns the first element in Queue. The element IS REMOVED from Queue. Returns null if empty. public Object dequeue(); }
To do it, implement also the
Node
class that represents each node in the linked queue.
Every node will have 2 references: one to the next node in the list,
and one to the element attached to that node.
Implement a TestQueue
class with a main
to test the code.
To better test the code, add a new method printInfo()
to the Queue class. This method should print the size of the queue and should go from first element to last one and present the data contained inside each order, by using the print()
method from Order
class.
That is, it should present something similar to:
********* QUEUE DUMP ********* Size: 4 ** Element 1 Customer: cust1 Quantity: 20 ------------ ** Element 2 Customer: cust2 Quantity: 30 ------------ ** Element 3 Customer: cust3 Quantity: 40 ------------ ** Element 4 Customer: cust3 Quantity: 50 ------------ ******************************
The program has to walk the linked list
in order to show its elements. It begins with the first
node, then the second node is reached through the next
reference in the first one, then the third node, and so on until
the end of the list is reached. The code for that should be something
like:
Node node = top; while (node != null) { // print here the information of the node (...) node = node.getNext(); }
In the main
method, create a new queue and, at least, 3 new orders.
Add them to the queue and test the methods front
and dequeue
.
To better check the effect, after every insertion or deletion, use the
printInfo
method.
Once we feel comfortable with a very simple queue, we will make it a little bit complex:
Implement a method to obtain the n-th element, without deleting it.
It must return null
if the position is not valid.
public Object getNth(int pos)
To test it, try to add 4 elements and obtain the third one.
To study in depth the use of linked list data structures.
The List
class represents a linear collection of data organized in a linked list. This list shows the peculiarity of storing a reference to a particular node in the list, which is pointed by the reference currentPosition
and on which data insertion and extraction operations are performed.
The reference currentPosition
must be able to move forwards and backwards through the list, position by position. It is therefore appropriate for the list to be double linked, i.e. each node store a reference not only to the following node in the list, but also to the node that precedes it. The following figure illustrates the structure of the list with an example:
The basic operations this list should allow are:
Move the reference position forwards: the reference is moving forwards as many positions as indicated. If the list is empty or the number of positions to move forwards is not greater than zero, nothing is done. If it is required to move it forwards more positions than number of nodes from the current position up until the end of the list, advance currentPosition
only until the last node. If currentPosition
points to null
before proceeding, it is first placed pointing to the first node of the list, and then moved forwards the specified number of positions minus one (for example, if it is required to move 5 positions forwards and currentPosition
points initially to null
, then currentPosition
is first placed pointing to the first node, and then moved 4 positions from this first one).
Move the currentPosition
reference backwards: the reference currentPosition
must go backwards as many positions as indicated. If the list is empty or the number of positions to move backwards is not greater than zero, nothing is done. If it is required to move backwards more positions than number of nodes from the current position up until the first node of the list, the reference will remain in this first node.
Insert new data in the list: a new node is inserted to save this data, immediately following currentPosition
, i.e. the new node will follow the node currently referenced by currentPosition
. When currentPosition
is null
, the new node is inserted into the top of the list. After the insertion, currentPosition
must point to the new node inserted.
Extracting data from the list: return the data pointed by currentPosition
, delete the node, and moves currentPosition
to the next node (currentPosition
will point to null
if there is no next node). If currentPosition
is pointing to null
, nothing is extracted and null
is returned.
Program the Node
class, the List
class and the following methods of the List
class:
public void moveForwards(int numPositions) {...}
public void moveBackwards(int numPositions) {...}
public void insert(Object data) {...}
public Object extract() {...}
To learn how to work with Deques.
A Deque
, is a linear data structure that
permits to insert and to remove elements on both sides. To build it, we
will be based on the use of a Double Linked List (DLL) on which we will
implement the corresponding methods to achieve such behaviour.
A Deque
's behaviour can be achieved by the
implementation of the following interface that shows its main
methods:
/**
* Interface for a Deque.
*
* @author DIT-UC3M
*
*/
public interface Dqueue {
void insertFirst(Object obj);
void insertLast(Object obj);
Object removeFirst();
Object removeLast();
int size();
}
To implement a Deque
by using a DLL, perform
the following tasks:
Program the DNode
class which represents
the node of a Double Linked List (DLL). Such node will contain both
prev
and next
references to the previous and
next node of the current one, as well as an Object
reference to the data to be stored.
Declare the DLDqueue
class that will
implement a Deque
based on a DLL. In order to do this, it
must implement the Dqueue
interface that has been
previously mentioned.
Add the head
and tail
attributes that represents the nodes at both ends of the DLL and the
integer attribute size
that permits to store the size of
it.
Program the constructor of the DLDqueue
class that initializes the head
and tail
references to an emtpy DLL node and set the size to 0.
Program the following methods of the
Dqueue
's interface:
public void insertFirst(Object obj)
This method inserts an object at the beginnig of the DLL.
public void insertLast(Object obj)
This method inserts an object at the end of the DLL.
public Object removeFirst()
This method extracts an object from the beginning
of the DLL and removes it from the list. If such object does not
exist, the method returns null
.
public Object removeLast()
This method extracts an object from the end of the
DLL and removes it from the list. If such object does not exist,
the method returns null
.
public int size()
This method returns the size of the DLL list.
Finally, implement the public String
toString()
method that permits to print the content of the
Deque
on console according the next format (the shown
values are examples):
head [ 1-2-3-4-5 ] tail
IMPORTANT NOTE!!
In this exercise, we have practice with the design and
implementation of a Deque
. However, in the Java API there are
also several classes that implement such data structures. A
Deque
is defined by the following interface: Interface
Deque. There are also two possible class implemntations of it, one
that is based on arrays (ArrayDeque)
and another one based on linked lists (LinkedList).
To learn how to make a Deque
to behave as a
Stack
or a Queue
.
In this exercise, we will take advantage of the flexiblity
of a Deque
to make it behave as if it was a
Stack
or a Queue
. For this, we will force the
class using the Deque
to implement the corresponding
interface that is shown next:
Stack
interface:
/**
* Interface for a Stack.
*
* @author DIT-UC3M
*
*/
public interface Stack {
void push(Object obj);
Object pop();
int size();
}
Queue
interface:
/**
* Interface for a Queue.
*
* @author DIT-UC3M
*
*/
public interface Queue {
public void enqueue(Object obj);
public Object dequeue();
int size();
}
Next, let's use a Deque
to implement both a
Stack
or a Queue
. In order to do this, perform
the following tasks:
To implement a Stack
by a
Deque
:
Declare the DQStack
class that should
behave like a Stack
. In order to do this, it should
implement the Stack
interface that has been
previously mentioned.
Add a data
attribute of
DLDqueue
type which constitutes the list where
stack's data will be stored.
Program the DQStack
class constructor
that should initialize the data
attribute creating an
instance of a DLDqueue
class.
Program the following methods of the
Stack
's inteface:
public void push(Object obj)
This method inserts an object at the beginning
of the DLDqueue
.
public Object pop()
This method extracts an object from the
beginning of the DLDqueue
and it removes it from
the list. If such object does not exist, the method returns
null
.
public int size()
This method returns the size of the
DLDqueue
.
Implemet the main
method to test the
Stack
. It must instantiate a DQStack
object, pushing and popping several data while it prints on
console the content and the size of the stack.
To implement a Queue
by a
DLDqueue
:
Declare the DQQueue
class that should
behave like a Queue
. In order to do this, it should
implement the Queue
interface that has been
previously mentioned.
Add a data
attribute of
DLDqueue
type which constitutes the list where
queue's data will be stored.
Program the DQQueue
class constructor
that should initialize the data
attribute creating an
instance of a DLDqueue
class.
Program the following methods of the
Queue
's inteface:
public void enqueue(Object obj)
This method inserts an object at the beginning
of the DLDqueue
.
public Object dequeue()
This method extracts an object from the end of
the DLDqueue
and it removes it from the list. If
such object does not exist, the method returns
null
.
public int size()
This method returns the size of the
DLDqueue
.
Finally, implement the main
method to
test the Queue
. It must instantiate a
DQQueue
object, queueing and enqueueing several data
while it prints on console the content and the size of the
queue.