C#图算法开发实战:从邻接表到关键路径的深度解析

一、图的基石:邻接表与邻接矩阵

1.1 邻接表实现(动态扩展)

/// 
/// 图结构核心类
/// 采用邻接表存储策略
/// 
public class Graph
{
    // 顶点数
    private int _vertexCount;
    
    // 邻接表:字典存储,顶点作为键,相邻顶点列表作为值
    private Dictionary<int, List<int>> _adjacencyList;

    /// 
    /// 构造函数
    /// 
    /// 顶点数量
    public Graph(int vertexCount)
    {
        _vertexCount = vertexCount;
        _adjacencyList = new Dictionary<int, List<int>>();
        
        // 初始化邻接表
        for (int i = 0; i < vertexCount; i++)
        {
            _adjacencyList[i] = new List<int>();
        }
    }

    /// 
    /// 添加边
    /// 支持有向/无向图切换
    /// 
    public void AddEdge(int source, int destination, bool isDirected = false)
    {
        // 添加正向边
        _adjacencyList[source].Add(destination);
        
        // 如果是无向图,添加反向边
        if (!isDirected)
        {
            _adjacencyList[destination].Add(source);
        }
    }

    /// 
    /// 获取邻接表
    /// 
    public Dictionary<int, List<int>> GetAdjacencyList()
    {
        return _adjacencyList;
    }

    /// 
    /// 打印图结构
    /// 可视化调试辅助
    /// 
    public void PrintGraph()
    {
        Console.WriteLine("图结构:");
        for (int i = 0; i < _vertexCount; i++)
        {
            Console.Write($"顶点 {i} -> ");
            foreach (var neighbor in _adjacencyList[i])
            {
                Console.Write($"{neighbor} ");
            }
            Console.WriteLine();
        }
    }
}

实现亮点

  • 使用字典实现动态扩展
  • 支持有向/无向图切换
  • 提供可视化调试接口
  • 时间复杂度:O(V + E)

二、深度优先搜索(DFS)进阶实现

2.1 递归与非递归双版本

/// 
/// DFS算法实现类
/// 支持递归和非递归两种模式
/// 
public class DfsAlgorithm
{
    private readonly Graph _graph;
    private readonly bool[] _visited;
    private readonly ILogger _logger;

    public DfsAlgorithm(Graph graph, ILogger logger)
    {
        _graph = graph;
        _visited = new bool[graph.GetVertexCount()];
        _logger = logger;
    }

