Tuesday, April 24, 2012

The Minimum Value in a Queue

[Question]
How to design a queue A, in addition to insert(), delete(), also has a function min() which returns the minimum element? Can you do all functions in O(1) time?

[Analysis]
Need an additional queue X to record each potential minimum elements along the way. Those are elements in queue A that are smaller than the last incoming element (the end of the queue). When adding a new element,  queue X will need to remove the elements that are larger than the new element.

The time complexity for min(), insert() and delete() is amortized O(1).

[Solution]

import java.util.*;

public class QueueWithWeights implements TestCaseRunner{

// -- interface of weights
public interface WeightedObject {
public int getWeight();
}

// -- class WeightedString is for testing purpose;
public class WeightedString implements WeightedObject {
String str;
int wgt;

WeightedString (String a, int w) {
str = a;
wgt = w;
}
public int getWeight() {
return wgt;
}
}

// -- members of the queue;
List<WeightedObject> oList;
List<Integer> wList;

// -- constructor;
QueueWithWeights() {
oList = new ArrayList<WeightedObject> ();
wList = new ArrayList<Integer>();
}

public  void insert(WeightedObject a) {
oList.add(a);
int i = wList.size()-1;
while (i>=0 && a.getWeight()<wList.get(i)) {
wList.remove(i--); //amortized O(1);
}
wList.add(a.getWeight());
}

public  WeightedObject delete() {
if (oList.size()>0) {
if (oList.get(0).getWeight() == wList.get(0)) {
wList.remove(0);
}
return oList.remove(0);
}
return null;
}

public  int min() {
if (oList.isEmpty()) return Integer.MIN_VALUE;
return wList.get(0);
}

public void runTestCases() {
int[] w = {3, 4, 5, 8, 5, 3, 2, 1, 2, 3, 9 , 10, 9, 8, 7, 6, 5, 4, 3, 6, 7};
QueueWithWeights que = new QueueWithWeights();
int j = 3;

for (int i: w) {
WeightedString s = new WeightedString(String.valueOf(i), i);
que.insert(s);
System.out.println("inserted " + i);
System.out.println("min " + que.min());
j--;
if (j==0) {
System.out.println("delete "+que.delete().getWeight());
System.out.println("min " + que.min());
System.out.println("delete "+que.delete().getWeight());
System.out.println("min " + que.min());
j=3;
}
}
}
}

2 comments:

  1. I am not able to understand the logic. Can you please explain in more detail?

    ReplyDelete
    Replies
    1. Yeah, the code is not intuitive.

      Along with the Queue A [0..n], the idea is to keep an accounting Queue X [0..n]. X[i] refers to the minimal of Queue A, when i-th element becomes the head of queue. The Queue X's elements are to be updated when new element is added at the tail of Queue A.

      For example, data in (..) is to be added to Queue A:

      A: 4, (1, 5, 7, 0, 6, )
      X: 4,

      A: 4, 1, (5, 7, 0, 6, )
      X: 1, 1, //X[0] changed

      A: 4, 1, 5, (7, 0, 6, )
      X: 1, 1, 5,


      A: 4, 1, 5, 7, (0, 6, )
      X: 1, 1, 5, 7,


      A: 4, 1, 5, 7, 0, (6, )
      X: 0, 0, 0, 0, 0, //change all X up to X[0]

      A: 4, 1, 5, 7, 0, 6,
      X: 0, 0, 0, 0, 0, 6,

      The X[] can be packed during operation. Like in the example, at the last step, X needs only [0,6] two elements to know the minimal of Queue.

      Delete