Приоритетная очередь в .Net

Я ищу .NET-реализацию очереди приоритетов или структуры данных кучи.

Приоритетные очереди - это структуры данных, которые обеспечивают большую гибкость, чем простая сортировка, поскольку они позволяют новым элементам входить в систему с произвольными интервалами. Гораздо выгоднее вставлять новое задание в приоритетную очередь, чем заново сортировать все при каждом таком поступлении.

Очередь с основным приоритетом поддерживает три основных операции:

  • Вставьте (Q, x). Учитывая элемент x с ключом k, вставьте его в очередь приоритетов Q.
  • Найти минимум (Q). Вернуть указатель на элемент, значение ключа которого меньше любого другого ключа в очереди приоритетов Q.
  • Удалить минимум (Q). Удалите элемент из очереди приоритетов Q, ключ которого минимален

Если я не ищу не в том месте, во фреймворке его нет. Кто-нибудь знает о хорошем, или мне стоит свернуть свой?


person Doug McClean    schedule 19.09.2008    source источник
comment
К вашему сведению, я разработал простую в использовании, высоко оптимизированную приоритетную очередь C #, которую можно найти на здесь. Он был разработан специально для приложений поиска пути (A * и т. Д.), Но должен отлично работать и для любого другого приложения. Я бы отправил это как ответ, но вопрос недавно был закрыт ...   -  person BlueRaja - Danny Pflughoeft    schedule 26.07.2013
comment
ParallelExtensionsExtras имеет ConcurrentPriorityQueue code.msdn.microsoft.com/ParExtSamples   -  person VoteCoffee    schedule 18.09.2014
comment
Бесстыдно вводите PriorityQueue, как часть усилий по переносу параллельного API java в .net для Spring.Net. Это и куча, и очередь с полной общей поддержкой. Двоичный файл можно загрузить здесь.   -  person Kenneth Xu    schedule 14.12.2014
comment
@ BlueRaja-DannyPflughoeft Не могли бы вы добавить ответ?   -  person mafu    schedule 20.11.2016
comment
Просто подведем итоги. В настоящее время нет структуры данных кучи ни в .net, ни в ядре .net. Хотя Array.Sort пользователи его для большого количества. Существуют внутренние реализации.   -  person Artyom    schedule 17.10.2017
comment
Вот еще один код другой реализации .Net (C # и F #). msdn.microsoft.com/windowsdesktop/   -  person u8it    schedule 21.08.2018
comment
@Artyom Было показано, что эта внутренняя реализация содержит ошибку: stackoverflow.com/q/44221454/3546415   -  person u8it    schedule 21.08.2018
comment
MS реализовала PriorityQueue, она просто не является общедоступной .com / # PresentationCore / Shared / MS /   -  person Bogdan    schedule 13.09.2020
comment
Приоритетная очередь с SortedSet, которая позволяет дублировать yogeshdorbala.wordpress. ru / 2021/02/07 /   -  person re3el    schedule 08.02.2021
comment
С января 2021 года в .Net Core добавлена ​​публичная реализация PriorityQueue. Фактическую фиксацию репо и API можно найти здесь: github.com/dotnet/runtime/ совершить /   -  person Daniel    schedule 24.02.2021
comment
Ядро .NET 6.0 имеет встроенный PriorityQueue. Его источник можно найти здесь.   -  person rustyx    schedule 10.05.2021


Ответы (14)


Мне нравится использовать классы OrderedBag и OrderedSet в PowerCollections в качестве приоритетных очередей.

person Ben Hoffstein    schedule 19.09.2008
comment
OrderedBag / OrderedSet выполняют больше работы, чем необходимо, они используют красно-черное дерево вместо кучи. - person Dan Berindei; 20.11.2009
comment
@DanBerindei: не требуется работать, если вам нужно выполнить текущий расчет (удалить старые элементы), куча поддерживает только удаление минимального или максимального значения - person Svisstack; 27.07.2014

Вам может понравиться IntervalHeap из библиотеки общих коллекций C5. Чтобы процитировать руководство пользователя

Класс IntervalHeap<T> реализует интерфейс IPriorityQueue<T>, используя интервальную кучу, хранящуюся в виде массива пар. Операции FindMin и FindMax и get-accessor индексатора занимают время O (1). Операции DeleteMin, DeleteMax, добавления и обновления, а также набор доступа индексатора занимают время O (журнал n). В отличие от обычной очереди с приоритетом, интервальная куча предлагает как минимальные, так и максимальные операции с одинаковой эффективностью.

API достаточно простой

> var heap = new C5.IntervalHeap<int>();
> heap.Add(10);
> heap.Add(5);
> heap.FindMin();
5

Установите с Nuget https://www.nuget.org/packages/C5 или GitHub https://github.com/sestoft/C5/

person functor    schedule 11.07.2009
comment
Это выглядит очень надежной библиотекой и включает 1400 модульных тестов. - person ECC-Dan; 26.03.2013
comment
Пытался использовать, но есть серьезные недоработки. IntervalHeap не имеет встроенной концепции приоритета и заставляет вас реализовывать IComparable или IComparer, делая его отсортированной коллекцией, а не приоритетом. Хуже того, нет прямого способа обновить приоритет какой-то предыдущей записи !!! - person morteza khosravi; 09.03.2018

Вот моя попытка создать кучу .NET

public abstract class Heap<T> : IEnumerable<T>
{
    private const int InitialCapacity = 0;
    private const int GrowFactor = 2;
    private const int MinGrow = 1;

    private int _capacity = InitialCapacity;
    private T[] _heap = new T[InitialCapacity];
    private int _tail = 0;

    public int Count { get { return _tail; } }
    public int Capacity { get { return _capacity; } }

    protected Comparer<T> Comparer { get; private set; }
    protected abstract bool Dominates(T x, T y);

    protected Heap() : this(Comparer<T>.Default)
    {
    }

    protected Heap(Comparer<T> comparer) : this(Enumerable.Empty<T>(), comparer)
    {
    }

    protected Heap(IEnumerable<T> collection)
        : this(collection, Comparer<T>.Default)
    {
    }

    protected Heap(IEnumerable<T> collection, Comparer<T> comparer)
    {
        if (collection == null) throw new ArgumentNullException("collection");
        if (comparer == null) throw new ArgumentNullException("comparer");

        Comparer = comparer;

        foreach (var item in collection)
        {
            if (Count == Capacity)
                Grow();

            _heap[_tail++] = item;
        }

        for (int i = Parent(_tail - 1); i >= 0; i--)
            BubbleDown(i);
    }

    public void Add(T item)
    {
        if (Count == Capacity)
            Grow();

        _heap[_tail++] = item;
        BubbleUp(_tail - 1);
    }

    private void BubbleUp(int i)
    {
        if (i == 0 || Dominates(_heap[Parent(i)], _heap[i])) 
            return; //correct domination (or root)

        Swap(i, Parent(i));
        BubbleUp(Parent(i));
    }

    public T GetMin()
    {
        if (Count == 0) throw new InvalidOperationException("Heap is empty");
        return _heap[0];
    }

    public T ExtractDominating()
    {
        if (Count == 0) throw new InvalidOperationException("Heap is empty");
        T ret = _heap[0];
        _tail--;
        Swap(_tail, 0);
        BubbleDown(0);
        return ret;
    }

    private void BubbleDown(int i)
    {
        int dominatingNode = Dominating(i);
        if (dominatingNode == i) return;
        Swap(i, dominatingNode);
        BubbleDown(dominatingNode);
    }

    private int Dominating(int i)
    {
        int dominatingNode = i;
        dominatingNode = GetDominating(YoungChild(i), dominatingNode);
        dominatingNode = GetDominating(OldChild(i), dominatingNode);

        return dominatingNode;
    }

    private int GetDominating(int newNode, int dominatingNode)
    {
        if (newNode < _tail && !Dominates(_heap[dominatingNode], _heap[newNode]))
            return newNode;
        else
            return dominatingNode;
    }

    private void Swap(int i, int j)
    {
        T tmp = _heap[i];
        _heap[i] = _heap[j];
        _heap[j] = tmp;
    }

    private static int Parent(int i)
    {
        return (i + 1)/2 - 1;
    }

    private static int YoungChild(int i)
    {
        return (i + 1)*2 - 1;
    }

    private static int OldChild(int i)
    {
        return YoungChild(i) + 1;
    }

    private void Grow()
    {
        int newCapacity = _capacity*GrowFactor + MinGrow;
        var newHeap = new T[newCapacity];
        Array.Copy(_heap, newHeap, _capacity);
        _heap = newHeap;
        _capacity = newCapacity;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _heap.Take(Count).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class MaxHeap<T> : Heap<T>
{
    public MaxHeap()
        : this(Comparer<T>.Default)
    {
    }

    public MaxHeap(Comparer<T> comparer)
        : base(comparer)
    {
    }

    public MaxHeap(IEnumerable<T> collection, Comparer<T> comparer)
        : base(collection, comparer)
    {
    }

    public MaxHeap(IEnumerable<T> collection) : base(collection)
    {
    }

    protected override bool Dominates(T x, T y)
    {
        return Comparer.Compare(x, y) >= 0;
    }
}

public class MinHeap<T> : Heap<T>
{
    public MinHeap()
        : this(Comparer<T>.Default)
    {
    }

    public MinHeap(Comparer<T> comparer)
        : base(comparer)
    {
    }

    public MinHeap(IEnumerable<T> collection) : base(collection)
    {
    }

    public MinHeap(IEnumerable<T> collection, Comparer<T> comparer)
        : base(collection, comparer)
    {
    }

    protected override bool Dominates(T x, T y)
    {
        return Comparer.Compare(x, y) <= 0;
    }
}

Некоторые тесты:

[TestClass]
public class HeapTests
{
    [TestMethod]
    public void TestHeapBySorting()
    {
        var minHeap = new MinHeap<int>(new[] {9, 8, 4, 1, 6, 2, 7, 4, 1, 2});
        AssertHeapSort(minHeap, minHeap.OrderBy(i => i).ToArray());

        minHeap = new MinHeap<int> { 7, 5, 1, 6, 3, 2, 4, 1, 2, 1, 3, 4, 7 };
        AssertHeapSort(minHeap, minHeap.OrderBy(i => i).ToArray());

        var maxHeap = new MaxHeap<int>(new[] {1, 5, 3, 2, 7, 56, 3, 1, 23, 5, 2, 1});
        AssertHeapSort(maxHeap, maxHeap.OrderBy(d => -d).ToArray());

        maxHeap = new MaxHeap<int> {2, 6, 1, 3, 56, 1, 4, 7, 8, 23, 4, 5, 7, 34, 1, 4};
        AssertHeapSort(maxHeap, maxHeap.OrderBy(d => -d).ToArray());
    }

    private static void AssertHeapSort(Heap<int> heap, IEnumerable<int> expected)
    {
        var sorted = new List<int>();
        while (heap.Count > 0)
            sorted.Add(heap.ExtractDominating());

        Assert.IsTrue(sorted.SequenceEqual(expected));
    }
}
person Ohad Schneider    schedule 08.12.2012
comment
Я бы рекомендовал очистить значение кучи в ExtractDominating, чтобы оно не удерживало указанный объект дольше, чем необходимо (потенциальная утечка памяти). Очевидно, что для типов значений это не имеет значения. - person Wout; 03.08.2015
comment
Красиво, но из него нельзя убирать предметы? Это важная операция для очередей с приоритетом. - person Tom Larkworthy; 06.08.2016
comment
Похоже, что базовый объект - это массив. Разве это не было бы лучше в виде двоичного дерева? - person Sean Munson; 26.06.2018
comment
@OhadSchneider очень, очень круто, я просто смотрел в минимальную кучу и пытался сделать то, что вы сделали, сделав ее общей и минимальной или максимальной кучей! отличная работа - person Gilad; 25.11.2018
comment
Я не нашел функций GetMax () ExtractTop () GetTop (). - person Jaydeep Shil; 26.07.2019
comment
@JaydeepShil методы называются ExtractDominating и GetDominating - хотя вы можете добавить методы, которые вы назвали в качестве псевдонимов для них в подклассах (на самом деле, неплохая идея). - person Ohad Schneider; 26.07.2019
comment
@OhadSchneider, почему вы не использовали IEqualityComparer, а используете Comparer? - person Gilad; 08.09.2019
comment
@Gilad IEqualityComparer<T> было бы недостаточно, так как это только скажет вам, равны ли два элемента, тогда как вам нужно знать отношение между ними (кто меньше / больше). Это правда, что я мог использовать IComparer<T>, хотя ... - person Ohad Schneider; 09.09.2019
comment
Array.Copy (_heap, newHeap, _capacity); звучит так, как будто Grow () масштабируется как O (n ^ 2) вместо O (n) - person user2286810; 28.06.2020
comment
Кроме того, в куче всегда должен быть элемент max / min вверху, поэтому GetDominating () выполняет больше работы, чем необходимо. - person user2286810; 28.06.2020

вот один, который я только что написал, возможно, он не так оптимизирован (просто использует отсортированный словарь), но прост для понимания. вы можете вставлять объекты разных типов, поэтому нет общих очередей.

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

namespace PrioQueue
{
    public class PrioQueue
    {
        int total_size;
        SortedDictionary<int, Queue> storage;

        public PrioQueue ()
        {
            this.storage = new SortedDictionary<int, Queue> ();
            this.total_size = 0;
        }

        public bool IsEmpty ()
        {
            return (total_size == 0);
        }

        public object Dequeue ()
        {
            if (IsEmpty ()) {
                throw new Exception ("Please check that priorityQueue is not empty before dequeing");
            } else
                foreach (Queue q in storage.Values) {
                    // we use a sorted dictionary
                    if (q.Count > 0) {
                        total_size--;
                        return q.Dequeue ();
                    }
                }

                Debug.Assert(false,"not supposed to reach here. problem with changing total_size");

                return null; // not supposed to reach here.
        }

        // same as above, except for peek.

        public object Peek ()
        {
            if (IsEmpty ())
                throw new Exception ("Please check that priorityQueue is not empty before peeking");
            else
                foreach (Queue q in storage.Values) {
                    if (q.Count > 0)
                        return q.Peek ();
                }

                Debug.Assert(false,"not supposed to reach here. problem with changing total_size");

                return null; // not supposed to reach here.
        }

        public object Dequeue (int prio)
        {
            total_size--;
            return storage[prio].Dequeue ();
        }

        public void Enqueue (object item, int prio)
        {
            if (!storage.ContainsKey (prio)) {
                storage.Add (prio, new Queue ());
              }
            storage[prio].Enqueue (item);
            total_size++;

        }
    }
}
person kobi7    schedule 14.02.2011
comment
это не позволяет использовать несколько записей с одинаковым приоритетом? - person Letseatlunch; 16.04.2011
comment
оно делает. когда вы вызываете метод Enqueue, он добавит элемент в очередь с этим приоритетом. (часть в else в методе постановки в очередь.) - person kobi7; 19.04.2011
comment
Что вы имеете в виду, говоря, что это не совсем очередь приоритетов в смысле информатики? Что в этом заставляет вас поверить, что это не очередь с приоритетом? - person Mark Byers; 23.02.2012
comment
-1 за неиспользование дженериков. - person cdiggins; 02.01.2013
comment
letsealunch, каждый приоритет имеет собственную очередь, поэтому она просто добавляет столько объектов, сколько вы хотите. при взятии объекта мы перебираем список очередей, глядя на счетчик. так что это единственное место, где он o (n), но n - это просто количество очередей, так что это не так много. все остальное - O (1) благодаря: поиску по словарю, удалению из очереди и методу list.count. Поскольку это отсортированный словарь, список, вероятно, представляет собой сбалансированное дерево, но это касается только ключей. На мой взгляд, это простое решение. - person kobi7; 24.01.2014
comment
Одним из самых больших преимуществ Heap / PriorityQueue является сложность O (1) извлечения min / max, то есть операции Peek. И здесь речь идет о настройке перечислителя, цикла for и т. Д. Почему !? Кроме того, операция Enqueue, а не O (logN) - еще одна ключевая функция кучи, имеет одно пролистывание O (longN) из-за ContainsKey, второе (снова O (longN)) для добавления записи очереди (при необходимости) , третий - для фактического получения очереди (строка storage [prio]) и, наконец, линейное добавление к этой очереди. Это действительно безумие в свете реализации основного алгоритма. - person Jonan Georgiev; 23.11.2016
comment
Привет, Иван, это действительно старый, но в любом случае: реализация должна быть довольно быстрой, операции с очередями - O (1) Я думаю, что это двусвязный список внизу, и словарь тоже 0 (1). SortedDictionary, я думаю, у них есть btree внизу, так что o (logn) я думаю, но я не уверен в этом. Единственная итерация - это foreach для перехода к следующей очереди. эта длина довольно мала, в зависимости от того, сколько у вас приоритетов. может быть 3, может быть 10? не так уж и плохо, как вы думаете ... и это довольно легко понять благодаря световой структуре. - person kobi7; 04.12.2016
comment
так что это единственное место, где он o (n), но n - это просто количество очередей, так что это не так много. - Это довольно предположение. Приоритетами могут быть временные метки или другие уникальные или почти уникальные значения. - person Jim Balter; 05.01.2017
comment
Джим Балтер: нет, приоритеты внутри. 0–100 или 0–255 может быть достаточно для многих целей. но я не поставил там ограничения (int32). также можно использовать отрицательные - person kobi7; 07.01.2017
comment
Операция с приоритетом удаления из очереди должна иметь условие проверки размера. - person Sandy; 01.10.2018
comment
@JonanGeorgiev, почему вы думаете, что текущая реализация Peek - это O (logN)? Это O (1). Вы говорите, что операция Enqueue имеет линейную временную сложность при добавлении в очередь. Это не правда. При постановке в очередь это O (1). Однако ContainsKey - это O (logN), как вы упомянули, и, следовательно, в целом это O (logN). - person Sandy; 01.10.2018

Я нашел один Джулиана Бакнелла в его блоге здесь - http://www.boyet.com/Articles/PriorityQueueCSharp3.html

Мы немного изменили его, чтобы элементы с низким приоритетом в очереди со временем «всплывали» вверх, чтобы они не страдали от голода.

person Duncan    schedule 14.10.2008

Как упоминалось в Коллекции Microsoft для .NET, Microsoft написала (и опубликовала в Интернете) 2 внутренних класса PriorityQueue в .NET Framework. Их код доступен для тестирования.

Как прокомментировал @ mathusum-mut, в одном из внутренних классов Microsoft PriorityQueue есть ошибка (конечно, сообщество SO предоставило исправления для нее): Ошибка во внутренней PriorityQueue Microsoft ‹T›?

person weir    schedule 02.03.2017
comment
Ошибка была обнаружена в одной из реализаций здесь: https://stackoverflow.com/questions/44221454/bug-in-microsofts-internal-priorityqueuet - person MathuSum Mut; 28.05.2017
comment
Ох! Я вижу, что все эти классы PriorityQueue<T> в общем источнике Microsoft отмечены internal спецификатором доступа. Таким образом, они используются только внутренними функциями фреймворка. Они недоступны для общего пользования, просто указав windowsbase.dll в проекте C #. Единственный способ - скопировать общий источник в сам проект внутри файла класса. - person RBT; 02.01.2018

Вам может пригодиться такая реализация: http://www.codeproject.com/Articles/126751/Priority-queue-in-Csharp-with-help-of-heap-data-st.aspx

это общий и основан на структуре данных кучи

person Alexey    schedule 11.11.2010

class PriorityQueue<T>
{
    IComparer<T> comparer;
    T[] heap;
    public int Count { get; private set; }
    public PriorityQueue() : this(null) { }
    public PriorityQueue(int capacity) : this(capacity, null) { }
    public PriorityQueue(IComparer<T> comparer) : this(16, comparer) { }
    public PriorityQueue(int capacity, IComparer<T> comparer)
    {
        this.comparer = (comparer == null) ? Comparer<T>.Default : comparer;
        this.heap = new T[capacity];
    }
    public void push(T v)
    {
        if (Count >= heap.Length) Array.Resize(ref heap, Count * 2);
        heap[Count] = v;
        SiftUp(Count++);
    }
    public T pop()
    {
        var v = top();
        heap[0] = heap[--Count];
        if (Count > 0) SiftDown(0);
        return v;
    }
    public T top()
    {
        if (Count > 0) return heap[0];
        throw new InvalidOperationException("优先队列为空");
    }
    void SiftUp(int n)
    {
        var v = heap[n];
        for (var n2 = n / 2; n > 0 && comparer.Compare(v, heap[n2]) > 0; n = n2, n2 /= 2) heap[n] = heap[n2];
        heap[n] = v;
    }
    void SiftDown(int n)
    {
        var v = heap[n];
        for (var n2 = n * 2; n2 < Count; n = n2, n2 *= 2)
        {
            if (n2 + 1 < Count && comparer.Compare(heap[n2 + 1], heap[n2]) > 0) n2++;
            if (comparer.Compare(v, heap[n2]) >= 0) break;
            heap[n] = heap[n2];
        }
        heap[n] = v;
    }
}

легкий.

person Shimou Dong    schedule 24.11.2015
comment
Иногда я вижу такие вещи, как for (var n2 = n / 2; n > 0 && comparer.Compare(v, heap[n2]) > 0; n = n2, n2 /= 2) heap[n] = heap[n2];, и думаю, стоило ли это одной строчки - person ; 15.05.2017
comment
@DustinBreakey в личном стиле :) - person Shimou Dong; 16.05.2017
comment
но определенно не читается для других. Подумайте о написании кода, который не оставляет вопросительного знака над головой разработчика. - person alzaimar; 18.03.2018

АлгоКит

Я написал библиотеку с открытым исходным кодом под названием AlgoKit, доступную через NuGet. Это содержит:

  • Неявные динамические кучи (ArrayHeap),
  • Биномиальные кучи,
  • Сопряжение куч.

Код был тщательно протестирован. Я определенно рекомендую вам попробовать.

Пример

var comparer = Comparer<int>.Default;
var heap = new PairingHeap<int, string>(comparer);

heap.Add(3, "your");
heap.Add(5, "of");
heap.Add(7, "disturbing.");
heap.Add(2, "find");
heap.Add(1, "I");
heap.Add(6, "faith");
heap.Add(4, "lack");

while (!heap.IsEmpty)
    Console.WriteLine(heap.Pop().Value);

Почему эти три кучи?

Оптимальный выбор реализации сильно зависит от ввода - как показывают Ларкин, Сен и Тарджан в Основное эмпирическое исследование приоритетных очередей, arXiv: 1403.0252v1 [cs.DS]. Они тестировали неявные d-арные кучи, парные кучи, кучи Фибоначчи, биномиальные кучи, явные d-арные кучи, кучи парного ранга, кучи землетрясений, кучи нарушений, слабые кучи с ослабленным рангом и строгие кучи Фибоначчи.

В AlgoKit есть три типа куч, которые оказались наиболее эффективными среди протестированных.

Подсказка на выбор

Для относительно небольшого количества элементов вам, вероятно, будет интересно использовать неявные кучи, особенно четвертичные кучи (неявные четырехмерные). В случае работы с кучей большего размера амортизированные структуры, такие как биномиальные кучи и парные кучи, должны работать лучше.

person Patryk Golebiowski    schedule 24.08.2016

Используйте переводчик Java в C # в реализации Java (java.util.PriorityQueue) в структуре Java Collections или более разумно используйте алгоритм и основной код и вставьте его в собственный класс C #, который соответствует структуре C # Collections. API для очередей или хотя бы коллекций.

person JeeBee    schedule 19.09.2008
comment
Это работает, но, к сожалению, IKVM не поддерживает дженерики Java, поэтому вы теряете безопасность типов. - person Mechanical snail; 08.11.2012
comment
Когда вы имеете дело с скомпилированным байт-кодом Java, не существует такой вещи, как дженерики Java. IKVM не может это поддерживать. - person Mark; 08.12.2012

Простая реализация максимальной кучи.

https://github.com/bharathkumarms/AlgorithmsMadeEasy/blob/master/AlgorithmsMadeEasy/MaxHeap.cs

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

namespace AlgorithmsMadeEasy
{
    class MaxHeap
    {
        private static int capacity = 10;
        private int size = 0;
        int[] items = new int[capacity];

        private int getLeftChildIndex(int parentIndex) { return 2 * parentIndex + 1; }
        private int getRightChildIndex(int parentIndex) { return 2 * parentIndex + 2; }
        private int getParentIndex(int childIndex) { return (childIndex - 1) / 2; }

        private int getLeftChild(int parentIndex) { return this.items[getLeftChildIndex(parentIndex)]; }
        private int getRightChild(int parentIndex) { return this.items[getRightChildIndex(parentIndex)]; }
        private int getParent(int childIndex) { return this.items[getParentIndex(childIndex)]; }

        private bool hasLeftChild(int parentIndex) { return getLeftChildIndex(parentIndex) < size; }
        private bool hasRightChild(int parentIndex) { return getRightChildIndex(parentIndex) < size; }
        private bool hasParent(int childIndex) { return getLeftChildIndex(childIndex) > 0; }

        private void swap(int indexOne, int indexTwo)
        {
            int temp = this.items[indexOne];
            this.items[indexOne] = this.items[indexTwo];
            this.items[indexTwo] = temp;
        }

        private void hasEnoughCapacity()
        {
            if (this.size == capacity)
            {
                Array.Resize(ref this.items,capacity*2);
                capacity *= 2;
            }
        }

        public void Add(int item)
        {
            this.hasEnoughCapacity();
            this.items[size] = item;
            this.size++;
            heapifyUp();
        }

        public int Remove()
        {
            int item = this.items[0];
            this.items[0] = this.items[size-1];
            this.items[this.size - 1] = 0;
            size--;
            heapifyDown();
            return item;
        }

        private void heapifyUp()
        {
            int index = this.size - 1;
            while (hasParent(index) && this.items[index] > getParent(index))
            {
                swap(index, getParentIndex(index));
                index = getParentIndex(index);
            }
        }

        private void heapifyDown()
        {
            int index = 0;
            while (hasLeftChild(index))
            {
                int bigChildIndex = getLeftChildIndex(index);
                if (hasRightChild(index) && getLeftChild(index) < getRightChild(index))
                {
                    bigChildIndex = getRightChildIndex(index);
                }

                if (this.items[bigChildIndex] < this.items[index])
                {
                    break;
                }
                else
                {
                    swap(bigChildIndex,index);
                    index = bigChildIndex;
                }
            }
        }
    }
}

/*
Calling Code:
    MaxHeap mh = new MaxHeap();
    mh.Add(10);
    mh.Add(5);
    mh.Add(2);
    mh.Add(1);
    mh.Add(50);
    int maxVal  = mh.Remove();
    int newMaxVal = mh.Remove();
*/
person Bharathkumar V    schedule 01.02.2017

Вот еще одна реализация от команды NGenerics:

NGenerics PriorityQueue

person husayt    schedule 08.07.2011

Недавно у меня была такая же проблема, и в итоге я создал для нее пакет NuGet.

Это реализует стандартную очередь приоритетов на основе кучи. В нем также есть все обычные тонкости коллекций BCL: реализация ICollection<T> и IReadOnlyCollection<T>, пользовательская поддержка IComparer<T>, возможность указать начальную емкость и DebuggerTypeProxy, чтобы упростить работу с коллекцией в отладчике.

Существует также Inline версия пакета, которая просто устанавливает один файл .cs в ваш проект (полезно, если вы хотите избежать видимых извне зависимостей).

Дополнительная информация доступна на странице github.

person ChaseMedallion    schedule 29.07.2016

Следующая реализация PriorityQueue использует SortedSet из системной библиотеки.

using System;
using System.Collections.Generic;

namespace CDiggins
{
    interface IPriorityQueue<T, K> where K : IComparable<K>
    {
        bool Empty { get; }
        void Enqueue(T x, K key);
        void Dequeue();
        T Top { get; }
    }

    class PriorityQueue<T, K> : IPriorityQueue<T, K> where K : IComparable<K>
    {
        SortedSet<Tuple<T, K>> set;

        class Comparer : IComparer<Tuple<T, K>> {
            public int Compare(Tuple<T, K> x, Tuple<T, K> y) {
                return x.Item2.CompareTo(y.Item2);
            }
        }

        PriorityQueue() { set = new SortedSet<Tuple<T, K>>(new Comparer()); }
        public bool Empty { get { return set.Count == 0;  } }
        public void Enqueue(T x, K key) { set.Add(Tuple.Create(x, key)); }
        public void Dequeue() { set.Remove(set.Max); }
        public T Top { get { return set.Max.Item1; } }
    }
}
person cdiggins    schedule 01.01.2013
comment
SortedSet.Add завершится ошибкой (и вернет false), если у вас уже есть элемент в наборе с тем же приоритетом, что и элемент, который вы пытаетесь добавить. Итак ... если A.Compare (B) == 0 и A уже есть в списке, ваша функция PriorityQueue.Enqueue автоматически завершится ошибкой. - person Joseph; 05.03.2013
comment
Не могли бы объяснить, что такое T x и K key? Я предполагаю, что это уловка, позволяющая дублировать T x, и мне нужно сгенерировать уникальный ключ (например, UUID)? - person Thariq Nugrohotomo; 29.01.2018