    /// 
    /// 递归DFS
    /// 
    public void RecursiveDfs(int startVertex)
    {
        try
        {
            _logger.LogInformation($"开始递归DFS,起始顶点: {startVertex}");
            _visited[startVertex] = true;
            _logger.LogInformation($"访问顶点 {startVertex}");

            foreach (var neighbor in _graph.GetNeighbors(startVertex))
            {
                if (!_visited[neighbor])
                {
                    RecursiveDfs(neighbor);
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogError($"递归DFS异常: {ex.Message}");
        }
    }

    /// 
    /// 非递归DFS
    /// 使用显式栈实现
    /// 
    public void IterativeDfs(int startVertex)
    {
        try
        {
            _logger.LogInformation($"开始迭代DFS,起始顶点: {startVertex}");
            Stack<int> stack = new Stack<int>();
            stack.Push(startVertex);
            _visited[startVertex] = true;

            while (stack.Count > 0)
            {
                int current = stack.Pop();
                _logger.LogInformation($"访问顶点 {current}");

                foreach (var neighbor in _graph.GetNeighbors(current))
                {
                    if (!_visited[neighbor])
                    {
                        _visited[neighbor] = true;
                        stack.Push(neighbor);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            _logger.LogError($"迭代DFS异常: {ex.Message}");
        }
    }
}

设计哲学

  • 双模式实现满足不同场景需求
  • 日志记录支持调试追踪
  • 异常处理保障稳定性
  • 栈结构显式控制递归深度

三、广度优先搜索(BFS)实战应用

3.1 路径查找与最短距离

/// 
/// BFS算法实现类
/// 支持路径查找和距离计算
/// 
public class BfsAlgorithm
{
    private readonly Graph _graph;
    private readonly int[] _distance;
    private readonly int[] _predecessor;
    private readonly ILogger _logger;

    public BfsAlgorithm(Graph graph, ILogger logger)
    {
        _graph = graph;
        _distance = new int[graph.GetVertexCount()];
        _predecessor = new int[graph.GetVertexCount()];
        _logger = logger;
    }

    /// 
    /// BFS执行
    /// 返回最短路径信息
    /// 
    public Dictionary<int, List<int>> FindShortestPaths(int startVertex)
    {
        try
        {
            _logger.LogInformation($"开始BFS,起始顶点: {startVertex}");
            Queue<int> queue = new Queue<int>();
            Array.Fill(_distance, -1);
            Array.Fill(_predecessor, -1);

            queue.Enqueue(startVertex);
            _distance[startVertex] = 0;
            _logger.LogInformation($"顶点 {startVertex} 距离为 0");

            while (queue.Count > 0)
            {
                int current = queue.Dequeue();
                _logger.LogInformation($"处理顶点 {current}");

                foreach (var neighbor in _graph.GetNeighbors(current))
                {
                    if (_distance[neighbor] == -1)
                    {
                        _distance[neighbor] = _distance[current] + 1;
                        _predecessor[neighbor] = current;
                        _logger.LogInformation($"发现顶点 {neighbor},距离: {_distance[neighbor]}");
                        queue.Enqueue(neighbor);
                    }
                }
            }

            return BuildPathMap(startVertex);
        }
        catch (Exception ex)
        {
            _logger.LogError($"BFS异常: {ex.Message}");
            return null;
        }
    }

    /// 
    /// 构建路径映射
    /// 
    private Dictionary<int, List<int>> BuildPathMap(int startVertex)
    {
        var paths = new Dictionary<int, List<int>>();
        
        for (int i = 0; i < _graph.GetVertexCount(); i++)
        {
            if (i != startVertex)
            {
                var path = new List<int>();
                int current = i;
                
                while (current != -1)
                {
                    path.Add(current);
                    current = _predecessor[current];
                }
                
                path.Reverse();
                paths[i] = path;
            }
        }
        
        return paths;
    }
}

性能优势

  • O(V + E) 时间复杂度
  • 支持路径重建
  • 记录完整距离信息
  • 队列结构保证层级处理

四、关键路径算法:项目管理的核心

4.1 拓扑排序与最长路径

/// 
/// 关键路径算法实现
/// 基于拓扑排序的最长路径算法
/// 
public class CriticalPathAlgorithm
{
    private readonly Graph _graph;
    private readonly int[] _earliest;
    private readonly int[] _latest;
    private readonly ILogger _logger;

    public CriticalPathAlgorithm(Graph graph, ILogger logger)
    {
        _graph = graph;
        _earliest = new int[graph.GetVertexCount()];
        _latest = new int[graph.GetVertexCount()];
        _logger = logger;
    }

    /// 
    /// 计算关键路径
    /// 返回关键活动集合
    /// 
    public List<int> CalculateCriticalPath()
    {
        try
        {
            _logger.LogInformation("开始计算关键路径");
            var topologicalOrder = TopologicalSort();
            
            if (topologicalOrder == null)
            {
                _logger.LogWarning("图中存在环,无法计算关键路径");
                return null;
            }

            ComputeEarliestTimes(topologicalOrder);
            ComputeLatestTimes(topologicalOrder);
            
            return FindCriticalActivities();
        }
        catch (Exception ex)
        {
            _logger.LogError($"关键路径计算异常: {ex.Message}");
            return null;
        }
    }

    /// 
    /// 拓扑排序
    /// 基于入度的Kahn算法
    /// 
    private List<int> TopologicalSort()
    {
        _logger.LogInformation("执行拓扑排序");
        var inDegree = new int[_graph.GetVertexCount()];
        var result = new List<int>();
        var queue = new Queue<int>();

        // 计算初始入度
        for (int i = 0; i < _graph.GetVertexCount(); i++)
        {
            foreach (var neighbor in _graph.GetNeighbors(i))
            {
                inDegree[neighbor]++;
            }
        }

        // 入度为0的顶点入队
        for (int i = 0; i < _graph.GetVertexCount(); i++)
        {
            if (inDegree[i] == 0)
            {
                queue.Enqueue(i);
            }
        }

        // 处理拓扑序列
        while (queue.Count > 0)
        {
            int current = queue.Dequeue();
            result.Add(current);
            _logger.LogInformation($"拓扑序列添加顶点 {current}");

            foreach (var neighbor in _graph.GetNeighbors(current))
            {
                inDegree[neighbor]--;
                if (inDegree[neighbor] == 0)
                {
                    queue.Enqueue(neighbor);
                }
            }
        }

        // 检查是否为DAG
        if (result.Count != _graph.GetVertexCount())
        {
            _logger.LogWarning("检测到环,拓扑排序失败");
            return null;
        }

        return result;
    }

    /// 
    /// 计算最早时间
    /// 
    private void ComputeEarliestTimes(List<int> topologicalOrder)
    {
        _logger.LogInformation("计算最早时间");
        Array.Fill(_earliest, 0);

        foreach (var vertex in topologicalOrder)
        {
            foreach (var neighbor in _graph.GetNeighbors(vertex))
            {
                int weight = GetEdgeWeight(vertex, neighbor); // 假设已实现获取边权重
                _earliest[neighbor] = Math.Max(_earliest[neighbor], _earliest[vertex] + weight);
            }
        }
    }

    /// 
    /// 计算最晚时间
    /// 
    private void ComputeLatestTimes(List<int> topologicalOrder)
    {
        _logger.LogInformation("计算最晚时间");
        Array.Fill(_latest, int.MaxValue);
        _latest[^1] = _earliest[^1]; // 最后一个顶点的最晚时间等于最早时间

        // 反向处理拓扑序列
        for (int i = topologicalOrder.Count - 1; i >= 0; i--)
        {
            var vertex = topologicalOrder[i];
            
            foreach (var neighbor in _graph.GetNeighbors(vertex))
            {
                int weight = GetEdgeWeight(vertex, neighbor);
                _latest[vertex] = Math.Min(_latest[vertex], _latest[neighbor] - weight);
            }
        }
    }

    /// 
    /// 查找关键活动
    /// 
    private List<int> FindCriticalActivities()
    {
        _logger.LogInformation("查找关键活动");
        var critical = new List<int>();
        
        for (int i = 0; i < _graph.GetVertexCount(); i++)
        {
            foreach (var neighbor in _graph.GetNeighbors(i))
            {
                if (_earliest[i] + GetEdgeWeight(i, neighbor) == _earliest[neighbor])
                {
                    critical.Add(i * 10 + neighbor); // 唯一标识活动
                }
            }
        }
        
        return critical;
    }

    /// 
    /// 获取边权重
    /// 
    private int GetEdgeWeight(int source, int destination)
    {
        // 实际应用中应从图中获取真实权重
        // 这里假设所有边权重为1
        return 1;
    }
}

工程亮点

  • 完整的关键路径计算流程
  • 拓扑排序检测环路
  • 双时间数组精确计算
  • 支持活动标识
  • 日志记录关键决策点

五、实战案例:社交网络好友推荐系统

5.1 图数据库与好友推荐

/// 
/// 社交网络好友推荐系统
/// 基于共同好友的图算法
/// 
public class SocialNetworkRecommender
{
    private readonly Graph _userGraph;
    private readonly ILogger _logger;

    public SocialNetworkRecommender(ILogger logger)
    {
        _userGraph = new Graph(1000); // 假设最多1000个用户
        _logger = logger;
    }

    /// 
    /// 添加好友关系
    /// 
    public void AddFriendship(int user1, int user2)
    {
        _logger.LogInformation($"添加好友关系: {user1} - {user2}");
        _userGraph.AddEdge(user1, user2, isDirected: false);
    }

    /// 
    /// 获取好友推荐
    /// 
    public Dictionary<int, int> GetRecommendations(int userId, int topN = 5)
    {
        try
        {
            _logger.LogInformation($"为用户 {userId} 生成好友推荐");
            var recommendations = new Dictionary<int, int>();
            
            // 遍历所有用户
            for (int i = 0; i < _userGraph.GetVertexCount(); i++)
            {
                // 跳过自己和已有好友
                if (i == userId || _userGraph.AreFriends(userId, i))
                {
                    continue;
                }

                // 计算共同好友数量
                int commonFriends = CountCommonFriends(userId, i);
                if (commonFriends > 0)
                {
                    recommendations[i] = commonFriends;
                }
            }

            // 按共同好友数量排序
            return recommendations
                .OrderByDescending(kv => kv.Value)
                .Take(topN)
                .ToDictionary(kv => kv.Key, kv => kv.Value);
        }
        catch (Exception ex)
        {
            _logger.LogError($"生成推荐异常: {ex.Message}");
            return null;
        }
    }

    /// 
    /// 计算共同好友数量
    /// 
    private int CountCommonFriends(int user1, int user2)
    {
        var set1 = new HashSet<int>(_userGraph.GetNeighbors(user1));
        var set2 = new HashSet<int>(_userGraph.GetNeighbors(user2));
        
        set1.IntersectWith(set2);
        return set1.Count;
    }

    /// 
    /// 检查是否已经是好友
    /// 
    private bool AreFriends(int user1, int user2)
    {
        return _userGraph.GetNeighbors(user1).Contains(user2) &&
               _userGraph.GetNeighbors(user2).Contains(user1);
    }
}

系统特性

  • 基于共同好友的推荐算法
  • 使用集合操作提升效率
  • 支持实时好友关系更新
  • 推荐结果排序优化
  • 完善的异常处理机制

六、性能优化与最佳实践

6.1 图算法性能对比

算法 时间复杂度 空间复杂度 适用场景
DFS O(V + E) O(V) 连通性检测、路径查找
BFS O(V + E) O(V) 最短路径、层次遍历
拓扑排序 O(V + E) O(V) 项目计划、依赖管理
关键路径 O(V + E) O(V) 项目调度、甘特图
最小生成树 O(E log V) O(V + E) 网络设计、优化

6.2 通用优化策略

/// 
/// 图算法优化工具
/// 提供性能增强和内存管理功能
/// 
public static class GraphOptimization
{
    /// 
    /// 压缩邻接表
    /// 移除无效顶点和空链表
    /// 
    public static void CompressGraph(Graph graph)
    {
        var validVertices = new HashSet<int>();
        
        foreach (var vertex in graph.GetVertices())
        {
            if (graph.GetNeighbors(vertex).Any())
            {
                validVertices.Add(vertex);
            }
        }
        
        var newGraph = new Graph(validVertices.Count);
        
        foreach (var vertex in validVertices)
        {
            foreach (var neighbor in graph.GetNeighbors(vertex))
            {
                if (validVertices.Contains(neighbor))
                {
                    newGraph.AddEdge(vertex, neighbor);
                }
            }
        }
        
        // 替换原始图
        graph.ReplaceWith(newGraph);
    }

    /// 
    /// 边权重预处理
    /// 优化后续算法计算
    /// 
    public static void PreprocessWeights(Graph graph)
    {
        foreach (var vertex in graph.GetVertices())
        {
            var neighbors = graph.GetNeighbors(vertex);
            for (int i = 0; i < neighbors.Count; i++)
            {
                int weight = CalculateWeight(vertex, neighbors[i]); // 自定义权重计算
                graph.SetEdgeWeight(vertex, neighbors[i], weight);
            }
        }
    }

    /// 
    /// 并行化处理
    /// 适用于大规模图
    /// 
    public static void ParallelProcessing(Graph graph, Action<int> vertexProcessor)
    {
        Parallel.ForEach(graph.GetVertices(), vertex =>
        {
            vertexProcessor(vertex);
        });
    }
}

优化亮点

  • 内存压缩减少冗余
  • 权重预处理提升计算效率
  • 并行化处理大规模数据
  • 动态图结构优化
  • 通用工具适配多种算法

七、常见问题与解决方案

7.1 图结构异常

  • 问题: 检测到环路导致拓扑排序失败
  • 解决方案:
    • 使用Kahn算法检测环路
    • 在添加边时验证环路
    • 提供环路修复建议

7.2 性能瓶颈

  • 问题: 大规模图处理缓慢
  • 解决方案:
    • 采用压缩邻接表
    • 使用并行化处理
    • 优化数据结构访问模式

7.3 算法不准确

  • 问题: 最短路径计算错误
  • 解决方案:
    • 验证图权重设置
    • 检查边方向性
    • 使用单元测试验证

构建你的图算法武器库

在C#的世界里,图算法开发就像是一场精密的手术,需要精准的工具和严谨的流程。从邻接表的基础构建到关键路径的复杂计算,从社交网络的推荐系统到物流调度的优化方案,图算法无处不在。

记住,最好的工程师不是代码写得最快的人,而是最能理解问题本质的人。当你在设计图算法时,不妨问自己:

  • 这个问题的本质是什么?
  • 什么数据结构最适合这个场景?
  • 哪种算法能最高效地解决问题?

带着这些问题,选择最适合的工具,你的代码将变得更加优雅、高效和可靠。现在,是时候让你的图算法开始改变世界了!


配置示例

1. appsettings.json

{
  "GraphSettings": {
    "MaxVertexCount": 10000,
    "DefaultWeight": 1,
    "CompressionThreshold": 0.3,
    "ParallelProcessingEnabled": true
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "GraphAlgorithms": "Debug"
    }
  }
}

2. 项目文件(.csproj)

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>ExeOutputType>
    <TargetFramework>net8.0TargetFramework>
  PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Serilog" Version="2.11.0" />
    <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
  ItemGroup>
Project>

你可能感兴趣的:(C#学习资料,算法,c#,深度优先)