Изменить приоритет в пользовательской очереди приоритетов - PullRequest
9 голосов
/ 10 февраля 2012

Я следовал указаниям, данным в этом вопросе (ответ Джейсона), чтобы написать мой PriorityQueue<T>, используя SortedList. Я понимаю, что поле count в этом классе используется для обеспечения уникальных приоритетов и сохранения порядка постановки в очередь среди одного и того же приоритета.

Однако, когда count достигает своего максимального значения, и я суммирую 1 с ним, последний снова начинает с 0, поэтому приоритет последующих элементов будет выше, чем приоритет предыдущих элементов. Используя этот подход, мне может понадобиться способ «безопасного» сброса счетчика count ... Фактически, предположим, что имеется следующее состояние очереди (в формате priority | count | item ):

0 | 123 | A
0 | 345 | B
1 | 234 | C
2 | 200 | Д

Теперь предположим, что предел счетчика достигнут, поэтому я должен сбросить его до 0: как следствие, следующий вставленный элемент будет иметь счетчик 0: например, если я вставлю элемент с приоритетом, равным 1, он будет неправильно введен перед 1 | 234 | D

0 | 123 | A
0 | 345 | B
1 | 000 | новый элемент
1 | 234 | C
2 | 200 | Д

Проблему приоритета можно решить путем реализации кучи: я создал класс Heap, затем использовал Heap<KeyValuePair<TPriority, TElement> и пользовательский PriorityComparer, чтобы отсортировать элементы по TPriority. С учетом TPriority как int и TElement как string, PriorityComparer выглядит следующим образом:

public class MyComparer : IComparer<KeyValuePair<int, string>>
{
    public int Compare(KeyValuePair<int, string> x, KeyValuePair<int, string> y)
    {
        return x.Key.CompareTo(y.Key);
    }
}

...

int capacity = 10;
Heap<KeyValuePair<int, string>> queue;
queue = new Heap<KeyValuePair<int, string>>(capacity, new PriorityComparer());

...

UPDATE Таким образом (используя PriorityComparer) мне удалось реализовать очередь с приоритетами. Теперь я хотел бы добавить поддержку для изменения его поведения во время выполнения, то есть переключиться с FIFO на сортировку приоритетов и наоборот. Поскольку моя реализация очереди с приоритетами имеет поле IComparer, я думаю, что для редактирования этого поля достаточно добавить свойство Comparer, например:

public IComparer
{
    set
    {
        this._comparer = value;
    }
}

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

public class PriorityQueue<T, int>
{
    private Queue<T> _defaultQueue;
    private bool _priority;
    private SortedList<int, Queue<T>> _priorityQueues;

    public PriorityQueue(int capacity)
    {
        this._defaultQueue = new Queue<T>(capacity);
        this._priority = false;
        this._priorityQueues = new SortedList<int, Queue<T>>(0);
    }

    public void PriorityEnable()
    {
        this._priority = true;
    }

    public void PriorityDisable()
    {

        this._priority = false;
    }

    public void Enqueue(T item)
    {
        if (this._priority)
        {
            // enqueue to one of the queues
            // with associated priority
            // ...
        }
        else this._defaultQueue.Enqueue(item);
    }

    public T Dequeue()
    {
        if (this._priority)
        {
            // dequeue from one of the queues
            // with associated priority and
            // return
            // ...
        }
        return this._defaultQueue.Dequeue();
    }
}
  1. Как управлять переходом из режим FIFO в режим приоритета , когда в очереди по умолчанию все еще есть элементы? Я мог бы скопировать их в очереди приоритетов в зависимости от приоритета элемента ... Другие лучшие решения?
  2. Как управлять переходом из в приоритетный режим в FIFO режим ? В этом случае у меня было бы несколько очередей с приоритетом, которые могут содержать элементы, но мне больше не нужно управлять ими в соответствии с приоритетом и даже не знать первоначальный порядок прибытия ...
  3. Как я могу управлять емкостью разных очередей?
  4. Как насчет характеристик двух вышеупомянутых решений? Который использует больше памяти?

Ответы [ 3 ]

1 голос
/ 18 февраля 2012

