SlideShare a Scribd company logo
Works Applications Programming Assignment
Chinmay Chauhan, 09005010, IIT-Bombay
Interface1 Implementation (ExamPeekableQueue):
File: ExamPeekableQueue.java
package jp.co.worksapp.recruiting;
public interface ExamPeekableQueue<E extends Comparable<E>> {
public void enqueue(E e);
public E dequeue();
public E peekMedian();
public E peekMaximum();
public E peekMinimum();
public int size();
}
I have used 2 approaches to solve the problem. Below is the code and logic used for both approaches.
Approach #1: We maintain a sorted list of elements in queue. For enqueue, whenever we insert an element in
queue, we find the correct location of that element in the sorted List using binarySearch and insert it at that
location. As per java documentation, ArrayList.add operation takes amortized O(1) time, binary search takes
O(lgN) time where N is the number of elements in the queue, so overall complexity of insertion is O(lgN)
amortized, and O(N) worst case. Same complexity holds for dequeue operation. So we have O(lgN) amortized
time for enqueue and dequeue operation, but worst case is O(N). Finding minimum, maximum and median once
we have a sorted list is O(1) worst case.
Code for Approach #1 (File: ExamPeekableQueueImpl.java)
package jp.co.worksapp.recruiting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
public class ExamPeekableQueueImpl<E extends Comparable<E>> implements ExamPeekableQueue<E>{
private List<E> queue;
// For storing queue elements in sorted Order for efficiently getting Min, Max, Median
private List<E> sortedQueue;
public ExamPeekableQueueImpl(){
queue = new ArrayList<E>();
sortedQueue = new ArrayList<E>();
}
@Override
public void enqueue(E e){
if (e == null) {
throw new IllegalArgumentException();
}
queue.add(e); // Adds e to the tail of queue
int index = Collections.binarySearch(sortedQueue, e);
if (index < 0) { // Element e is not present in the queue
sortedQueue.add(-index - 1, e);
} else { // Element e is present in the queue
sortedQueue.add(index + 1, e);
}
}
@Override
public E dequeue(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
E e = queue.get(0);
queue.remove(0);
int index = Collections.binarySearch(sortedQueue, e);
sortedQueue.remove(index); // Remove e from sortedQueue
return e;
}
@Override
public E peekMedian(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
int size = this.size();
return sortedQueue.get(size/2);
}
@Override
public E peekMaximum(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
int size = this.size();
return sortedQueue.get(size-1);
}
@Override
public E peekMinimum(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
return sortedQueue.get(0);
}
@Override
public int size(){
return queue.size();
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("[ ");
for (E e : queue) {
s.append(e + " ");
}
s.append("]");
s.append(" [ ");
for (E e : sortedQueue) {
s.append(e + " ");
}
s.append("]");
return s.toString();
}
}
Approach #2: We maintain 2 TreeSets, 1st
one contains N/2 (or N/2 + 1) smallest elements and 2nd
one contains
the rest. If we maintain this invariant throughout, we see that median has to be either the Largest element of
TreeSet1 or the smallest element of TreeSet2. Enqueue & Dequeue both operation take O(lgN) time since
inserting or deleting elements from any TreeSet of size N is O(lgN). Even in this case, the maximum, minimum
and median operation needs O(1) time. So we have significantly improved our complexity. However, since Java
TreeSet doesn’t support duplicates, so our code works only for distinct elements. If Java had a implementation
of MultiSet, then we could have support for non-distinct elements too.
Code for Approach #2 (File: ExamPeekableQueueImpl.java)
package jp.co.worksapp.recruiting;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.TreeSet;
public class ExamPeekableQueueImpl<E extends Comparable<E>> implements ExamPeekableQueue<E>{
private List<E> queue;
private TreeSet<E> left;
private TreeSet<E> right;
public ExamPeekableQueueImpl(){
queue = new ArrayList<E>();
left = new TreeSet<E>();
right= new TreeSet<E>();
}
@Override
public void enqueue(E e){
if (e == null) {
throw new IllegalArgumentException();
}
queue.add(e);
if(left.isEmpty()){ // We are adding the first element
left.add(e);
return;
}
if (left.size() == 1 && right.size() == 0) {
if (e.compareTo(left.last()) == -1) {
right.add(left.last());
left.remove(left.last());
left.add(e);
}
else {
right.add(e);
}
return;
}
if(e.compareTo(left.last()) == -1 || e.compareTo(left.last()) == 0){ // Less or Equal
if (left.size() == right.size()) {
left.add(e);
}
else if (left.size() == right.size()+1) {
right.add(left.last());
left.remove(left.last());
left.add(e);
}
}
else if (e.compareTo(left.last()) == 1 && e.compareTo(right.first()) == -1) {
if (left.size() == right.size()){
left.add(e);
}
else if (left.size() == right.size()+1) {
right.add(e);
}
}
else {
if (left.size() == right.size()) {
left.add(right.first());
right.remove(right.first());
right.add(e);
}
else if (left.size() == right.size()+1) {
right.add(e);
}
}
}
@Override
public E dequeue(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
E e = queue.get(0);
// Maintain the invariant (left.size - right.size) == 0 / 1
if(e.compareTo(left.last()) == -1 || e.compareTo(left.last()) == 0){ // Less or Equal
if (left.size() == right.size()) {
left.add(right.first());
left.remove(e);
right.remove(right.first());
}
else if (left.size() == right.size()+1) {
left.remove(e);
}
}
else {
if (left.size() == right.size()) {
right.remove(e);
}
else if (left.size() == right.size() + 1){
right.remove(e);
right.add(left.last());
left.remove(left.last());
}
}
// Remove element from FIFO queue
queue.remove(0);
// If queue is empty, empty left and right TreeSets
if (queue.isEmpty()) {
if (!left.isEmpty()) left.remove(0);
if (!right.isEmpty()) right.remove(0);
}
return e;
}
@Override
public E peekMedian(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
if((left.size()+right.size())%2 == 0) return right.first();
else return left.last();
}
@Override
public E peekMaximum(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
if(right.isEmpty()) return left.last();
else return right.last();
}
@Override
public E peekMinimum(){
if (queue.isEmpty()) {
throw new NoSuchElementException();
}
return left.first();
}
@Override
public int size(){
return queue.size();
}
public String toString() {
StringBuilder s = new StringBuilder();
s.append("[ ");
for (E e : queue) {
s.append(e + " ");
}
s.append("]");
s.append(" [ ");
for (E e : left) {
s.append(e + " ");
}
s.append("]");
s.append(" [ ");
for (E e : right) {
s.append(e + " ");
}
s.append("]");
return s.toString();
}
}
Interface2 Implementation (ExamImmutableQueue):
Approach: We maintain 2 pointers enqueueHead, dequeueHead. We permanently add elements to the queue
using addElement(e) function. Functions enqueue(), dequeue(), peek() do not modify the queue and their
complexity is O(1) amortized. If we enqueue, elements are always appended at the start of enqueueHead list. If
while dequeuing, the dequeueHead is pointing to null, we move all elements from enqueueHead to
dequeueHead in the reverse Order. (More logic is explained as comments in source files)
File: ExamImmutableQueue.java
package jp.co.worksapp.recruiting;
public interface ExamImmutableQueue<E> {
public ExamImmutableQueue<E> enqueue(E e);
public ExamImmutableQueue<E> dequeue();
public E peek();
public int size();
}
File: ExamImmutableQueueImpl.java
package jp.co.worksapp.recruiting;
import java.util.NoSuchElementException;
/*
* Logic Used : We maintain 2 pointers enqueueHead, dequeueHead.
* We permanently add elements to the queue using addElement(e) function.
* Function enqueue/dequeue do not modify/mutate the queue.
* Functions enqueue(e) and dequeue() have O(1) amortized time complexity.
*
* If we enqueue, elements are always appended at the start of enqueueHead List, so
* if enqueueHead -> 3->2->1, and we do enqueueHead(10), then we return the appropriate
* pointer to 10->3->2->1, but DO NOT modify enqueueHead. So enqueueHead is still pointing to 3->2-
>1
* So enqueue(e) needs O(1) time in every case.
*
* If while dequeuing, the dequeueHead is pointing to null, we move all elements from
* enqueueHead to dequeueHead in the reverse Order.
* Eg. say enqueueHead -> 3->2->1, dequeueHead -> null, then on calling dequeue(),
* enqueueHead -> null, dequeueHead -> 1->2->3, so now dequeueHead points to the first
* element of queue. Only in this specific case we need O(N) time for dequeue(), in all
* other cases dequeue() is O(1). Hence the overall time complexity is O(1) amortized
*
* For peek(), if dequeueHead is not empty, then we return the first element that dequeueHead
* points to. Otherwise, we move all elements from enqueueHead list to dequeueHead (in reverse,
* and then return the first element that dequeueHead points to.
* Hence peek() has O(1) amortized time complexity.
*/
public class ExamImmutableQueueImpl<E> implements ExamImmutableQueue<E> {
class Node<T> {
public T data;
public Node<T> next;
public Node(T t){data = t; next = null;}
}
private Node<E> enqueueHead;
private Node<E> dequeueHead;
private int size;
public ExamImmutableQueueImpl() {
enqueueHead = null;
dequeueHead = null;
size = 0;
}
public ExamImmutableQueueImpl(Node<E> e, Node<E> d, int sz) {
enqueueHead = e;
dequeueHead = d;
size = sz;
}
@Override
public ExamImmutableQueue<E> enqueue(E e){
if (e == null) {
throw new IllegalArgumentException();
}
Node<E> temp = new Node<E>(e);
temp.next = enqueueHead;
return new ExamImmutableQueueImpl<E> (temp, dequeueHead, size+1);
}
@Override
public ExamImmutableQueue<E> dequeue(){
if (dequeueHead == null && enqueueHead == null){
throw new NoSuchElementException();
}
if(dequeueHead != null){
return new ExamImmutableQueueImpl<E> (enqueueHead, dequeueHead.next, size-1);
}
else {
while(enqueueHead != null){
Node<E> temp = new Node<E>(enqueueHead.data);
temp.next = dequeueHead;
dequeueHead = temp;
enqueueHead = enqueueHead.next;
}
return new ExamImmutableQueueImpl<E>(enqueueHead, dequeueHead.next, size-1);
}
}
// To permanently add some elements to the queue
public void addElement(E e) {
Node<E> temp = new Node<E>(e);
temp.next = enqueueHead;
enqueueHead = temp;
size++;
}
@Override
public E peek(){
if (enqueueHead == null && dequeueHead == null){
throw new NoSuchElementException();
}
else if (dequeueHead != null){
return dequeueHead.data;
}
else { // Move all elements of enqueueHead to dequeueHead in reverse order
while(enqueueHead != null){
Node<E> temp = new Node<E>(enqueueHead.data);
temp.next = dequeueHead;
dequeueHead = temp;
enqueueHead = enqueueHead.next;
}
return dequeueHead.data;
}
}
@Override
public int size(){
return size;
}
// For checking contents of the queue, Printing the queue contents
public String toString() {
if (dequeueHead != null) { // Print dequeueHead before enqueueHead
String s = "";
s = "[ " + s;
Node<E> tmp = dequeueHead;
while (tmp != null) {
s = s + tmp.data + " ";
tmp = tmp.next;
}
String t = "";
tmp = enqueueHead;
while (tmp != null) {
t = tmp.data + " " + t;
tmp = tmp.next;
}
s = s+t + "]";
return s;
}
else { // Since dequeueHead is null, just print enqueueHead in reverse
String s = "";
Node<E> tmp = enqueueHead;
while (tmp != null) {
s = tmp.data + " " + s;
tmp = tmp.next;
}
s = "[ " + s + "]";
return s;
}
}
}

