Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 8 additions & 29 deletions src/dataStructures/queue/MonotonicQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import java.util.ArrayDeque;

/**
* Implementation of a non-increasing monotonic queue for certain (but common) use case:
* Implementation of a non-increasing (decreasing) monotonic queue for certain (but common) use case:
* When larger objects pushed to the queue are able to replace and represent smaller objects that come before it.
* Callable methods are:
* isEmpty()
Expand All @@ -20,8 +20,9 @@
* 4 2 [1, 2] #2->1 [5, 3, 2] 2 kick out 1 #2->3
* 5 4 [1, 2, 4] #4->2 [5,4] 4 kick out 2, 3 #4->2
*/

public class MonotonicQueue<T extends Comparable<T>> {
private Deque<Pair<T>> dq = new ArrayDeque<>(); // or LinkedList
private Deque<T> dq = new ArrayDeque<>(); // or LinkedList

/**
* Checks if queue is empty.
Expand All @@ -37,12 +38,10 @@ public boolean isEmpty() {
*/
public void push(T obj) {
Integer count = 0;
while (!dq.isEmpty() && obj.compareTo(dq.peekLast().value) >= 0) {
Pair<T> popped = dq.pollLast();
// accumulate count of objects that were popped to maintain the non-increasing property
count += 1 + popped.countDeleted;
while (!dq.isEmpty() && obj.compareTo(dq.peekLast()) > 0) {
dq.pollLast(); // Removes elements that do not conform the non-increasing sequence.
}
dq.offerLast(new Pair<T>(obj, count));
dq.offerLast(obj);
}

/**
Expand All @@ -53,7 +52,7 @@ public T max() {
if (isEmpty()) {
return null;
}
return dq.peek().value;
return dq.peek();
}

/**
Expand All @@ -64,26 +63,6 @@ public T pop() {
if (dq.isEmpty()) {
return null;
}
Pair<T> node = dq.peek();
if (node.countDeleted > 0) {
node.countDeleted -= 1;
return node.value;
}
dq.poll();
return node.value;
}

/**
* Node class that is represented as a pair.
* Tracks the deleted count and the value to be wrapped.
*/
private static class Pair<T> {
private T value;
private Integer countDeleted;

private Pair(T val, Integer count) {
this.value = val;
this.countDeleted = count;
}
return dq.poll(); // Returns & remove head of deque
}
}
57 changes: 57 additions & 0 deletions test/dataStructures/queue/MonotonicQueueTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package test.dataStructures.queue;

import org.junit.Assert;
import org.junit.Test;
import src.dataStructures.queue.MonotonicQueue;
public class MonotonicQueueTest {
@Test
public void testEmpty() {
MonotonicQueue<Integer> q = new MonotonicQueue<>();
Assert.assertEquals(true, q.isEmpty());
Assert.assertEquals(null, q.max());
Assert.assertEquals(null, q.pop());
}

@Test
public void testMax() {
MonotonicQueue<Integer> q = new MonotonicQueue<>();
q.push(2);
Assert.assertEquals("2", q.max().toString());
q.push(7);
Assert.assertEquals("7", q.max().toString());
q.push(1);
Assert.assertEquals("7", q.max().toString());
q.push(7);
Assert.assertEquals("7", q.max().toString());
q.push(5);
Assert.assertEquals("7", q.max().toString());
q.push(4);
Assert.assertEquals("7", q.max().toString());
q.push(3);
q.push(2);
q.push(5);
Assert.assertEquals("7", q.max().toString());
}

@Test
public void testPop() {
MonotonicQueue<Integer> q = new MonotonicQueue<>();
q.push(2);
q.push(7);
q.push(1);
q.push(7);
q.push(5);
q.push(4);
q.push(3);
q.push(2);
q.push(5);
q.push(2);

Assert.assertEquals("7", q.pop().toString());
Assert.assertEquals("7", q.pop().toString());
Assert.assertEquals("5", q.pop().toString());
q.pop();
Assert.assertEquals("2", q.pop().toString());
Assert.assertEquals(null, q.pop());
}
}