【问题标题】:Java Priority Queue ComparatorJava 优先队列比较器
【发布时间】:2013-02-06 20:41:12
【问题描述】:

我已经为优先级队列定义了自己的比较函数,但是比较函数需要数组的信息。问题是当数组的值发生变化时,并没有影响比较功能。我该如何处理? 代码示例:

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

public class Main {
    public static final int INF = 100;
    public static int[] F = new int[201];

    public static void main(String[] args){

        PriorityQueue<Integer> Q = new PriorityQueue<Integer>(201, 
            new Comparator<Integer>(){
                public int compare(Integer a, Integer b){
                    if (F[a] > F[b]) return 1;
                    if (F[a] == F[b]) return 0;
                    return -1;
                }
            });

            Arrays.fill(F, INF);
            F[0] = 0; F[1] = 1; F[2] = 2;
            for (int i = 0; i < 201; i ++) Q.add(i);
            System.out.println(Q.peek()); // Prints 0, because F[0] is the smallest
            F[0] = 10;
            System.out.println(Q.peek()); // Still prints 0 ... OMG
        }
   }

【问题讨论】:

  • 您需要自己的 PriorityQueue 实现,它在 peek 上使用比较器,而不是在 add 上。
  • 你的意思是 Q.add(F[i]) 吗?

标签: java priority-queue


【解决方案1】:

您的比较器仅在您修改队列时(即添加项目时)才会被调用。在那之后,队列不知道是什么导致了顺序改变,这就是为什么它保持不变。

有这样一个比较器是相当令人困惑的。如果你有两个值,A 和 B,并且在某个时候 A>B,每个人都会期望 A 保持大于 B。我认为你对这个问题使用优先级队列是错误的。

【讨论】:

【解决方案2】:

因此,本质上,您是在动态更改比较标准,而这并不是优先队列合同提供的功能。请注意,这似乎适用于某些情况(例如,在删除或插入另一个项目时,堆可能会对 一些 项目进行排序),但由于您无法保证,这不是一种有效的方法。

您可以做的是,每次更改数组时,都会取出所有元素,然后将它们放回原处。这当然 非常 昂贵(O(n*log(n ))) 所以你可能应该尝试解决你的设计以避免改变数组值。

【讨论】:

    【解决方案3】:

    使用在 peek 上使用比较器的 PriorityQueue 的自定义实现,而不是在 add 上:

    public class VolatilePriorityQueue <T> extends AbstractQueue <T>
    {
        private final Comparator <? super T> comparator;
    
        private final List <T> elements = new ArrayList <T> ();
    
        public VolatilePriorityQueue (Comparator <? super T> comparator)
        {
            this.comparator = comparator;
        }
    
        @Override
        public boolean offer (T e)
        {
            return elements.add (e);
        }
    
        @Override
        public T poll ()
        {
            if (elements.isEmpty ()) return null;
            else return elements.remove (getMinimumIndex ());
        }
    
        @Override
        public T peek ()
        {
            if (elements.isEmpty ()) return null;
            else return elements.get (getMinimumIndex ());
        }
    
        @Override
        public Iterator <T> iterator ()
        {
            return elements.iterator ();
        }
    
        @Override
        public int size ()
        {
            return elements.size ();
        }
    
        private int getMinimumIndex ()
        {
            T e = elements.get (0);
            int index = 0;
    
            for (int count = elements.size (), i = 1; i < count; i++)
            {
                T ee = elements.get (i);
                if (comparator.compare (e, ee) > 0)
                {
                    e = ee;
                    index = i;
                }
            }
    
            return index;
        }
    }
    

    【讨论】:

    • 值得一提的是,这会比内置的PriorityQueue效率要低很多。
    • 一旦比较标准可能随着时间而改变,我看不出如何避免在 peek 中遍历所有元素。如果每次更改比较标准时都可以通知队列,则可能会实施更有效的解决方案。
    • 我并不是建议在Queue API 中实现更好的实现;我只是说 OP 需要意识到这将产生线性时间开销。
    • Mikhail:是的,您的解决方案有效,但正如@LouisWasserman 所说,它基本上是封装(隐藏)线性方法。甚至可能更糟糕的是,您在 peek 即使 数组没有更改时迭代元素。我认为 OP 最好重新考虑他的方法,并尽可能避免更改这些数组
    猜你喜欢
    • 2020-09-07
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    相关资源
    最近更新 更多