使用数组的O(1)插入时间的优先级队列?

时间:2010-11-01 16:56:31

标签: java data-structures queue priority-queue

我的代码现在有O(N)插入时间和O(1)删除时间。我需要改变这个。 我正在尝试实现O(1)插入时间和O(N)删除时间。

图例:

nItems =项目/对象的数量。最初设置为0。

queArray 是我的长整数数组。

这是我的两种方法。插入方法完成所有排序工作。删除方法只需一行 - 由于我们的Insert方法,删除数组中第一个恰好是最小数字的元素。

如果我要将插入时间更改为O(1),我是否需要提供“排序任务”来删除方法?毕竟它是一个优先级队列,我们​​必须对它进行排序,否则它只是一个随机顺序排列的常规队列。

请,任何帮助都会很好!!!

public void insert(long item) {
    int j;
    if(nItems==0) // if no items,
        queArray[nItems++] = item; // insert at 0
    else {
        for(j=nItems-1; j>=0; j--) { // start at the end
            if( item > queArray[j] ) // if new item larger,
                queArray[j+1] = queArray[j]; // shift upward
            else // if smaller,
                break; // done shifting
        } // end for

        queArray[j+1] = item; // insert it
        nItems++;
    } // end else (nItems > 0)
} 

public long remove() // remove minimum item
{ return queArray[--nItems]; }

5 个答案:

答案 0 :(得分:5)

如果您想要O(1)插入时间和O(N)移除时间,只需将未分类的新元素添加到内部数组的末尾,然后通过列表进行O(N)线性搜索以进行移除,移动阵列的其余部分下来一个。

或者为了更好的实施,您可能需要考虑Fibonacci heap

答案 1 :(得分:3)

我不确定您是否可以为基于阵列的优先级队列实现O(1)插入时间。您可以使用最小/最大堆结构获得O(log n)

这是在内部使用List<>的实现(但可以很容易地交换到数组实现。

using System;
using System.Collections;
using System.Collections.Generic;

namespace HeapADT
{
    public class Heap<T> : ICollection, IEnumerable<T>
        where T : IComparable<T>
    {
        #region Private Members
        private readonly List<T> m_Items;
        private readonly IComparer<T> m_Comparer;
        #endregion

        #region Constructors
        public Heap()
            : this(0)
        {}

        public Heap( int capacity )
            : this( capacity, null )
        {}

        public Heap( IEnumerable<T> items )
            : this( items, null )
        {}

        public Heap( int capacity, IComparer<T> comparer )
        {
            m_Items = new List<T>(capacity);
            m_Comparer = comparer ?? Comparer<T>.Default;
        }

        public Heap( IEnumerable<T> items, IComparer<T> comparer )
        {
            m_Items = new List<T>(items);
            m_Comparer = comparer ?? Comparer<T>.Default;
            BuildHeap();
        }
        #endregion

        #region Operations
        public void Add( T item )
        {
            m_Items.Add( item );

            var itemIndex = Count - 1;

            while( itemIndex > 0 )
            {
                var parentIndex = ParentIndex(itemIndex);
                // are we a heap? If yes, then we're done...
                if( m_Comparer.Compare( this[parentIndex], this[itemIndex] ) < 0 )
                    return;
                // otherwise, sift the item up the heap by swapping with parent
                Swap( itemIndex, parentIndex );
                itemIndex = parentIndex;
            }
        }

        public T RemoveRoot()
        {
            if( Count == 0 )
                throw new InvalidOperationException("Cannot remove the root of an empty heap.");

            var rootItem = this[0];
            ReplaceRoot(RemoveLast());
            return rootItem;
        }

        public T RemoveLast()
        {
            if( Count == 0 )
                throw new InvalidOperationException("Cannot remove the tail from an empty heap.");

            var leafItem = this[Count - 1];
            m_Items.RemoveAt( Count-1 );
            return leafItem;
        }

        public void ReplaceRoot( T newRoot )
        {
            if (Count == 0)
                return; // cannot replace a nonexistent root

            m_Items[0] = newRoot;
            Heapify(0);
        }

        public T this[int index]
        {
            get { return m_Items[index]; }
            private set { m_Items[index] = value; }
        }
        #endregion

        #region Private Members
        private void Heapify( int parentIndex )
        {
            var leastIndex = parentIndex;
            var leftIndex  = LeftIndex(parentIndex);
            var rightIndex = RightIndex(parentIndex);

            // do we have a right child?
            if (rightIndex < Count) 
                leastIndex = m_Comparer.Compare(this[rightIndex], this[leastIndex]) < 0 ? rightIndex : leastIndex;
            // do we have a left child?
            if (leftIndex < Count) 
                leastIndex = m_Comparer.Compare(this[leftIndex], this[leastIndex]) < 0 ? leftIndex : leastIndex;

            if (leastIndex != parentIndex)
            {
                Swap(leastIndex, parentIndex);
                Heapify(leastIndex);
            }
        }

        private void Swap( int firstIndex, int secondIndex )
        {
            T tempItem = this[secondIndex];
            this[secondIndex] = this[firstIndex];
            this[firstIndex] = tempItem;
        }

        private void BuildHeap()
        {
            for( var index = Count/2; index >= 0; index-- )
                Heapify( index );
        }

        private static int ParentIndex( int childIndex )
        {
            return (childIndex - 1)/2;
        }

        private static int LeftIndex( int parentIndex )
        {
            return parentIndex*2 + 1;
        }

        private static int RightIndex(int parentIndex)
        {
            return parentIndex*2 + 2;
        }
        #endregion
        #region ICollection Members
        public void CopyTo(Array array, int index)
        {
            m_Items.CopyTo( (T[])array, index );
        }

        public int Count
        {
            get { return m_Items.Count; }
        }

        public bool IsSynchronized
        {
            get { return false; }
        }

        public object SyncRoot
        {
            get { return null; }
        }
        #endregion

        #region IEnumerable Members
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        public IEnumerator<T> GetEnumerator()
        {
            return m_Items.GetEnumerator();
        }
        #endregion
    }
}

答案 2 :(得分:2)

未排序的链表听起来符合规定的要求(尽管对于大多数实际应用来说它们看起来有些愚蠢)。你有不断的插入时间(坚持到最后或开始)和线性删除时间(扫描最小元素的列表)。

答案 3 :(得分:2)

为了将插入时间更改为O(1),您可以将元素插入到未排序的数组中。然后,您可以创建一个minPeek()方法,使用线性搜索搜索最小的键,然后在删除/删除方法中调用它并删除最小的键。

以下是如何实现这一目标。

public void insert(int item) {
    queArray[nItems++] = item;
}
public int remove() {
    int removeIndex = minPeek();

    if (nItems - 1 != removeIndex) {

        for (int i = removeIndex; i < nItems - 1; i++) {
            queArray[i] = queArray[i + 1];
        }
    }

    return queArray[--nItems];
}

public int minPeek() {
    int min = 0;

    for (int i = 0; i < maxSize; i++) {
        if (queArray[i] < queArray[min]) {
            min = i;
        }
    }

    return min;
}

通过这样做,您的优先级队列具有O(1)插入时间删除方法具有O(N)时间

答案 4 :(得分:1)

无法实现O(1)插入方法并保持数组排序。如果您将排序传递给delete方法,那么您可以快速执行O(N log(n))快速排序或其他操作。 或者您可以在插入方法中执行O(log n)算法,如LBushkin建议。