双标准优先级队列

时间:2014-08-20 21:52:27

标签: java priority-queue

如何使用两个标准实现优先级队列是否有一个不太复杂的方法?队列使用2 Comparator s创建,并提供(除了add)操作poll1()poll2(),其中每个操作根据相应的比较器移除并返回最小元素。

请注意,它与这些two questions没有任何共同之处。

动机

我的用例是Branch and Bound Optimization。当您获得无限时间时,以最佳界限扩大候选人可证明是最佳的。假设无限时间可证明是错误的。

严格遵循这一策略通常最终在截止日期到来时根本没有解决方案。一个简单的创可贴首先是将搜索引向解决方案,然后切换到最佳约束策略。这是相当不令人满意的,因为发现的第一个解决方案可能具有任意低质量。

这就是为什么我想使用两个标准队列:在一个步骤中,展开最佳边界候选者,在另一个步骤中,根据一些启发式扩展“最佳外观”候选者。

另一种可能的用途是用于帕累托优化。

5 个答案:

答案 0 :(得分:3)

使用两个TreeSet。每个集合都使用其受尊重的比较器,add()方法将提供的值添加到每个集合中。 poll1()将获取第一个集合中的first()值,并从两个集合中删除该值。同样适用于poll2()

add()poll1()poll2()将有Ο(log n )。

接下来是一个示例实现(真实的可能需要更多错误检查和其他方法):

class Priority2Queue <E>
{
    private TreeSet<E> q1;
    private TreeSet<E> q2;

    public Priority2Queue (Comparator<E> c1, Comparator<E> c2) {
        q1 = new TreeSet<E>(c1);
        q2 = new TreeSet<E>(c2);
    }

    public void add (E e) {
        q1.add(e);
        q2.add(e);
    }

    private E pollx (TreeSet<E> a, TreeSet<E> b) {
        E top = a.first();
        a.remove(top);
        b.remove(top);
        return top;
    }

    public E poll1 () { return pollx(q1, q2); }
    public E poll2 () { return pollx(q2, q1); }
}

答案 1 :(得分:2)

使用两个优先级队列。

执行此操作的一种方法是在一个人的民意调查中标记要删除的项目。如果另一个队列看到它,请跳过并移至下一个。我不确定这个的复杂性,但它将取决于一些微妙的问题,例如一个项目在队列中剩余的时间长于另一个项目。我建议您根据实际情况进行分析。

答案 2 :(得分:2)

如果你只关心渐近的复杂性,我会选择@jxh的解决方案,它使用O(n)内存和每次操作保证时间O(log(n))(这是最好的)并且需要很少编码。

但是TreeSetTreeMap支持并且使用TreeEntry作为条目,并且一个条目将占用内存的总32B,并且将导致具有2个树{每个元素的内存{1}}!

编辑2:旧答案不正确,如果有人想看到它,我已将其移动here。跟随(希望)正确答案。

这个想法非常简单。让我们有2个普通堆,一个通过第一个比较器排序,另一个通过第二个比较器排序。并且有两个索引数组将链接堆之间的元素。

例如,如果元素X在堆1中的位置2和堆2中的位置8中,则链接数组将如下所示:

64B

因此,如果您在任何一个堆中执行任何元素移动,则必须相应地更新这些数组。

但是现在如果从一个堆中删除最小值,就知道该元素在第二个堆中的位置,所以你也可以从那里删除它。

我已发布完整代码here,此解决方案每个元素只需要linkFrom1To2[2] = 8; linkFrom2To1[8] = 2; ,并且在我已经完成的测试中甚至比@ jxh的解决方案更快,但是我使用了具有最大容量的简单数组,而不是像16B那样的自动增长结构。

答案 3 :(得分:1)

此任务的对象有望为目标优先级队列维护相同的amoritized运行时。

这些运行时为O(log n),用于插入和删除。

维护两个优先级队列,每个队列都有自己的比较器。维护添加对象计数的映射。从队列中删除对象时,减少计数。如果您尝试从队列轮询的对象具有计数&lt; = 0,则轮询另一个项目。

以下实现维护运行时。它已被设置O(log n)用于插入和删除。因为每个元素都添加到每个队列并且只从每个队列中删除一次,所以它必须是单个队列的运行时的常量倍数。此外,地图的插入和删除时间为O(1),这对队列的O(log n)运行时没有贡献。

public class DualPriorityQueue<T> {

    private Queue<T> queue1, queue2;
    private Map<T, Integer> counts;

    public DualPriorityQueue(int size, Comparator<T> c1, Comparator<T> c2) {
        queue1 = new PriorityQueue(size, c1);
        queue2 = new PriorityQueue(size, c2);
        counts = new HashMap<T, Integer>(size);
    }

    public T poll1() {
        return poll(queue1);
    }

    public T poll2() {
        return poll(queue2);
    }

    private T poll(Queue<T> queue) {
        T t = null;
        while (!queue.isEmpty() && !removeCount(t = queue.poll())) {
            t = null;
        }
        return t;
    }

    public boolean offer(T t) {
        queue1.offer(t);
        queue2.offer(t);
        addCount(t);
        return true;
    }

    private boolean removeCount(T t) {
        final Integer value = counts.get(t);
        if (value != null && value > 0) {
            final int newValue = value - 1;
            if (newValue == 0) {
                counts.remove(t);
            } else {
                counts.put(t, newValue);
            }
            return true;
        }
        return false;
    }

    private void addCount(T t) {
        final Integer prev = counts.get(t);
        if (prev == null || prev <= 0) {
            counts.put(t, 1);
        } else {
            counts.put(t, prev + 1);
        }
    }
}

答案 4 :(得分:0)

你可以做类似的事情。

public final class MyPriorityQueue < T >
{
  private static final int INITIAL_CAPACITY = 10;

  private final Queue < T > q1;
  private final Queue < T > q2;
  private final List < Queue < T > > queues;

  public MyPriorityQueue ( Comparator < T > comparator1,
      Comparator < T > comparator2 )
  {
    q1 = new PriorityQueue < T > ( INITIAL_CAPACITY, comparator1 );
    q2 = new PriorityQueue < T > ( INITIAL_CAPACITY, comparator2 );
    queues = Arrays.asList ( q1, q2 );
  }

  public void enqueue ( T t )
  {
    for ( Queue < T > queue : queues )
      queue.add ( t );
  }

  public T poll1 ()
  {
    T returnValue = q1.poll ();
    q2.remove ( returnValue );
    return returnValue;
  }

  public T poll2 ()
  {
    T returnValue = q2.poll ();
    q1.remove ( returnValue );
    return returnValue;
  }
}

另一种修改可能就是这个。

public final class MyPriorityQueue < T >
{
  private static final int INITIAL_CAPACITY = 10;

  private final Map < Comparator < T >, Queue < T > > queues;

  public MyPriorityQueue ( List < Comparator < T > > comparators )
  {
    queues = new HashMap < Comparator < T >, Queue < T > > ();
    for ( Comparator < T > comparator : comparators )
    {
      Queue < T > q = new PriorityQueue < T > ( INITIAL_CAPACITY, comparator );
      queues.put ( comparator, q );
    }
  }

  public void enqueue ( T t )
  {
    for ( Queue < T > queue : queues.values () )
      queue.add ( t );
  }

  public T poll ( Comparator < T > comparator )
  {
    Queue < T > q = queues.get ( comparator );
    if ( q == null )
      return null;
    T returnValue = q.poll ();
    for ( Queue < T > queue : queues.values () )
      if ( queue != q )
        queue.remove ( returnValue );
    return returnValue;
  }
}