找回密码
 立即注册
首页 业界区 业界 重生之数据结构与算法----图论

重生之数据结构与算法----图论

焦尔蕾 2025-6-4 21:46:11
简介

图结构本质上还有多叉树的变种,图结构在逻辑上,由于若干个节点和边组成。但在实际落地中,一般用邻接表,邻接矩阵来存储图
在标准的树结构中,一般都是单链表表示,即只允许父节点指向子节点,两个子节点之间也不允许互相指向。
而图中,则是双链表放飞自我版,既可以父子之间互相指向,又可以子节点互相链接,形成复杂的网络结构。
图的逻辑视图

1.png

可以看到一幅图由节点(Vertex)与边(Edge)组成,那么从直觉出发,我们可以认为它的数据结构应该是这个样子的
  1.     public class Vertex
  2.     {
  3.         public int Value { get; set; }
  4.         Vertex[] Neighbors { get; set; }
  5.     }
复制代码
可以看到,与多叉树并无区别,所以图在本质上还是树.因此适用于树的DFS/BFS算法同样适用于图
Degree

图论中有一个独特的概念,叫度(Degree).
在没有方向的图中,Degree就是每个节点相连边的条数。在有方向的图中,Degree被细分为indegree和outdegree
2.png

比如在此图中,节点3的indegree为3,outdegree为1。节点4的indegree为3,outdegree为0
图的实际视图

与上面代码相反的是,图的实际存储方式如下
邻接表

3.png

0号节点存储着它的indegree,【4,3,1】
2号节点存储着它的indegree,【3,2,4】
......
代码结构如下:
  1. //邻接表
  2. //List存节点,Int[]存储相邻节点
  3. List<int[]> grath = new List<int[]>();
复制代码
邻接矩阵

4.png

邻接矩阵则是把所有可能的节点都穷举描绘出来,然后再到上面标点。
代码结构如下:
  1. //邻接矩阵
  2. //二维数组
  3. bool[,] matrix = new bool[5,5];
复制代码
为什么会有两种不同存储方式?
因为任何结构都有两个考虑因素,时间与空间。这是一个万能公式。

  • 可以直观的看到,邻接矩阵是空间换时间,通过填充整个矩阵,只需要matrix[i,j]就能以O(1)的复杂度实现查找。
  • 而邻接表则是时间换空间,只存储必要的信息,节省了空间,但查找复杂度退化为O(N)
加权图

上面介绍的图最基本的结构,是不是很简单?所有的复杂结构都是在简单上一步一步演化的,图也不例外。
那加权图又如何实现呢?回忆我们的套路.算法共一石,空间换时间独占八斗。
邻接表加权
  1. //List<int[]> grath = new List<int[]>();
  2. // 空间换时间,加一个字段存权重不就好了?
  3. List<Edge[]> grath = new List<Edge[]>();
  4. public struct Edge
  5. {
  6.         public int Indegree { get; set; }
  7.         public int Weight { get; set; }
  8. }
复制代码
5.png

矩阵表加权
  1. //bool[,] matrix = new bool[5,5];
  2. //由bool二维数组切换成int二维数组
  3. //=0 代表没有边,!=0 代表有边且与权重
  4. int[,] matrix = new int[5,5];
复制代码
6.png

无向图

上面我们介绍的,都是有向无权图与有向加权图。那什么是无向图呢?
很简单,无向图=双向图
7.png

