【问题标题】:Get Min/Max in O(1) time from a Queue? [closed]从队列中获取 O(1) 时间内的最小值/最大值? [关闭]
【发布时间】:2012-08-16 18:32:04
【问题描述】:

如何以 0(1) 的时间复杂度随时从队列中检索最大和最小元素? 早些时候我使用 Collections.max 和 min 来查找元素,但这将是 0(n)。

【问题讨论】:

  • 除非队列已排序,否则不能...
  • 使用 Treeset 而不是队列。
  • 你可以创建一个特殊的字段来存储你更新队列的最大/最小值,并在需要时读取它。
  • @Pshemo 是的,但更新需要非常时间。
  • 搜索最小堆栈 O(1)。然后使用 2 个堆栈搜索实施队列。将它们结合起来,你将有最小队列 O(1),弹出时平均 O(1)。

标签: java data-structures


【解决方案1】:

存在这样一种结构,它的作用类似于队列,但可以让您在恒定时间内检索最小/最大值,实际上不是严格恒定的,它是摊销的恒定时间(您可以猜到命名为最小/最大队列)。有两种实现方式 - 使用两个堆栈或使用队列和双端队列。

Deque 实现看起来不像这样(语言无关):

所以我们有一个最大元素的双端队列,前面的那个是所需的最大值,还有一个标准队列。

推送操作

  1. 如果队列为空,只需将元素推送到队列和双端队列。
  2. 如果队列不为空,则将元素推入队列,从双端队列的后面删除所有严格小于我们现在推入的元素(它们肯定不是最大值,因为推元素更大并且将在队列中持续更长时间)并将当前元素推到双端队列的后面

删除操作

  1. 如果双端队列的前部等于队列的前部,则将两者都弹出(从前部的双端队列)
  2. 如果双端队列的前端不等于队列的前端,则仅弹出队列,弹出的元素肯定不是最大的。

获取最大值

  1. 它只是双端队列的第一个元素。

(应添加大量参数以明确其工作原理,但下面介绍的第二个版本可能是这种必要性的答案)

Stack 的实现非常相似,我认为实现的时间可能会更长一些,但可能更容易掌握。首先要注意的是,将最大元素存储在堆栈中很容易 - 容易练习(对于懒惰的人 - Stack with find-min/find-max more efficient than O(n)?)。第二部分,如果第一次看到可能有点棘手,那就是使用两个堆栈实现队列非常容易,可以在这里找到 - How to implement a queue using two stacks?。基本上就是这样 - 如果我们可以获得两个堆栈的最大元素,我们可以获得整个队列的最大元素(如果你想要一个更正式的参数,取最大值是关联的或类似的东西,但我打赌你不't,这真的很明显)。

最小版本是类推的。

一切也可以在 O(nlogn) 时间内使用一个集合(或类似的东西)来完成,但这毫无意义,因为 O(n) 中的常数非常小,它应该更快,但易于实现.

第一个版本中不感兴趣的部分:

希望我能帮上一点忙。并希望这没有说错什么。如果需要,可以在 C++/C 中给出一个简单的实现。非常感谢您对表格的任何反馈,因为这是我在任何地方的第一篇此类帖子:)(而且英语不是我的母语)。还有一些关于正确性的确认会很棒。

编辑:因为这个答案让我得到了一些分数,所以我觉得有必要对其进行一些清理,并对其进行一些扩展。

