【问题标题】:Change the priority in a custom priority queue更改自定义优先级队列中的优先级
【发布时间】:2012-03-03 04:58:08
【问题描述】:

我按照this question(Jason 的回答)中给出的指示使用SortedList 编写我的PriorityQueue<T>。我了解此类中的 count 字段用于确保唯一的优先级并保持相同优先级之间的入队顺序。

但是,当count达到最大值并且我对其加1时,后者又会从0开始,所以后续项的优先级会高于前面项的优先级。使用这种方法,我可能需要一种“安全地”重置计数器count...事实上,假设具有以下队列状态(格式为 priority | count | item):

0 | 123 |一个
0 | 345 |乙
1 | 234 | C
2 | 200 | D

现在假设计数器已达到限制,所以我必须将其重置为 0:因此,下一个插入的项目将具有计数器 0:例如,如果我插入一个优先级等于 1 的元素,它将是在 1 之前错误插入 | 234 | D

0 | 123 |一个
0 | 345 | B
1 | 000 |新元素
1 | 234 | C
2 | 200 | D

优先级问题可以通过实现堆来解决:我创建了一个Heap 类,然后我使用Heap<KeyValuePair<TPriority, TElement> 和一个自定义PriorityComparer 来按TPriority 对元素进行排序。 给定 TPriority 作为 int 和 TElement 作为 stringPriorityComparer 如下:

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());

...

更新 通过这种方式(使用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. 以上两种解决方案的性能如何?哪个会占用更多内存?

【问题讨论】:

  • 我的回复对您的问题有帮助吗?

标签: c# .net algorithm data-structures


【解决方案1】:

编辑:您通过编辑改变了您的要求。你从提出一个问题到采用一种新方法并提出一个新问题。可能应该为您的新方法打开一个新问题,因为这个问题现在令人困惑,什么答案/响应是什么问题/评论。我相信您最初关于排序相同优先级的问题已经得到解答。

您可以使用 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:

如果第二个参数只是一个唯一值,您可以始终使用字符串并将值设置为数字字符串。这样你就永远不会达到上限,只需要相应地解析字符串。您可以使用表示新集合的前导 alpha 值。

我没有测试过这段代码,只是想知道你能做什么。

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 堆栈,但也可以使用仅存储项目唯一 ID 的优先级列表。但是,每次从 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);
    }
}

【讨论】:

  • 好吧,SortedList 使用IComparer 自动对项目进行排序:我已经创建了这个比较器,它可以按优先级和计数器排序(具有相同的项目优先级通过比较它们的计数器进行排序)。问题是,在某些时候,计数器达到最大值并从 0 重新开始:在重置计数器之前添加的任何项目都会有一个更大的计数器,因此它们看起来好像它们是在最后一个之后插入的。 Guid 并没有解决问题,因为它是随机的,所以它可能会扭曲插入的顺序。
  • 是否可以将附加参数附加到您的数据中?也就是说,如果你有一个从 0 到 255 的 id(只是一个例子),那么你可以有一个从 0 到 255 的 idSecond,你首先检查 id 然后是 idSecond?因此,您将拥有从 0,0 到 0,1 到 ... 到 255,255 的所有内容,这将使您的允许数字平方?使用两个 long 肯定会超出您的要求,对吧? -- 来自学徒程序员。
  • @BlackVegetable: 是否可以在你的数据中附加一个额外的参数? 如果没有其他选择,我认为唯一的权利是添加另一个参数,也许long 包含当前日期和时间。
  • 第二个参数的意义何在?我认为这是为了保持一个唯一的身份。使用 Guid 会确保这不会吗?优先级仍然按优先级 id 而不是唯一 id 排序。如果您添加新项目,如果您在相等时返回 1,它们将始终放置在最后一个匹配优先级之后。
  • 我认为他需要一个特定的顺序,不一定是它们添加的顺序,对吧?
【解决方案2】:

我会同时使用QueuePriority Queue
但如果你必须...

一个元素用两个键代替一个键。
第一个键 priority 将是优先级。
第二个键 time 将是一个类似于时间戳的计数器。

对于常规行为,请使用 priority 键。

当堆满时,通过timeHEAPIFY 它。
然后删除 n 需要的元素。
现在HEAPIFY 再次使用priority 键返回常规行为。

【讨论】:

  • 当堆满时,也许我应该通过 priority 键(而不是按时间)re-heapify...
  • 这更像是一个好玩的想法,队列+优先队列是要走的路。
  • 我想我会使用一个堆:每个元素包括优先级就足够了,因为重复对堆来说不是问题(具有相同值的项目按顺序放置到达,无需添加更多字段)。一旦从队列中删除了一些元素(N 元素),我就会使用彼此相等的新优先级重新堆集。
  • 在 OOD 中,它表示更喜欢组合而不是继承。同样的规则在这里也成立。如果您想要多个数据结构的优点,那么在组合中使用它们。在组合中,您无需更改数据结构代码,这是一个很大的优势。
  • 我应该使用Heap 对象作为新PriorityQueue 类的私有字段吗?
【解决方案3】:

您可以“作弊”并使用BigInteger,这样您就永远不会“用完数字”。随着时间的推移,这当然会导致性能逐渐下降,但可能还不够重要。

将它与基于heap 的优先级队列结合起来,您就可以设置了!


不要尝试“从 FIFO 切换到优先排序,反之亦然” - 只需将元素放在适合任务的 both 数据结构中(Queue 和优先级队列)。

【讨论】:

  • 我只需要“从 FIFO 切换到优先排序,反之亦然”。这样,当队列填充超过一定限制时,它会切换到优先队列。然后,当填充水平降低到某个阈值以下时,它会切换到 FIFO。
  • @enzom83 为什么?你想完成什么?
  • 我需要控制传出消息到连接的流向,所以我为每个连接使用一个队列:如果队列中的消息太多,可能连接速度很慢,那么我可以应用一个传出消息的优先级。
  • @enzom83 那么为什么你不能总是使用优先队列呢?
  • 因为在正常情况下(即不需要控制流量)我需要一个FIFO排序。
猜你喜欢
  • 2012-02-24
  • 2022-01-07
  • 2011-12-20
  • 1970-01-01
  • 2013-02-13
  • 1970-01-01
  • 2010-10-01
  • 2012-04-15
  • 1970-01-01
相关资源
最近更新 更多