所以你无脑数,有几条边就有几个节点,不再区分indegree,outdegree
一个简单的图
  1.     public interface IGraphSimple
  2.     {
  3.         /// <summary>
  4.         /// 添加一条边
  5.         /// </summary>
  6.         /// <param name="from"></param>
  7.         /// <param name="to"></param>
  8.         /// <param name="weight"></param>
  9.         void AddEdge(int from, int to, int weight);
  10.         /// <summary>
  11.         /// 删除一条边
  12.         /// </summary>
  13.         /// <param name="from"></param>
  14.         /// <param name="to"></param>
  15.         void RemoveEdge(int from, int to);
  16.         /// <summary>
  17.         /// 判断两个节点是否相等
  18.         /// </summary>
  19.         /// <param name="from"></param>
  20.         /// <param name="to"></param>
  21.         /// <returns></returns>
  22.         bool IsEdge(int from, int to);
  23.         /// <summary>
  24.         /// 返回一条边的权重
  25.         /// </summary>
  26.         /// <param name="from"></param>
  27.         /// <param name="to"></param>
  28.         /// <returns></returns>
  29.         int? Weight(int from, int to);
  30.         List<Edge> Neighbors(int v);
  31.     }
  32.     public struct Edge
  33.     {
  34.         /// <summary>
  35.         /// 相邻的节点
  36.         /// </summary>
  37.         public int Indegree { get; set; }
  38.         /// <summary>
  39.         /// 权重
  40.         /// </summary>
  41.         public int Weight { get; set; }
  42.     }
  43.     /// <summary>
  44.     /// 邻接表实现图
  45.     /// </summary>
  46.     public class AdjacencySimple : IGraphSimple
  47.     {
  48.         public static void Run()
  49.         {
  50.             var s = new AdjacencySimple(10);
  51.             s.AddEdge(0, 1, 0);
  52.             s.AddEdge(0, 2, 0);
  53.             s.AddEdge(2, 5, 0);
  54.             s.AddEdge(2, 6, 0);
  55.             s.AddEdge(1, 3, 0);
  56.             s.AddEdge(1, 4, 0);
  57.             s.AddEdge(3, 6, 0);
  58.             s.AddEdge(3, 0, 0);
  59.             s.AddEdge(6, 0, 0);
  60.             s.DFSTraverse(0);
  61.         }
  62.         private List<List<Edge>> _graph;
  63.         private bool[] _visited;
  64.         private LinkedList<int> _path=new LinkedList<int>();
  65.         public AdjacencySimple(int capacity)
  66.         {
  67.             //init
  68.             _graph = new List<List<Edge>>(capacity);
  69.             _visited=new bool[capacity];
  70.             for (int i = 0; i < capacity; i++)
  71.             {
  72.                 _graph.Add(new List<Edge>());
  73.             }
  74.         }
  75.         public void Add(int from, int to, int weight)
  76.         {
  77.             //如果是无向加权表,就调用此方法
  78.             AddEdge(from, to, weight);
  79.             //多维护一遍关系
  80.             AddEdge(from,to, weight);
  81.         }
  82.         public void AddEdge(int from, int to, int weight)
  83.         {
  84.             var neighbor = new Edge()
  85.             {
  86.                 Indegree = to,
  87.                 Weight = weight
  88.             };
  89.             _graph[from].Add(neighbor);
  90.         }
  91.         public bool IsEdge(int from, int to)
  92.         {
  93.             foreach (var edge in _graph[from])
  94.             {
  95.                 if (edge.Indegree.Equals(to))
  96.                 {
  97.                     return true;
  98.                 }
  99.             }
  100.             return false;
  101.         }
  102.         public List<Edge> Neighbors(int from)
  103.         {
  104.             return _graph[from];
  105.         }
  106.         public void Remove(int from, int to)
  107.         {
  108.             //如果是无向加权表,就调用此方法
  109.             RemoveEdge(from, to);
  110.             //多维护一遍关系
  111.             RemoveEdge(to, from);
  112.         }
  113.         public void RemoveEdge(int from, int to)
  114.         {
  115.             var neighbors = _graph[from];
  116.             foreach (var edge in neighbors)
  117.             {
  118.                 if (edge.Indegree.Equals(to))
  119.                 {
  120.                     neighbors.Remove(edge);
  121.                     break;
  122.                 }
  123.             }
  124.         }
  125.         public int? Weight(int from, int to)
  126.         {
  127.             var neighbors = _graph[from];
  128.             foreach (var edge in neighbors)
  129.             {
  130.                 if (edge.Indegree.Equals(to))
  131.                 {
  132.                     return edge.Weight;
  133.                 }
  134.             }
  135.             return null;
  136.         }
  137.         public void DFSTraverse(int startIndex)
  138.         {
  139.             if (startIndex < 0 || startIndex >= _graph.Count)
  140.                 return;
  141.             
  142.             if (_visited[startIndex])
  143.                 return;
  144.             _visited[startIndex] = true;
  145.             //前序遍历
  146.             Console.WriteLine($"index={startIndex}");
  147.             if (_graph[startIndex]?.Count > 0)
  148.             {
  149.                 foreach (var item in _graph[startIndex])
  150.                 {
  151.                     DFSTraverse(item.Indegree);
  152.                 }
  153.             }
  154.             //后序遍历
  155.             //Console.WriteLine($"index={index}");
  156.         }
  157.     }
  158.     /// <summary>
  159.     /// 邻接矩阵实现图
  160.     /// </summary>
  161.     public class MatrixSimple : IGraphSimple
  162.     {
  163.         private int[,] _matrix;
  164.         private bool[] _visited;
  165.         public static void Run()
  166.         {
  167.             var s = new MatrixSimple(10);
  168.             s.AddEdge(0, 1, 1);
  169.             s.AddEdge(0, 2, 2);
  170.             s.AddEdge(2, 5, 3);
  171.             s.AddEdge(2, 6, 4);
  172.             s.AddEdge(1, 3, 5);
  173.             s.AddEdge(1, 4, 6);
  174.             s.AddEdge(3, 6, 7);
  175.             s.AddEdge(3, 0, 8);
  176.             s.AddEdge(6, 0, 9);
  177.             s.DFSTraverse(0);
  178.         }
  179.         public MatrixSimple(int capacity)
  180.         {
  181.             _matrix = new int[capacity, capacity];
  182.             _visited = new bool[capacity];
  183.         }
  184.         public void Add(int from, int to, int weight)
  185.         {
  186.             //如果是无向加权表,就调用此方法
  187.             AddEdge(from, to, weight);
  188.             //多维护一遍关系
  189.             AddEdge(to, from, weight);
  190.         }
  191.         public void AddEdge(int from, int to, int weight)
  192.         {
  193.             _matrix[from, to] = weight;
  194.         }
  195.         public bool IsEdge(int from, int to)
  196.         {
  197.             return _matrix[from, to] != 0;
  198.         }
  199.         public List<Edge> Neighbors(int from)
  200.         {
  201.             var result=new List<Edge>();
  202.             var columns = _matrix.GetLength(from);
  203.             for (int i = 0; i < columns; i++)
  204.             {
  205.                 if (_matrix[columns, i] > 0)
  206.                 {
  207.                     result.Add(new Edge { Indegree = i, Weight = _matrix[columns, i] });
  208.                 }
  209.             }
  210.             return result;
  211.         }
  212.         public void Remove(int from, int to)
  213.         {
  214.             //如果是无向加权表,就调用此方法
  215.             RemoveEdge(from, to);
  216.             //多维护一遍关系
  217.             RemoveEdge(to, from);
  218.         }
  219.         public void RemoveEdge(int from, int to)
  220.         {
  221.             //0代表未使用
  222.             _matrix[from, to] = 0;
  223.         }
  224.         public int? Weight(int from, int to)
  225.         {
  226.             return _matrix[from, to];
  227.         }
  228.         public void DFSTraverse(int startIndex)
  229.         {
  230.             if (_visited[startIndex])
  231.                 return;
  232.             _visited[startIndex] = true;
  233.             //前序遍历
  234.             Console.WriteLine($"index={startIndex}");
  235.             for (int i = 0; i < _visited.Length; i++)
  236.             {
  237.                 //为0代表未使用
  238.                 if (_matrix[startIndex, i] == 0)
  239.                     continue;
  240.                 DFSTraverse(i);
  241.             }
  242.             //后序遍历
  243.             //Console.WriteLine($"index={index}");
  244.         }
  245.     }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册