More Related Content

What's hot (20)

PPTX
Java весна 2013 лекция 2
Technopark
 
PDF
Java 8 - Nuts and Bold - SFEIR Benelux
yohanbeschi
 
PDF
Java programs
Mukund Gandrakota
 
PPTX
Introduction to nsubstitute
Suresh Loganatha
 
PDF
Java 8 Stream API. A different way to process collections.
David Gómez García
 
PDF
Important java programs(collection+file)
Alok Kumar
 
PPTX
Basic java, java collection Framework and Date Time API
jagriti srivastava
 
PDF
An Introduction to Property Based Testing
C4Media
 
PDF
Java Puzzlers
Mike Donikian
 
ODP
Akka
Tim Dalton
 
PDF
Java puzzles
Nikola Petrov
 
PDF
Java Puzzle
SFilipp
 
KEY
Groovy 1.8の新機能について
Uehara Junji
 
PDF
The Ring programming language version 1.5.1 book - Part 31 of 180
Mahmoud Samir Fayed
 
DOCX
Java PRACTICAL file
RACHIT_GUPTA
 
PDF
Advanced Java Practical File
Soumya Behera
 
PDF
Let's go Developer 2011 sendai Let's go Java Developer (Programming Language ...
Uehara Junji
 
PPT
Swiss army knife Spring
Mario Fusco
 
PDF
Guice2.0
Masaaki Yonebayashi
 
PPT
Simple Java Programs
AravindSankaran
 
Java весна 2013 лекция 2
Technopark
 
Java 8 - Nuts and Bold - SFEIR Benelux
yohanbeschi
 
Java programs
Mukund Gandrakota
 
Introduction to nsubstitute
Suresh Loganatha
 
Java 8 Stream API. A different way to process collections.
David Gómez García
 
Important java programs(collection+file)
Alok Kumar
 
Basic java, java collection Framework and Date Time API
jagriti srivastava
 
An Introduction to Property Based Testing
C4Media
 
Java Puzzlers
Mike Donikian
 
Java puzzles
Nikola Petrov
 
Java Puzzle
SFilipp
 
Groovy 1.8の新機能について
Uehara Junji
 
The Ring programming language version 1.5.1 book - Part 31 of 180
Mahmoud Samir Fayed
 
Java PRACTICAL file
RACHIT_GUPTA
 
Advanced Java Practical File
Soumya Behera
 
Let's go Developer 2011 sendai Let's go Java Developer (Programming Language ...
Uehara Junji
 
Swiss army knife Spring
Mario Fusco
 
Simple Java Programs
AravindSankaran
 

Viewers also liked (7)

PDF
Increasing Resilience towards Floods in Mumbai city
Chinmay Chauhan
 
PPTX
An Introduction to Saville Comprehension Aptitude
JobTestPrep
 
PPTX
An Introduction to Saville Analysis Aptitude
JobTestPrep
 
PDF
Strategic Analysis of Microsoft Corp. (2014)
Chinmay Chauhan
 
PPT
Software Testing Life Cycle
Udayakumar Sree
 
PPT
Public transport problems in mumbai
aziz khan
 
PPTX
Software testing life cycle
Garuda Trainings
 
Increasing Resilience towards Floods in Mumbai city
Chinmay Chauhan
 
An Introduction to Saville Comprehension Aptitude
JobTestPrep
 
An Introduction to Saville Analysis Aptitude
JobTestPrep
 
Strategic Analysis of Microsoft Corp. (2014)
Chinmay Chauhan
 
Software Testing Life Cycle
Udayakumar Sree
 
Public transport problems in mumbai
aziz khan
 
Software testing life cycle
Garuda Trainings
 
Ad

Similar to Works Applications Test - Chinmay Chauhan (20)

DOCX
package algs13;import stdlib.;import java.util.Iterator;im.docx
gerardkortney
 
DOCX
EmptyCollectionException-java -- - Represents the situation in which.docx
BlakeSGMHemmingss
 
PDF
Java Collections API
Alex Miller
 
PDF
I only need help with four methods in the EmployeeManager class the .pdf
arpitcomputronics
 
PPTX
Collection implementation classes - Arraylist, linkedlist, Stack
RajalakshmiS74
 
PPTX
Module-1 Updated Collection Framework.pptx
rekhakeerti19
 
PDF
Listings for BinaryHeap.java and BinaryHeapTest.java are shown in th.pdf
RAJATCHUGH12
 
PDF
CS3381 OBJECT ORIENTED PROGRAMMINGLABS_1.pdf
deepak14367
 
PDF
Description (Part A) In this lab you will write a Queue implementati.pdf
rishabjain5053
 
PDF
computer notes - Priority queue
ecomputernotes
 
PDF
1. Suppose you want to implement an ADT in which you can insert valu.pdf
forwardcom41
 
PDF
Using NetBeansImplement a queue named QueueLL using a Linked List .pdf
siennatimbok52331
 
PDF
CS253: Priority queues (2019)
Jinho Choi
 
PDF
For problems 3 and 4, consider the following functions that implemen.pdf
anjandavid
 
PPT
queuesArrays.ppt bbbbbbbbbbbbbbbbbbbbbbbbbb
RAtna29
 
DOCX
Java Foundations StackADT-java --- - Defines the interface to a stack.docx
VictorXUQGloverl
 
PDF
PLEASE MAKE SURE THE PROGRAM IS ASKING FOR INPUT FROM USER TO ADD OR.pdf
mallik3000
 
PDF
Create a new java class called ListNode. Implement ListNode as a gen.pdf
mohamednihalshahru
 
PPTX
Basic Queue Operation in DataStructure.pptx
LakshmiSamivel
 
package algs13;import stdlib.;import java.util.Iterator;im.docx
gerardkortney
 
EmptyCollectionException-java -- - Represents the situation in which.docx
BlakeSGMHemmingss
 
Java Collections API
Alex Miller
 
I only need help with four methods in the EmployeeManager class the .pdf
arpitcomputronics
 
Collection implementation classes - Arraylist, linkedlist, Stack
RajalakshmiS74
 
Module-1 Updated Collection Framework.pptx
rekhakeerti19
 
Listings for BinaryHeap.java and BinaryHeapTest.java are shown in th.pdf
RAJATCHUGH12
 
CS3381 OBJECT ORIENTED PROGRAMMINGLABS_1.pdf
deepak14367
 
Description (Part A) In this lab you will write a Queue implementati.pdf
rishabjain5053
 
computer notes - Priority queue
ecomputernotes
 
1. Suppose you want to implement an ADT in which you can insert valu.pdf
forwardcom41
 
Using NetBeansImplement a queue named QueueLL using a Linked List .pdf
siennatimbok52331
 
CS253: Priority queues (2019)
Jinho Choi
 
For problems 3 and 4, consider the following functions that implemen.pdf
anjandavid
 
queuesArrays.ppt bbbbbbbbbbbbbbbbbbbbbbbbbb
RAtna29
 
Java Foundations StackADT-java --- - Defines the interface to a stack.docx
VictorXUQGloverl
 
PLEASE MAKE SURE THE PROGRAM IS ASKING FOR INPUT FROM USER TO ADD OR.pdf
mallik3000
 
Create a new java class called ListNode. Implement ListNode as a gen.pdf
mohamednihalshahru
 
Basic Queue Operation in DataStructure.pptx
LakshmiSamivel
 
Ad

Recently uploaded (20)

DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PDF
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
PDF
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
PPTX
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
PPTX
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
PDF
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
PPTX
MuleSoft MCP Support (Model Context Protocol) and Use Case Demo
shyamraj55
 
PDF
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PDF
CIFDAQ Market Wrap for the week of 4th July 2025
CIFDAQ
 
PDF
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PDF
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
PPT
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
PDF
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
PDF
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
PDF
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
NASA A Researcher’s Guide to International Space Station : Physical Sciences ...
Dr. PANKAJ DHUSSA
 
Agentic AI lifecycle for Enterprise Hyper-Automation
Debmalya Biswas
 
The Project Compass - GDG on Campus MSIT
dscmsitkol
 
Future Tech Innovations 2025 – A TechLists Insight
TechLists
 
“Squinting Vision Pipelines: Detecting and Correcting Errors in Vision Models...
Edge AI and Vision Alliance
 
MuleSoft MCP Support (Model Context Protocol) and Use Case Demo
shyamraj55
 
Transforming Utility Networks: Large-scale Data Migrations with FME
Safe Software
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
CIFDAQ Market Wrap for the week of 4th July 2025
CIFDAQ
 
“Computer Vision at Sea: Automated Fish Tracking for Sustainable Fishing,” a ...
Edge AI and Vision Alliance
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
AI Agents in the Cloud: The Rise of Agentic Cloud Architecture
Lilly Gracia
 
Ericsson LTE presentation SEMINAR 2010.ppt
npat3
 
Automating Feature Enrichment and Station Creation in Natural Gas Utility Net...
Safe Software
 
NLJUG Speaker academy 2025 - first session
Bert Jan Schrijver
 
Future-Proof or Fall Behind? 10 Tech Trends You Can’t Afford to Ignore in 2025
DIGITALCONFEX
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 

Works Applications Test - Chinmay Chauhan

  • 1. Works Applications Programming Assignment Chinmay Chauhan, 09005010, IIT-Bombay Interface1 Implementation (ExamPeekableQueue): File: ExamPeekableQueue.java package jp.co.worksapp.recruiting; public interface ExamPeekableQueue<E extends Comparable<E>> { public void enqueue(E e); public E dequeue(); public E peekMedian(); public E peekMaximum(); public E peekMinimum(); public int size(); } I have used 2 approaches to solve the problem. Below is the code and logic used for both approaches. Approach #1: We maintain a sorted list of elements in queue. For enqueue, whenever we insert an element in queue, we find the correct location of that element in the sorted List using binarySearch and insert it at that location. As per java documentation, ArrayList.add operation takes amortized O(1) time, binary search takes O(lgN) time where N is the number of elements in the queue, so overall complexity of insertion is O(lgN) amortized, and O(N) worst case. Same complexity holds for dequeue operation. So we have O(lgN) amortized time for enqueue and dequeue operation, but worst case is O(N). Finding minimum, maximum and median once we have a sorted list is O(1) worst case. Code for Approach #1 (File: ExamPeekableQueueImpl.java) package jp.co.worksapp.recruiting; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.NoSuchElementException; public class ExamPeekableQueueImpl<E extends Comparable<E>> implements ExamPeekableQueue<E>{ private List<E> queue; // For storing queue elements in sorted Order for efficiently getting Min, Max, Median private List<E> sortedQueue; public ExamPeekableQueueImpl(){ queue = new ArrayList<E>(); sortedQueue = new ArrayList<E>(); } @Override public void enqueue(E e){
  • 2. if (e == null) { throw new IllegalArgumentException(); } queue.add(e); // Adds e to the tail of queue int index = Collections.binarySearch(sortedQueue, e); if (index < 0) { // Element e is not present in the queue sortedQueue.add(-index - 1, e); } else { // Element e is present in the queue sortedQueue.add(index + 1, e); } } @Override public E dequeue(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } E e = queue.get(0); queue.remove(0); int index = Collections.binarySearch(sortedQueue, e); sortedQueue.remove(index); // Remove e from sortedQueue return e; } @Override public E peekMedian(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } int size = this.size(); return sortedQueue.get(size/2); } @Override public E peekMaximum(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } int size = this.size(); return sortedQueue.get(size-1); } @Override public E peekMinimum(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } return sortedQueue.get(0); } @Override public int size(){ return queue.size(); } public String toString() { StringBuilder s = new StringBuilder(); s.append("[ "); for (E e : queue) { s.append(e + " "); } s.append("]"); s.append(" [ ");
  • 3. for (E e : sortedQueue) { s.append(e + " "); } s.append("]"); return s.toString(); } } Approach #2: We maintain 2 TreeSets, 1st one contains N/2 (or N/2 + 1) smallest elements and 2nd one contains the rest. If we maintain this invariant throughout, we see that median has to be either the Largest element of TreeSet1 or the smallest element of TreeSet2. Enqueue & Dequeue both operation take O(lgN) time since inserting or deleting elements from any TreeSet of size N is O(lgN). Even in this case, the maximum, minimum and median operation needs O(1) time. So we have significantly improved our complexity. However, since Java TreeSet doesn’t support duplicates, so our code works only for distinct elements. If Java had a implementation of MultiSet, then we could have support for non-distinct elements too. Code for Approach #2 (File: ExamPeekableQueueImpl.java) package jp.co.worksapp.recruiting; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.TreeSet; public class ExamPeekableQueueImpl<E extends Comparable<E>> implements ExamPeekableQueue<E>{ private List<E> queue; private TreeSet<E> left; private TreeSet<E> right; public ExamPeekableQueueImpl(){ queue = new ArrayList<E>(); left = new TreeSet<E>(); right= new TreeSet<E>(); } @Override public void enqueue(E e){ if (e == null) { throw new IllegalArgumentException(); } queue.add(e); if(left.isEmpty()){ // We are adding the first element left.add(e); return; } if (left.size() == 1 && right.size() == 0) { if (e.compareTo(left.last()) == -1) { right.add(left.last()); left.remove(left.last()); left.add(e); } else { right.add(e); } return;
  • 4. } if(e.compareTo(left.last()) == -1 || e.compareTo(left.last()) == 0){ // Less or Equal if (left.size() == right.size()) { left.add(e); } else if (left.size() == right.size()+1) { right.add(left.last()); left.remove(left.last()); left.add(e); } } else if (e.compareTo(left.last()) == 1 && e.compareTo(right.first()) == -1) { if (left.size() == right.size()){ left.add(e); } else if (left.size() == right.size()+1) { right.add(e); } } else { if (left.size() == right.size()) { left.add(right.first()); right.remove(right.first()); right.add(e); } else if (left.size() == right.size()+1) { right.add(e); } } } @Override public E dequeue(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } E e = queue.get(0); // Maintain the invariant (left.size - right.size) == 0 / 1 if(e.compareTo(left.last()) == -1 || e.compareTo(left.last()) == 0){ // Less or Equal if (left.size() == right.size()) { left.add(right.first()); left.remove(e); right.remove(right.first()); } else if (left.size() == right.size()+1) { left.remove(e); } } else { if (left.size() == right.size()) { right.remove(e); } else if (left.size() == right.size() + 1){ right.remove(e); right.add(left.last()); left.remove(left.last()); } } // Remove element from FIFO queue queue.remove(0); // If queue is empty, empty left and right TreeSets if (queue.isEmpty()) { if (!left.isEmpty()) left.remove(0); if (!right.isEmpty()) right.remove(0); }
  • 5. return e; } @Override public E peekMedian(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } if((left.size()+right.size())%2 == 0) return right.first(); else return left.last(); } @Override public E peekMaximum(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } if(right.isEmpty()) return left.last(); else return right.last(); } @Override public E peekMinimum(){ if (queue.isEmpty()) { throw new NoSuchElementException(); } return left.first(); } @Override public int size(){ return queue.size(); } public String toString() { StringBuilder s = new StringBuilder(); s.append("[ "); for (E e : queue) { s.append(e + " "); } s.append("]"); s.append(" [ "); for (E e : left) { s.append(e + " "); } s.append("]"); s.append(" [ "); for (E e : right) { s.append(e + " "); } s.append("]"); return s.toString(); } }
  • 6. Interface2 Implementation (ExamImmutableQueue): Approach: We maintain 2 pointers enqueueHead, dequeueHead. We permanently add elements to the queue using addElement(e) function. Functions enqueue(), dequeue(), peek() do not modify the queue and their complexity is O(1) amortized. If we enqueue, elements are always appended at the start of enqueueHead list. If while dequeuing, the dequeueHead is pointing to null, we move all elements from enqueueHead to dequeueHead in the reverse Order. (More logic is explained as comments in source files) File: ExamImmutableQueue.java package jp.co.worksapp.recruiting; public interface ExamImmutableQueue<E> { public ExamImmutableQueue<E> enqueue(E e); public ExamImmutableQueue<E> dequeue(); public E peek(); public int size(); } File: ExamImmutableQueueImpl.java package jp.co.worksapp.recruiting; import java.util.NoSuchElementException; /* * Logic Used : We maintain 2 pointers enqueueHead, dequeueHead. * We permanently add elements to the queue using addElement(e) function. * Function enqueue/dequeue do not modify/mutate the queue. * Functions enqueue(e) and dequeue() have O(1) amortized time complexity. * * If we enqueue, elements are always appended at the start of enqueueHead List, so * if enqueueHead -> 3->2->1, and we do enqueueHead(10), then we return the appropriate * pointer to 10->3->2->1, but DO NOT modify enqueueHead. So enqueueHead is still pointing to 3->2- >1 * So enqueue(e) needs O(1) time in every case. * * If while dequeuing, the dequeueHead is pointing to null, we move all elements from * enqueueHead to dequeueHead in the reverse Order. * Eg. say enqueueHead -> 3->2->1, dequeueHead -> null, then on calling dequeue(), * enqueueHead -> null, dequeueHead -> 1->2->3, so now dequeueHead points to the first * element of queue. Only in this specific case we need O(N) time for dequeue(), in all * other cases dequeue() is O(1). Hence the overall time complexity is O(1) amortized * * For peek(), if dequeueHead is not empty, then we return the first element that dequeueHead * points to. Otherwise, we move all elements from enqueueHead list to dequeueHead (in reverse, * and then return the first element that dequeueHead points to. * Hence peek() has O(1) amortized time complexity. */ public class ExamImmutableQueueImpl<E> implements ExamImmutableQueue<E> { class Node<T> { public T data; public Node<T> next; public Node(T t){data = t; next = null;} }
  • 7. private Node<E> enqueueHead; private Node<E> dequeueHead; private int size; public ExamImmutableQueueImpl() { enqueueHead = null; dequeueHead = null; size = 0; } public ExamImmutableQueueImpl(Node<E> e, Node<E> d, int sz) { enqueueHead = e; dequeueHead = d; size = sz; } @Override public ExamImmutableQueue<E> enqueue(E e){ if (e == null) { throw new IllegalArgumentException(); } Node<E> temp = new Node<E>(e); temp.next = enqueueHead; return new ExamImmutableQueueImpl<E> (temp, dequeueHead, size+1); } @Override public ExamImmutableQueue<E> dequeue(){ if (dequeueHead == null && enqueueHead == null){ throw new NoSuchElementException(); } if(dequeueHead != null){ return new ExamImmutableQueueImpl<E> (enqueueHead, dequeueHead.next, size-1); } else { while(enqueueHead != null){ Node<E> temp = new Node<E>(enqueueHead.data); temp.next = dequeueHead; dequeueHead = temp; enqueueHead = enqueueHead.next; } return new ExamImmutableQueueImpl<E>(enqueueHead, dequeueHead.next, size-1); } } // To permanently add some elements to the queue public void addElement(E e) { Node<E> temp = new Node<E>(e); temp.next = enqueueHead; enqueueHead = temp; size++; } @Override public E peek(){ if (enqueueHead == null && dequeueHead == null){ throw new NoSuchElementException(); } else if (dequeueHead != null){ return dequeueHead.data; } else { // Move all elements of enqueueHead to dequeueHead in reverse order while(enqueueHead != null){ Node<E> temp = new Node<E>(enqueueHead.data); temp.next = dequeueHead; dequeueHead = temp;
  • 8. enqueueHead = enqueueHead.next; } return dequeueHead.data; } } @Override public int size(){ return size; } // For checking contents of the queue, Printing the queue contents public String toString() { if (dequeueHead != null) { // Print dequeueHead before enqueueHead String s = ""; s = "[ " + s; Node<E> tmp = dequeueHead; while (tmp != null) { s = s + tmp.data + " "; tmp = tmp.next; } String t = ""; tmp = enqueueHead; while (tmp != null) { t = tmp.data + " " + t; tmp = tmp.next; } s = s+t + "]"; return s; } else { // Since dequeueHead is null, just print enqueueHead in reverse String s = ""; Node<E> tmp = enqueueHead; while (tmp != null) { s = tmp.data + " " + s; tmp = tmp.next; } s = "[ " + s + "]"; return s; } } }