.NET 9 中的新增功能:更新了集合中的优先级队列

3e0995c13ad349d639b89c2cec28908e.jpeg

在这篇文章中,我将介绍 .NET 9(.NET 8 的后继者)中引入的最新集合功能。

.NET 中的图形算法刚刚变得更酷! .NET 9 为该类引入了一个新功能:方法。这种新增功能使得处理优先级队列变得更加容易,这对于处理寻路问题和图形遍历(例如 Dijkstra 算法的变体)的软件工程师来说是个好消息。

了解优先级队列

想象一下,在一条线上,人们不仅在等待轮到自己,而且根据他们的重要性被安排。这就是优先级队列的基本思想。优先级较高的人可以更快地排在队伍的最前面,这使他们非常适合像 Dijkstra 的最短路径算法这样的算法。

然而,传统的优先队列有一个问题:很难改变已经排队的人的重要性。这可能会使使用一些图形算法变得棘手。

using System;
using System.Collections.Generic;

class Program
{
    static void Main(string[] args)
    {
        // Create a priority queue of strings with int priorities
        PriorityQueue<string, int> priorityQueue = new PriorityQueue<string, int>();

        // Enqueue elements with priority
        priorityQueue.Enqueue("Niraj", 2); // John has priority 2
        priorityQueue.Enqueue("Alice", 1); // Alice has priority 1
        priorityQueue.Enqueue("Emily", 3); // Emily has priority 3
        priorityQueue.Enqueue("David", 4); // David has priority 4

        // Dequeue elements (sorted by priority)
        Console.WriteLine("People are being served according to their priority:");
        while (priorityQueue.Count > 0)
        {
            var person = priorityQueue.Dequeue();
            Console.WriteLine($"Serving {person}");
        }
    }
}

在这里,创建了一个优先级队列,用于保存人员的姓名以及他们各自的优先级。元素被排入优先级队列中,并具有分配的优先级。然后,该程序遍历队列,逐个对元素进行出列,并将它们打印到控制台。

新方法Remove

通常,数组堆的一个问题是它们不支持优先级更新,这使得它们在算法(例如 Dijkstra 算法的变体)中使用时被禁止。

.NET 9 为类中的方法带来了一个新选项。此方法需要四个参数:RemovePriorityQueue

  • TElement element:该人(或事物)想从队列中取出。

  • TElement outMatchingElement:此变量将保存实际被删除的人(或事物)(如果行中有重复项,则很有用)。

  • TPriority outPriority:此变量将保持已删除的人(或事物)的重要性级别。

  • IEqualityComparer<TElement> comparer:用于处理具有重复重要性级别的情况的可选工具。

其工作原理如下:

  1. 打电话与想要改变重要性的人(或事物)为。Remove

  2. 该方法删除人(或事物)(并将其存储在 和 中)。outMatchingElementoutPriority

  3. 然后,将同一个人(或事物)放回原处,但这次使用新的、更高的重要性级别使用该方法。Enqueue

简单示例

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

class Program
{
    static void Main(string[] args)
    {
        // Create a priority queue of strings with int priorities
        PriorityQueue<string, int> priorityQueue = new PriorityQueue<string, int>();

        // Enqueue elements with priority
        priorityQueue.Enqueue("Niraj", 2); // John has priority 2
        priorityQueue.Enqueue("Alice", 1); // Alice has priority 1
        priorityQueue.Enqueue("Emily", 3); // Emily has priority 3
        priorityQueue.Enqueue("David", 4); // David has priority 4
        priorityQueue.Enqueue("John", 2); // John has priority 2

        // removing the element - "Alice" from priority queue
        priorityQueue.Remove("Alice", out _, out _);

        // removing the element - "Alice" from priority queue
        string removedElement;
        int priority;
        priorityQueue.Remove("John", out removedElement, out priority);
        Console.WriteLine($"Person {removedElement} which had a priority {priority} was removed from the queue.");

        // Dequeue elements (sorted by priority)
        Console.WriteLine("People are being served according to their priority:");
        while (priorityQueue.Count > 0)
        {

            var person = priorityQueue.Dequeue();
            Console.WriteLine($"Serving {person}");
        }
    }
}

在此示例中,元素以其分配的优先级排队到优先级队列中,然后使用该方法从队列中删除 2 个元素。最后,该程序遍历队列,逐个取消排队元素并将它们打印到控制台。Remove

具有优先级队列的 Dijkstra 算法示例

IGraph 接口:

public interface IGraph<TNode>
{
    IEnumerable<TNode> GetNeighbors(TNode node);
    int GetEdgeWeight(TNode source, TNode target);
}

此接口定义了表示图形所需的基本功能:

  • GetNeighbors(TNode node):此方法检索给定节点的相邻节点列表。

  • GetEdgeWeight(TNode source, TNode target):此方法返回与两个节点之间的边关联的权重。

SampleGraph 类:

public class SampleGraph : IGraph<string>
{
    private readonly Dictionary<string, List<KeyValuePair<string, int>>> edges = new Dictionary<string, List<KeyValuePair<string, int>>>();

