有效地删除SortedSet中的第一个元素<t>

时间:2018-04-05 07:03:47

标签: c# sortedset

我有一个SortedSet,我将其添加到(以不受控制的顺序显然使用它的排序功能)。

集合中的项目始终按顺序使用和删除,一次一个。

set.Min.Process();
set.Remove(set.Min);

然而,我面临的问题是由于Remove方法的O(log n)方面以及SortedSet的二进制搜索性质,这导致每个方面的最大可能比较次数。删除(~log n)。

对我来说,基于访问最小和最大项目的集合不会有一种有效的方法来删除它们,这似乎很奇怪。

有效地我之后是一个set.RemoveMin()方法,利用更优化的方法(免费比较)来获得第一个元素。

有没有办法做到这一点? 我可以使用现有的替代SortedSet实现吗?

1 个答案:

答案 0 :(得分:1)

[编辑]我对此进行了检测,它看起来并不比使用SortedSet更快,所以这可能不是一个好的答案!

@ randomman159 - 如果您在尝试后没有帮助,请对此答案发表评论,我会将其删除。

您所描述的是Priority Queue

这是使用堆的基本实现。

Enqueue()Dequeue()的复杂度为O(LogN):

/// <summary>Priority Queue data structure.</summary>
/// <remarks>
/// Implemented in traditional fashion, using a heap.
/// Based on code from http://www.vcskicks.com/priority-queue.php
/// 
/// Also see http://en.wikipedia.org/wiki/Heap_(data_structure)
/// and http://en.wikipedia.org/wiki/Priority_queue
/// </remarks>

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]

public sealed class PriorityQueue<T>
{
    /// <summary>Constructor.</summary>
    /// <param name="comparer">A comparison function for items of type T>.</param>

    public PriorityQueue(Comparison<T> comparer)
    {
        _comparer = comparer;
        _heap = new List<T> {default(T)};
    }

    /// <summary>The number of items in the queue.</summary>

    public int Count => _heap.Count - 1;

    /// <summary>
    /// Returns the value at the head of the Priority Queue without removing it.
    /// Throws an exception if the queue is empty.
    /// </summary>

    public T Peek()
    {
        if (this.Count > 0)
        {
            return _heap[1]; // Head of the queue is at [1], not [0].
        }
        else
        {
            throw new InvalidOperationException("Attempt to Peek() into an empty PriorityQueue<T>");
        }
    }

    /// <summary>Adds a value to the Priority Queue</summary>

    public void Enqueue(T value)
    {
        _heap.Add(value);
        this.bubbleUp(_heap.Count - 1); // Bubble up to preserve the heap property, starting at the inserted value.
    }

    /// <summary>Returns the front of the Priority Queue.</summary>

    public T Dequeue()
    {
        if (this.Count > 0)
        {
            T minValue = this._heap[1]; // The smallest value in the Priority Queue is the first item in the array

            if (this._heap.Count > 2) // If there's more than one item, replace the first item in the array with the last one.
            {
                T lastValue = this._heap[_heap.Count - 1];

                this._heap.RemoveAt(_heap.Count - 1);       // Move last node to the head
                this._heap[1] = lastValue;
                this.bubbleDown(1);
            }
            else  // Only one item in the queue.
            {
                _heap.RemoveAt(1);  // Remove the only value stored in the queue.
            }

            return minValue;
        }
        else
        {
            throw new InvalidOperationException("Attempt to Dequeue() from an empty PriorityQueue<T>");
        }
    }

    /// <summary>Restores the heap-order property between child and parent values going up towards the head.</summary>

    private void bubbleUp(int startCell)
    {
        // Requires(startCell >= 0);
        // Requires(startCell < _heap.Count);

        int cell = startCell;

        while (this.isParentBigger(cell))   // Bubble up as long as the parent is greater.
        {
            // Get values of parent and child.

            T parentValue = this._heap[cell/2];
            T childValue  = this._heap[cell];

            // Swap the values.

            this._heap[cell/2] = childValue;
            this._heap[cell]   = parentValue;

            cell /= 2; // Go up parents.
        }
    }

