Introduction
A queue is a linear data structure that follows the First In, First Out (FIFO) principle, where elements are added (enqueued) to the rear of the queue and removed (dequeued) from the front. Unlike an array-based implementation, a linked list-based queue dynamically adjusts its size as elements are added or removed. This guide will walk you through writing a Java program that implements a queue using a singly linked list.
Problem Statement
Create a Java program that:
- Implements a queue using a linked list.
- Provides methods to perform standard queue operations:
enqueue
,dequeue
,peek
, andisEmpty
. - Displays the queue contents after various operations.
Example:
- Input: Enqueue elements:
10, 20, 30
- Operations:
enqueue(40)
,dequeue()
,peek()
- Output:
- After enqueuing 40:
10 -> 20 -> 30 -> 40 -> null
- After dequeuing:
20 -> 30 -> 40 -> null
- Peeked element:
20
- After enqueuing 40:
Solution Steps
- Create the Node Structure: Define a
Node
class to represent each element in the queue. - Create the Queue Structure: Define a
Queue
class to manage the queue using a linked list, including methods for standard queue operations. - Implement Queue Operations: Implement methods to enqueue elements into the queue, dequeue elements from the queue, peek at the front element, and check if the queue is empty.
- Display the Queue: Output the queue’s contents after performing various operations.
Java Program
// Java Program to Implement a Queue Using Linked List // Author: https://www.rameshfadatare.com/ // Step 1: Define the Node class to represent each element in the queue class Node { int data; Node next; // Constructor to initialize the node public Node(int data) { this.data = data; this.next = null; } } // Step 2: Define the Queue class to manage the queue operations using a linked list class Queue { private Node front, rear; // Constructor to initialize the queue (initially empty) public Queue() { this.front = this.rear = null; } // Step 3: Method to enqueue an element into the queue public void enqueue(int value) { // Create a new node with the given value Node newNode = new Node(value); // If the queue is empty, both front and rear point to the new node if (this.rear == null) { this.front = this.rear = newNode; System.out.println("Enqueued " + value + " into the queue."); return; } // Otherwise, add the new node at the end of the queue and change the rear this.rear.next = newNode; this.rear = newNode; System.out.println("Enqueued " + value + " into the queue."); } // Step 4: Method to dequeue an element from the queue public int dequeue() { if (isEmpty()) { System.out.println("Queue is empty. Unable to dequeue."); return -1; // Return a sentinel value indicating the queue is empty } // Move the front pointer to the next node int dequeuedValue = this.front.data; this.front = this.front.next; // If the front becomes null, then the queue is empty, so set rear to null if (this.front == null) { this.rear = null; } System.out.println("Dequeued " + dequeuedValue + " from the queue."); return dequeuedValue; } // Step 5: Method to peek at the front element of the queue public int peek() { if (isEmpty()) { System.out.println("Queue is empty. Nothing to peek."); return -1; // Return a sentinel value indicating the queue is empty } else { System.out.println("Peeked at the front element: " + this.front.data); return this.front.data; } } // Step 6: Method to check if the queue is empty public boolean isEmpty() { return (this.front == null); } // Step 7: Method to display the contents of the queue public void display() { if (isEmpty()) { System.out.println("Queue is empty."); } else { System.out.print("Queue contents: "); Node current = front; while (current != null) { System.out.print(current.data + " -> "); current = current.next; } System.out.println("null"); } } } public class QueueDemo { public static void main(String[] args) { Queue queue = new Queue(); // Create a queue // Enqueue elements into the queue queue.enqueue(10); queue.enqueue(20); queue.enqueue(30); queue.display(); // Enqueue another element queue.enqueue(40); queue.display(); // Dequeue an element from the queue queue.dequeue(); queue.display(); // Peek at the front element queue.peek(); queue.display(); // Dequeue all elements to test underflow queue.dequeue(); queue.dequeue(); queue.dequeue(); queue.dequeue(); // This dequeue should trigger an underflow condition queue.display(); } }
Explanation
Step 1: Define the Node Class
- The
Node
class represents a single element in the queue. Each node containsdata
and a reference to the next node (next
). - The constructor initializes the node with data and sets the
next
pointer tonull
.
Step 2: Define the Queue Class
- The
Queue
class manages the queue operations using a linked list. The class contains two pointers,front
andrear
, which point to the first and last nodes in the queue, respectively. - The queue is initialized as empty, with both
front
andrear
set tonull
.
Step 3: Enqueue an Element into the Queue
- The
enqueue()
method adds a new node to the rear of the queue:- A new node is created with the given value.
- If the queue is empty, both
front
andrear
point to the new node. - Otherwise, the
next
pointer of therear
node is updated to point to the new node, andrear
is updated to the new node.
Step 4: Dequeue an Element from the Queue
- The
dequeue()
method removes and returns the front element of the queue:- If the queue is empty, an underflow message is displayed.
- Otherwise, the
front
pointer is updated to point to the next node, and the data of the dequeued node is returned. - If the queue becomes empty after the dequeue operation,
rear
is set tonull
.
Step 5: Peek at the Front Element of the Queue
- The
peek()
method returns the front element without removing it:- If the queue is empty, an appropriate message is displayed.
- Otherwise, the
front
element’s data is returned.
Step 6: Check if the Queue is Empty
- The
isEmpty()
method checks if the queue is empty by verifying iffront
isnull
.
Step 7: Display the Queue Contents
- The
display()
method prints all elements currently in the queue:- The queue is traversed from the
front
to therear
, printing each element’s data.
- The queue is traversed from the
Output Example
Enqueued 10 into the queue. Enqueued 20 into the queue. Enqueued 30 into the queue. Queue contents: 10 -> 20 -> 30 -> null Enqueued 40 into the queue. Queue contents: 10 -> 20 -> 30 -> 40 -> null Dequeued 10 from the queue. Queue contents: 20 -> 30 -> 40 -> null Peeked at the front element: 20 Queue contents: 20 -> 30 -> 40 -> null Dequeued 20 from the queue. Dequeued 30 from the queue. Dequeued 40 from the queue. Queue is empty. Unable to dequeue. Queue is empty.
Conclusion
This Java program demonstrates how to implement a queue using a linked list, including handling underflow conditions when attempting to dequeue from an empty queue. The program efficiently manages queue operations, providing dynamic growth and shrinkage, unlike array-based queues. This exercise is valuable for understanding how to implement and manipulate queues using linked lists in Java programming.