    public SampleGraph()
    {
        // Add edges and their weights to the dictionary here
        edges.Add("A", new List<KeyValuePair<string, int>>() { new KeyValuePair<string, int>("B", 4), new KeyValuePair<string, int>("C", 2) });
        edges.Add("B", new List<KeyValuePair<string, int>>() { new KeyValuePair<string, int>("D", 2), new KeyValuePair<string, int>("E", 3) });
        edges.Add("C", new List<KeyValuePair<string, int>>() { new KeyValuePair<string, int>("D", 5) });
        edges.Add("D", new List<KeyValuePair<string, int>>() { new KeyValuePair<string, int>("E", 1) });
    }

    public IEnumerable<string> GetNeighbors(string node)
    {
        if (!edges.ContainsKey(node))
        {
            return Enumerable.Empty<string>();
        }
        return edges[node].Select(x => x.Key);
    }

    public int GetEdgeWeight(string source, string target)
    {
        if (!edges.ContainsKey(source) || !edges[source].Any(x => x.Key == target))
        {
            throw new ArgumentException("Edge not found in the graph");
        }
        return edges[source].First(x => x.Key == target).Value;
    }
}

此类实现简单内存中图的接口。IGraph

  • 它使用字典,其中键是节点名称(字符串),值是键值对的列表。每个键值对都代表一个邻居及其权重。edges

  • 构造函数 () 通过将边及其权重添加到字典中来初始化图形(您可以将其替换为实际的图形数据)。SampleGraph()

  • GetNeighbors(string node):此方法检查字典中是否存在节点。如果是这样,它通过从相应值的键值对中提取键来返回相邻节点的列表。

  • GetEdgeWeight(string source, string target):此方法检查源节点是否存在,以及它是否与目标节点有边。如果这两个条件都满足,则返回源节点和目标节点之间的边权重。否则,它将抛出一个 .ArgumentException

DijkstraWithPriorityQueue 类:

public static class DijkstraWithPriorityQueue
{
    public static Dictionary<TNode, int> FindShortestPaths<TNode>(
        IGraph<TNode> graph, TNode startNode)
    {
        var distances = new Dictionary<TNode, int>();
        distances[startNode] = 0;

        var priorityQueue = new PriorityQueue<TNode, int>();
        priorityQueue.Enqueue(startNode, 0);

        while (priorityQueue.TryDequeue(out TNode currentNode, out int currentDistance))
        {
            foreach (var neighbor in graph.GetNeighbors(currentNode))
            {
                var edgeWeight = graph.GetEdgeWeight(currentNode, neighbor);
                var tentativeDistance = currentDistance + edgeWeight;

                // Check if relaxation is needed
                if (!distances.ContainsKey(neighbor) || tentativeDistance < distances[neighbor])
                {
                    distances[neighbor] = tentativeDistance;

                    // Simulate priority update using Remove and Enqueue
                    priorityQueue.Remove(neighbor, out _, out _);
                    priorityQueue.Enqueue(neighbor, tentativeDistance);
                }
            }
        }

        return distances;
    }
}

此类使用优先级队列实现 Dijkstra 的最短路径算法。

  • FindShortestPaths<TNode>(IGraph<TNode> graph, TNode startNode):此静态方法将图形和起始节点作为输入。

  • 它创建一个字典来存储到目前为止为每个节点找到的最短距离。distances

  • 它将起始节点的距离初始化为 0,将所有其他节点的距离初始化为无穷大(通过在字典中不包括它们来表示)。

  • 它创建一个优先级队列 (),其中节点根据其与起始节点的当前距离(较低的距离 = 较高的优先级)确定优先级。priorityQueue

  • 该算法进入一个循环,该循环一直持续到优先级队列为空,它将优先级最高(最接近起始节点)的节点从队列中脱离队列。它遍历已出列的节点的所有邻居。对于每个邻居,它通过将当前节点与邻居之间的边权重与当前节点的当前距离相加来计算暂定距离。如果暂定距离短于邻居的当前已知距离,它将更新字典中的距离,并使用更新的距离将邻居重新加入优先级队列(模拟优先级更新)。distances

  • 最后,该方法返回包含从起始节点到图形中所有其他节点的最短距离的字典。distances

程序类:

public class Program
{
    public static void Main(string[] args)
    {
        var graph = new SampleGraph();
        var startNode = "A";

        var shortestDistances = DijkstraWithPriorityQueue.FindShortestPaths(graph, startNode);

        Console.WriteLine("Shortest distances from {0}:", startNode);
        foreach (var node in shortestDistances)
        {
            Console.WriteLine("{0}: {1}", node.Key, node.Value);
        }
    }
}

此类是程序的入口点。

  • 它创建一个表示示例图形的对象。SampleGraph

  • 它定义起始节点。

  • 它从类中调用方法,以查找从起始节点到所有其他节点的最短距离。FindShortestPathsDijkstraWithPriorityQueue

  • 它打印计算出的到控制台的最短距离。

总的来说,这段代码演示了如何使用类中的新方法(尽管是为了模拟更新)来实现 Dijkstra 的算法并在图形中找到最短路径。RemovePriorityQueue

优势和用例

新方法提供以下功能:Remove

  • 简化优先级队列更新: 这种方法提供了一种更易于管理的方式来更新基于阵列的队列中的优先级,即使它不是性能最高的选项。

  • 增强教育和原型设计体验: 此功能使 .NET 9 中的图形算法更容易地试验和理解。

  • 开发基本的寻路解决方案:对于渐近性能不重要的情况,此方法提供了一种实现寻路算法的实用方法。

  • 如果你喜欢我的文章,请给我一个赞!谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值