    /// <summary>Restores the heap-order property between child and parent values going down towards the bottom.</summary>

    private void bubbleDown(int startCell)
    {
        // Requires(startCell > 0);
        // Requires(startCell < _heap.Count);

        int cell = startCell;

        // Bubble down as long as either child is smaller.

        while (this.isLeftChildSmaller(cell) || this.isRightChildSmaller(cell))
        {
            int child = this.compareChild(cell);

            if (child == -1) // Left Child.
            {
                // Swap values.

                T parentValue    = _heap[cell];
                T leftChildValue = _heap[2*cell];

                _heap[cell]   = leftChildValue;
                _heap[2*cell] = parentValue;

                cell = 2*cell; // Move down to left child.
            }
            else if (child == 1) // Right Child.
            {
                // Swap values.

                T parentValue     = _heap[cell];
                T rightChildValue = _heap[2*cell+1];

                _heap[cell]     = rightChildValue;
                _heap[2*cell+1] = parentValue;

                cell = 2*cell+1; // Move down to right child.
            }
        }
    }

    /// <summary>Is the value of a parent greater than its child?</summary>

    private bool isParentBigger(int childCell)
    {
        // Requires(childCell >= 0);
        // Requires(childCell < _heap.Count);

        if (childCell == 1)
        {
            return false;  // Top of heap, no parent.
        }
        else
        {
            return _comparer(_heap[childCell/2], _heap[childCell]) > 0;
        }
    }

    /// <summary>
    /// Returns whether the left child cell is smaller than the parent cell.
    /// Returns false if a left child does not exist.
    /// </summary>

    private bool isLeftChildSmaller(int parentCell)
    {
        // Requires(parentCell >= 0);
        // Requires(parentCell < _heap.Count);

        if (2*parentCell >= _heap.Count)
        {
            return false; // Out of bounds.
        }
        else
        {
            return _comparer(_heap[2*parentCell], _heap[parentCell]) < 0;
        }
    }

    /// <summary>
    /// Returns whether the right child cell is smaller than the parent cell.
    /// Returns false if a right child does not exist.
    /// </summary>

    private bool isRightChildSmaller(int parentCell)
    {
        // Requires(parentCell >= 0);
        // Requires(parentCell < _heap.Count);

        if (2 * parentCell + 1 >= _heap.Count)
        {
            return false; // Out of bounds.
        }
        else
        {
            return _comparer(_heap[2*parentCell+1], _heap[parentCell]) < 0;
        }
    }

    /// <summary>
    /// Compares the children cells of a parent cell. -1 indicates the left child is the smaller of the two,
    /// 1 indicates the right child is the smaller of the two, 0 inidicates that neither child is smaller than the parent.
    /// </summary>

    private int compareChild(int parentCell)
    {
        // Requires(parentCell >= 0);
        // Requires(parentCell < _heap.Count);

        bool leftChildSmaller  = this.isLeftChildSmaller(parentCell);
        bool rightChildSmaller = this.isRightChildSmaller(parentCell);

        if (leftChildSmaller || rightChildSmaller)
        {
            if (leftChildSmaller && rightChildSmaller)
            {
                // Figure out which of the two is smaller.

                int leftChild  = 2 * parentCell;
                int rightChild = 2 * parentCell + 1;

                T leftValue  = this._heap[leftChild];
                T rightValue = this._heap[rightChild];

                // Compare the values of the children.

                if (_comparer(leftValue, rightValue) <= 0)
                {
                    return -1; // Left child is smaller.
                }
                else
                {
                    return 1; // Right child is smaller.
                }
            }
            else if (leftChildSmaller)
            {
                return -1; // Left child is smaller.
            }
            else
            {
                return 1; // Right child smaller.
            }
        }
        else
        {
            return 0; // Both children are bigger or don't exist.
        }
    }

    private readonly List<T>       _heap;
    private readonly Comparison<T> _comparer;
}

使用Enqueue()添加元素,然后使用Dequeue()删除前面的元素。

另见这里的另一个实现:https://visualstudiomagazine.com/Articles/2012/11/01/Priority-Queues-with-C.aspx?Page=1