Вы можете "обмануть" и использовать BigInteger , чтобы у вас никогда не заканчивались цифры.Это, конечно, приводит к постепенному ухудшению производительности с течением времени, но, вероятно, не настолько значительным, чтобы иметь значение.

Объедините это с очередью приоритетов на основе heap и вы настроены!1008 *

Не пытайтесь «переключиться с FIFO на сортировку по приоритетам и наоборот» - просто поместите элементы в обе структуры данных, соответствующие задаче ( Очередь и приоритеточереди).

1 голос
/ 21 февраля 2012

Я бы использовал Queue и Priority Queue.
Но если вам нужно ...

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

Для обычного поведения используйте клавишу priority.

Когда куча заполнится, HEAPIFY с помощью клавиши time.
Затем удалите n необходимых элементов.
Теперь HEAPIFY снова с помощью priorityключ для возврата к обычному поведению.

1 голос
/ 10 февраля 2012

РЕДАКТИРОВАТЬ: Вы изменили то, что вы просили, с вашими правками.Вы перешли от одного вопроса к новому подходу и задали новый вопрос.Вероятно, следует открыть новый вопрос для вашего нового подхода, так как этот теперь запутывает вопрос о том, какой ответ / ответ на какой вопрос / комментарий.Я полагаю, что на ваш первоначальный вопрос о сортировке равных приоритетов был дан ответ.

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

Может быть, вместо этого использовать GUID для каждого элемента?

Guid.NewGuid()

РЕДАКТИРОВАТЬ:

Чтобы добавить после редактирования: Если вы хотите, чтобы новый 1 был помещен после существующего, В переопределении Сравнить, верните большеечем результат (1), когда значения равны.Таким образом, произойдет следующее:

1 > 0, return greater (1), continue
1 > 0, return greater (1), continue
1 == 1, return greater (1), continue
1 < 2, return less than (-1), insert

РЕДАКТИРОВАТЬ 2:

Если второй параметр предназначен только для уникального значения, вы всегда можете использовать строку и установить значение как числовоеСтроки вместо.Таким образом, вы никогда не достигнете предела, просто нужно будет соответственно проанализировать строку.Вы можете использовать начальные альфа-значения, которые представляют новый набор.

Я не проверял этот код, просто представление о том, что вы могли бы сделать.

static string leadingStr = "";
static char currentChar = 'a';
static Int32 currentId = Int32.MinValue;

static string getNextId()
{
    if (currentId >= Int32.MaxValue)
    {
        currentId = Int32.MinValue;
        if (currentChar >= 'z')
        {
            currentChar = 'a';
            leadingStr = leadingStr.Insert(0, "X");
        }
        else
            currentChar++;
    }
    else
        currentId++;

    return String.Format("{0}{1}-{2}", leadingStr, currentChar, currentId);
}

РЕДАКТИРОВАТЬ 3: Сбросить значения

static Int64 currentValue = Int64.MinValue;
static void AddItem(object item)
{
    if (currentValue == Int64.MaxValue)
        RecountItems();

    item.counter = currentValue++;
    SortedList.Add(item);
}

static void RecountItems()
{
    currentValue = 0;
    foreach (var item in SortedList)
    {
        item.counter = currentValue++;
    }
}

Редактировать 4: Для вашего второго вопроса:

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

static Object RemoveNextFIFO()
{
    if (fifoList.Count > 0)
    {
        var removedItem = fifoList[0];
        fifoList.RemoveAt(0);
        RemoveItemFromPriority(removedItem);
        return removedItem;
    }
}

static void RemoveItemFromPriority(Object itemToRemove)
{
    foreach (var counter in priorityQueue)
    {
        if (counter == itemToRemove.counter)
        {
            priorityQueue.remove(item);
            break;
        }
    }
}

static Object RemoveFromFIFO(int itemCounter)
{
    foreach (var item in fifoList)
    {
        if (item.counter == itemCounter)
        {
            fifoList.Remove(item);
            return item;   
        }
    }
}

static Object RemoveNextPriority()
{
    if (priorityQueue.Count > 0)
    {
        var counter = priorityQueue.Pop();
        return RemoveFromFIFO(counter);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...