【讨论】:

    【解决方案2】:

    我在这里发布完整的代码,以便在恒定时间内找到队列中的 MIN 和 MAX。 如果您有任何疑问,请随时与我联系。

    队列

    // Queue Interface
    package com.java.util.collection.advance.datastructure.queue;
    public interface Queue<E>{
        boolean addR(E e);
        E removeL();
        E element();
        E elementR();
        boolean isFull();
        boolean isEmpty();
        void trim();
    }
    

    双端队列

    package com.java.util.collection.advance.datastructure.queue;
    /**
    * A deque is a double-ended queue. You can insert items at either end and delete them
    * from either end. The methods might be called insertLeft() and insertRight(), and 
    * removeLeft() and removeRight().
    * @author vsinha
    *
    * @param <E>
    */
    public interface DeQueue<E> extends Queue<E>{
    
        boolean addL(E element);
    
        E removeR();
    
    }
    

    查找最小最大队列

    package com.java.util.collection.advance.datastructure.queue;
    
    
    @SuppressWarnings("hiding")
    public interface FindMinMaxQueue<Integer> extends Queue<Integer>{
    
        public Integer min();
    
        public Integer max();
    }
    

    我的队列

    package com.java.util.collection.advance.datastructure.queue;
    
    import java.util.Arrays;
    
    public class MyQueue<E> implements Queue<E>,DeQueue<E>{
    
        protected int front = 0;
        protected int rear =-1;
        protected E[] elements =null;
        private static final int DEFAULT_INTIAL_CAPACITY =100; 
        private int size =0;
    
        public MyQueue(){
            this(DEFAULT_INTIAL_CAPACITY);
        }
        @SuppressWarnings("unchecked")
        public MyQueue(int intialCapacity){
            if(intialCapacity < 0){
                throw new IllegalArgumentException("intial capacity can't be null");
            }
            elements =(E[]) new Object[intialCapacity];
        }
        @Override
        public boolean addR(E e) {
            if(! isFull()) {
                elements[++rear] = e;
                size++;
                return true;
            }
            return false;
        }
    
        @Override
        public E removeL() {
            E element =null;
            if(!isEmpty()){
                element=elements[front];
                // Nullify the reference
                elements[front] =null;
                ++front;
                --size;
            }
            return element;
        }
    
        @Override
        public E element() {
            E element =null;
            if(!isEmpty()){
                element=elements[front];
            }
            return element;
        }
    
        @Override
        public E elementR() {
            E element =null;
            if(!isEmpty()){
                element=elements[rear];
            }
            return element;
        }
    
        public boolean isFull() {
            return rear == elements.length;
        }
    
    
        public boolean isEmpty() {
            return size == 0;
        }
        Override
        public String toString() {
            return "MyQueue [front=" + front + ", rear=" + rear + ", elements="
                    + Arrays.toString(elements) + ", size=" + size + "]";
        }
        @Override
        public void trim() {
            @SuppressWarnings("unchecked")
            E[] dest =(E[]) new Object[size];
            System.arraycopy(elements, front, dest, 0, size);
            elements = dest;
            front =0;
            rear=size-1;
        }
        @Override
        public boolean addL(E element) {
            if(front != 0) {
                elements[--front] = element;
                size++;
                return true;
            }
            return false;
        }
    
        @Override
        public E removeR() {
            E element =null;
            if(size > 0) {
                element=elements[rear];
                // Nullify the reference
                elements[rear] =null;
                --rear;
                --size;
            }
            return element;
        }
    
    }
    

    MinAndMaxFinderQueue

    package com.java.util.collection.advance.datastructure.queue;
    
    public class MinAndMaxFinderQueue extends MyQueue<Integer> implements FindMinMaxQueue<Integer> {
    
        private Queue<Integer> maxValuesQueue =null;
    
        private Queue<Integer> minValuesQueue =null;
    
    
        public MinAndMaxFinderQueue (int intialCapacity){
            super(intialCapacity);
            maxValuesQueue =new MyQueue<Integer>(intialCapacity);
            minValuesQueue =new MyQueue<Integer>(intialCapacity);
    
        }
        @Override
        public boolean addR(Integer e) {
            if(super.addR(e)){
                if(max() == null || max() <= e){
                    maxValuesQueue.addR(e);
                }
    
                if(min() == null || min() >= e){
                    minValuesQueue.addR(e);
                }
                return true;
            }
            return false;
        }
    
        @Override
        public Integer removeL() {
            Integer element =super.removeL();
            if(element !=null){
                if(maxValuesQueue.element() == element){
                    maxValuesQueue.removeL();
                }
    
                if(minValuesQueue.element() == element){
                    minValuesQueue.removeL();
                }
            }
            //Need to re-generate MIN and MAX queue when the main queue is not empty and min/max queue is empty
            regenerateMin();
            regenerateMax();
    
            return element;
        }
    
        private void regenerateMin(){
            Integer current =null;
            if(!super.isEmpty() && min() ==null){
                for(int front = super.front; front<= super.rear;front++){
                    current = (Integer)elements[front];
                    if(min() == null || min() >= current){
                        minValuesQueue.addR(current);
                    }
    
                }
            }
        }
    
        private void regenerateMax(){
            Integer current =null;
            if(!super.isEmpty() && max() ==null){
                for(int front = super.front; front<= super.rear;front++){
                    current = (Integer)elements[front];
                    if(max() == null || max() <= current){
                        maxValuesQueue.addR(current);
                    }
                }
            }
        }
        public Integer min() {
            return minValuesQueue.elementR();
        }
    
        public Integer max() {
            return maxValuesQueue.elementR();
        }
        @Override
        public String toString() {
            return super.toString()+"\nMinAndMaxFinderQueue [maxValuesQueue=" + maxValuesQueue
                    + ", minValuesQueue=" + minValuesQueue + "]";
        }
    
    
    
    }
    

    测试

    //Test class 
    package com.java.util.collection.advance.datastructure.queue;
    
    import java.util.Random;
    
    
    public class MinMaxQueueFinderApp {
    
        public static void main(String[] args) {
            FindMinMaxQueue<Integer> queue =new MinAndMaxFinderQueue(10);
            Random random =new Random();
            for(int i =0; i< 10; i++){
                queue.addR(random.nextInt(100));
                System.out.println(queue);
                System.out.println("MAX :"+queue.max());
                System.out.println("MIN :"+queue.min());
            }
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
    
            queue.removeL();
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
            queue.removeL();
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
            queue.removeL();
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
            queue.removeL();
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
            queue.removeL();
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
    
    
            System.out.println(queue);
            System.out.println("MAX :"+queue.max());
            System.out.println("MIN :"+queue.min());
        }
    }
    

    【讨论】:

      【解决方案3】:
      【解决方案4】:

      这并不是真正的队列,但您可以实现 Min-Max Heap。

      http://en.wikipedia.org/wiki/Min-max_heap

      基本上,它是一个堆,它在偶数级别具有最大堆属性,在奇数级别具有最小堆属性。

      它同时具有 O(1) MIN() 和 O(1) MAX() 操作。然而,迭代起来相当棘手,但它可以工作并满足您的要求。

      【讨论】:

        【解决方案5】:

        对于最小/最大操作,您只有 2 种方法来获得 O(1):

        • 如果结构已排序并且您知道最大值/最小值的位置
        • 如果结构未排序且仅允许插入:您可以在每次插入项目时重新计算最小值/最大值并单独存储该值
        • 如果结构没有排序并允许插入和删除:我认为你不能比 O(n) 做得更好,unless you use more than one collection(但该解决方案不支持删除任何元素,仅支持头/尾元素,这应该是队列的情况)。

        【讨论】:

        • 我删除了我的答案,因为我很确定它只适用于堆栈,而不适用于队列。
        • 好的,我的答案中的链接对于
        • 搜索最小堆栈 O(1)。然后使用 2 个堆栈搜索实施队列。将它们结合起来,你将有最小队列 O(1),在弹出时平均 O(1)。
        【解决方案6】:

        我将存储两个字段 minIndexmaxIndex,它们将分别在您的数据结构中存储索引位置的最小值和最大值。

        当新元素添加到队列中时,检查两件事:

        1. 元素小于minIndex位置的当前最小元素;如果是,则在插入后更新 minIndex 的值。
        2. 元素大于 maxIndex 位置的当前最大元素,并相应地更新引用。

        这将为您提供当前最小值和最大值的 O(1) 渐近线。

        【讨论】:

        • ...当一个元素被移除?
        • 啊,是的;所以最好另外创建两个堆栈,一个用于最小值,另一个用于最大值。
        • 实际上,这也无济于事。当您在一端添加并在另一端移除时,队列作为一个整体在不等于任何先前状态的不同状态之间转换。因此历史方法是没有用的。
        • 用两个队列来做最小值和最大值怎么样?
        【解决方案7】:

        我怀疑您正在尝试实现 PriorityQueue 的功能。这是一个排序队列,O(log N) 得到最低值。我不知道为什么你会想要最大值,因为队列只有一端。

        【讨论】:

        • 有点,但我没有使用堆。
        • 有趣,所以你有使用堆外内存吗? (数组和对象在堆上)
        • 我的意思是要实现 PQ,我需要使用 min-heap / max-heap ,heapify 操作!也就是堆(作为数据结构)。
        • 内置的 PriorityQueue 没有这些操作docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
        猜你喜欢
        • 2020-05-19
        • 2019-04-28
        • 2016-10-04
        • 2012-09-09
        • 2020-06-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-02
        相关资源
        最近更新 更多