85 km 217 km 173 km 80 km 186 km 167 km 250 km 84 km 183 km 502 km Frankfurt [0] Mannheim [1] Würzburg [2] Kassel [3] Karlsruhe [4] Augsburg [5] München [6] Erfurt [7] Nürnberg [8] Stuttgart [9]
<svg version="1.1" viewBox="0 0 1200 900" xmlns="http://www.w3.org/2000/svg">
 <line x1="600" x2="300" y1="80" y2="250" stroke="black" stroke-width="2"/>
 <line x1="599.73" x2="660.04" y1="79.562" y2="215.41" stroke="black" stroke-width="2"/>
 <line x1="705.03" x2="765.35" y1="310.8" y2="446.64" stroke="black" stroke-width=".80161"/>
 <line x1="300" x2="200" y1="250" y2="450" stroke="black" stroke-width="2"/>
 <line x1="200" x2="350" y1="450" y2="650" stroke="black" stroke-width="2"/>
 <line x1="350" x2="600" y1="650" y2="800" stroke="black" stroke-width="2"/>
 <line x1="629.12" x2="498.96" y1="316.31" y2="451.26" stroke="black" stroke-width="2"/>
 <line x1="761.13" x2="648.66" y1="534.55" y2="583.92" stroke="black" stroke-width=".5269"/>
 <line x1="682.17" x2="781.08" y1="757.03" y2="526.34" stroke="black" stroke-width="2"/>
 <line x1="1045.2" x2="689.14" y1="408.67" y2="792.75" stroke="black" stroke-width="2"/>
 <line x1="1003.2" x2="720.59" y1="303.34" y2="76.433" stroke="black" stroke-width="2"/>
 <text x="430" y="150" font-family="serif" font-size="20" fill="blue" text-anchor="middle">85 km</text>
 <text x="598.17426" y="189.20297" font-family="serif" font-size="20" fill="blue" text-anchor="middle">217 km</text>
 <text x="774.0871" y="381.63113" font-family="serif" font-size="20" fill="blue" text-anchor="middle">173 km</text>
 <text x="240" y="340" font-family="serif" font-size="20" fill="blue" text-anchor="middle">80 km</text>
 <text x="609.91656" y="377.53476" font-family="serif" font-size="20" fill="blue" text-anchor="middle">186 km</text>
 <text x="774.88416" y="626.56165" font-family="serif" font-size="20" fill="blue" text-anchor="middle">167 km</text>
 <text x="260" y="560" font-family="serif" font-size="20" fill="blue" text-anchor="middle">250 km</text>
 <text x="460" y="740" font-family="serif" font-size="20" fill="blue" text-anchor="middle">84 km</text>
 <text x="614.73596" y="557.49768" font-family="serif" font-size="20" fill="blue" text-anchor="middle">183 km</text>
 <text x="987.81281" y="515.77393" font-family="serif" font-size="20" fill="blue" text-anchor="middle">502 km</text>
 <ellipse cx="600" cy="80" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="600" y="95" font-family="serif" font-size="24" text-anchor="middle" fill="black">Frankfurt [0]</text>
 <ellipse cx="300" cy="250" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="300" y="265" font-family="serif" font-size="24" text-anchor="middle" fill="black">Mannheim [1]</text>
 <ellipse cx="664.78" cy="265.85" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="664.78" y="280.85" font-family="serif" font-size="24" text-anchor="middle" fill="black">Würzburg [2]</text>
 <ellipse cx="1026.2" cy="356.49" rx="100" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="1021.22" y="348.15" font-family="serif" font-size="24" text-anchor="middle" fill="black">Kassel [3]</text>
 <ellipse cx="200" cy="450" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="200" y="465" font-family="serif" font-size="24" text-anchor="middle" fill="black">Karlsruhe [4]</text>
 <ellipse cx="350" cy="650" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="350" y="665" font-family="serif" font-size="24" text-anchor="middle" fill="black">Augsburg [5]</text>
 <ellipse cx="600" cy="800" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="600" y="815" font-family="serif" font-size="24" text-anchor="middle" fill="black">München [6]</text>
 <ellipse cx="500" cy="450" rx="100" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="500" y="465" font-family="serif" font-size="24" text-anchor="middle" fill="black">Erfurt [7]</text>
 <ellipse cx="775.67" cy="484.2" rx="120" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="789.85" y="495.03" font-family="serif" font-size="24" text-anchor="middle" fill="black">Nürnberg [8]</text>
 <ellipse cx="594.76" cy="627.48" rx="110" ry="50" fill="white" stroke="black" stroke-width="2"/>
 <text x="594.76" y="642.48" font-family="serif" font-size="24" text-anchor="middle" fill="black">Stuttgart [9]</text>
</svg>

这段内容是讲 图(Graph) 的定义和一个示例图的可视化(用 SVG 绘制的“城市距离图”)。我帮你分成两部分解释:

一、概念解释

What Is a Graph?
A graph ( G = (V, E) ) is a set of vertices ( V ) (points in a space), and edges ( E ) (links between these vertices).
Edges may or may not be oriented, that is, directed or undirected, respectively. Moreover, edges may be weighted, that is, assigned a value.
理解:
图 ( G = (V, E) ) 是由 顶点(Vertices)边(Edges) 组成的集合:

  • 顶点(V):图中的点,代表对象(比如城市、节点、任务等)。
  • 边(E):顶点之间的连接,代表关系(比如道路、通信链路等)。
    进一步地:
  • 有向边(directed edge):边有方向(例如从A指向B)。
  • 无向边(undirected edge):边没有方向(A与B是相互连接的)。
  • 加权边(weighted edge):每条边有一个“权值”(如距离、成本、时间等)。
  • 加权顶点(weighted vertex):顶点本身也可以带权值(例如城市人口、节点负载)。

二、SVG 图示理解

SVG 画的是一个“城市距离图”,用来说明图的直观结构。
每个城市是一个 顶点(vertex),城市之间的道路(带距离的连线)是 边(edge)

顶点(城市)
城市名称 索引 位置 说明
Frankfurt [0] 顶部中央 图的中心城市
Mannheim [1] 左上 与 Frankfurt 相连
Würzburg [2] 右上 与 Frankfurt 相连
Kassel [3] 右下远端 与 Frankfurt、München 等城市相连
Karlsruhe [4] 左中 与 Mannheim、Augsburg 相连
Augsburg [5] 左下 与 Karlsruhe、München 相连
München [6] 底部中央 大终点,很多城市通向这里
Erfurt [7] 中央偏左 中间枢纽节点
Nürnberg [8] 右中 与 Würzburg、Stuttgart 等相连
Stuttgart [9] 右下中 与 Nürnberg、München 等相连
边(道路)举例:
起点 终点 距离
Frankfurt → Mannheim 85 km
Frankfurt → Würzburg 217 km
Würzburg → Nürnberg 103 km
Mannheim → Karlsruhe 80 km
Karlsruhe → Augsburg 250 km
Augsburg → München 84 km
München → Stuttgart 167 km
München → Kassel 502 km
这其实是一个 加权无向图(weighted undirected graph),展示了城市之间的连接与距离。

三、总结

概念 含义
图(Graph) 顶点 + 边 的集合
顶点(Vertex) 图中的点(如城市)
边(Edge) 顶点间的连接(如道路)
有向/无向 是否存在方向性
加权 是否有数值(距离、权重等)
图示(SVG) 展示城市与道路的关系,是图论的直观实例
Adjacency List
<?xml version="1.0" encoding="UTF-8"?> 85 km 217 km 173 km 80 km 186 km 167 km 250 km 84 km 183 km 502 km Frankfurt [0] Mannheim [1] Würzburg [2] Kassel [3] Karlsruhe [4] Augsburg [5] München [6] Erfurt [7] Nürnberg [8] Stuttgart [9]

<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1" viewBox="0 0 1200 900" xmlns="http://www.w3.org/2000/svg">
 <!-- &#23450;&#20041;&#31661;&#22836; marker -->
 <defs>
  <marker id="arrow" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="3">
   <path d="m0 0v6l9-3z"/>
  </marker>
  <marker id="arrow-9" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="3">
   <path d="m0 0v6l9-3z"/>
  </marker>
 </defs>
 <g stroke="#000">
  <!-- Connections (lines) with arrow -->
  <line x1="600.12" x2="368.28" y1="79.791" y2="207.67" marker-end="url(#arrow)" stroke-width="2"/>
  <line x1="599.73" x2="660.04" y1="79.562" y2="215.41" marker-end="url(#arrow)" stroke-width="2"/>
  <line x1="704.97" x2="752.9" y1="310.77" y2="431.66" marker-end="url(#arrow)" stroke-width=".80161"/>
  <g stroke-width="2">
   <line x1="300.13" x2="208.21" y1="249.94" y2="400.01" marker-end="url(#arrow)"/>
   <line x1="199.77" x2="302.68" y1="449.83" y2="600.13" marker-end="url(#arrow)"/>
   <line x1="349.84" x2="537.6" y1="649.74" y2="749.38" marker-end="url(#arrow)"/>
   <line x1="629.36" x2="538.76" y1="316.08" y2="401.45" marker-end="url(#arrow)"/>
  </g>
  <line x1="753.66" x2="669.13" y1="539.06" y2="754.4" marker-end="url(#arrow)" stroke-width="1.8286"/>
  <line x1="1036.9" x2="707.44" y1="411.13" y2="771.11" marker-end="url(#arrow)" stroke-width="1.9858"/>
  <line x1="702.62" x2="949.43" y1="109.26" y2="319.72" marker-end="url(#arrow)" stroke-width="2.0955"/>
 </g>
 <g fill="blue" font-family="serif" font-size="20" text-anchor="middle">
  <!-- Distance labels -->
  <text x="430" y="150">85 km</text>
  <text x="598.17426" y="189.20297">217 km</text>
  <text x="774.0871" y="381.63113">173 km</text>
  <text x="240" y="340">80 km</text>
  <text x="609.91656" y="377.53476">186 km</text>
  <text x="774.88416" y="626.56165">167 km</text>
  <text x="260" y="560">250 km</text>
  <text x="460" y="740">84 km</text>
  <text x="614.73596" y="557.49768">183 km</text>
  <text x="987.81281" y="515.77393">502 km</text>
  <!-- Cities -->
 </g>
 <ellipse cx="600" cy="80" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="600" y="95" fill="black" font-family="serif" font-size="24" text-anchor="middle">Frankfurt [0]</text>
 <ellipse cx="300" cy="250" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="300" y="265" fill="black" font-family="serif" font-size="24" text-anchor="middle">Mannheim [1]</text>
 <ellipse cx="664.78" cy="265.85" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="664.78" y="280.85" fill="black" font-family="serif" font-size="24" text-anchor="middle">Würzburg [2]</text>
 <ellipse cx="1026.2" cy="356.49" rx="100" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="1021.22" y="348.15" fill="black" font-family="serif" font-size="24" text-anchor="middle">Kassel [3]</text>
 <ellipse cx="200" cy="450" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="200" y="465" fill="black" font-family="serif" font-size="24" text-anchor="middle">Karlsruhe [4]</text>
 <ellipse cx="350" cy="650" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="350" y="665" fill="black" font-family="serif" font-size="24" text-anchor="middle">Augsburg [5]</text>
 <ellipse cx="600" cy="800" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="600" y="815" fill="black" font-family="serif" font-size="24" text-anchor="middle">München [6]</text>
 <ellipse cx="500" cy="450" rx="100" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="500" y="465" fill="black" font-family="serif" font-size="24" text-anchor="middle">Erfurt [7]</text>
 <ellipse cx="775.67" cy="484.2" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="789.85" y="495.03" fill="black" font-family="serif" font-size="24" text-anchor="middle">Nürnberg [8]</text>
 <ellipse cx="594.76" cy="627.48" rx="110" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="594.76" y="642.48" fill="black" font-family="serif" font-size="24" text-anchor="middle">Stuttgart [9]</text>
 <line x1="719.17" x2="650.39" y1="529.87" y2="583.29" marker-end="url(#arrow-9)" stroke="#000" stroke-width="1.3395"/>
</svg>
 Outer Range - Vertices                        Inner Range - Edges                        
    ┌──────────┬──┐       ┌───┬──────┬───┐        ┌───┬──────┬───┐        ┌───┬──────┬───┐
 0  │Frankfürt │ ─┼───────▶ 1 │  85  │ ──┼───────▶│ 4 │ 217  │ ──┼───────▶│ 6 │173   │ / │
    ├──────────┼──┤       └───┴──────┴───┘        └───┴──────┴───┘        └───┴──────┴───┘
 1  │Mannheim  │  │                                                                       
    ├──────────┼──┤                                                                       
 2  │Karlsruhe │  │                                                                       
    ├──────────┼──┤                                                                       
 3  │Augsburg  │  │                                                                       
    ├──────────┼──┤                                                                       
 4  │Würzburg  │  │                                                                       
    ├──────────┼──┤                                                                       
 5  │Nürnberg  │  │                                                                       
    ├──────────┼──┤                                                                       
 6  │Kassel    │  │                                                                       
    ├──────────┼──┤                                                                       
 7  │Erfurt    │ /│                                                                       
    ├──────────┼──┤                                                                       
 8  │München   │ /│                                                                       
    ├──────────┼──┤                                                                       
 9  │Stuttgart │ /│                                                                       
    └──────────┴──┘          
 Outer Range - Vertices                        Inner Range - Edges                        
    ┌──────────┬──┐       ┌───┬──────┬───┐        ┌───┬──────┬───┐        ┌───┬──────┬───┐
 0  │Frankfürt │ ─┼──────▶│ 1 │  85  │ ──┼───────▶│ 4 │ 217  │ ──┼───────▶│ 6 │173   │ / │
    ├──────────┼──┤       ├───┼──────┼───┤        └───┴──────┴───┘        └───┴──────┴───┘
 1  │Mannheim  │ ─┼──────▶│ 2 │  80  │ / │                                                
    ├──────────┼──┤       ├───┼──────┼───┤                                                
 2  │Karlsruhe │ ─┼──────▶│ 3 │  250 │ / │                                                
    ├──────────┼──┤       ├───┼──────┼───┤                                                
 3  │Augsburg  │ ─┼──────▶│ 8 │  84  │ / │                                                
    ├──────────┼──┤       ├───┼──────┼───┤        ┌───┬──────┬───┐                        
 4  │Würzburg  │ ─┼──────▶│ 5 │  103 │ ──┼───────▶│ 7 │ 186  │ / │                        
    ├──────────┼──┤       ├───┼──────┼───┤        ├───┼──────┼───┤                        
 5  │Nürnberg  │ ─┼──────▶│ 8 │  502 │ ──┼───────▶│ 9 │ 183  │ / │                        
    ├──────────┼──┤       ├───┼──────┼───┤        └───┴──────┴───┘                        
 6  │Kassel    │ ─┼──────▶│ 8 │  85  │ / │                                                
    ├──────────┼──┤       └───┴──────┴───┘                                                
 7  │Erfurt    │ /│                                                                       
    ├──────────┼──┤                                                                       
 8  │München   │ /│                                                                       
    ├──────────┼──┤                                                                       
 9  │Stuttgart │ /│                                                                       
    └──────────┴──┘                                                                       
85 km 217 km 173 km 80 km 186 km 167 km 250 km 84 km 183 km 502 km Frankfurt [0] Mannheim [1] Würzburg [2] Kassel [3] Karlsruhe [4] Augsburg [5] München [6] Erfurt [7] Nürnberg [8] Stuttgart [9]
<svg version="1.1" viewBox="0 0 1200 900" xmlns="http://www.w3.org/2000/svg">
 <!-- &#23450;&#20041;&#31661;&#22836; marker -->
 <defs>
  <!-- &#32456;&#28857;&#31661;&#22836; -->
  <marker id="arrow-end" markerHeight="10" markerWidth="10" orient="auto" refX="10" refY="3">
   <path d="m0 0v6l9-3z"/>
  </marker>
  <!-- &#36215;&#28857;&#31661;&#22836;&#65288;&#21453;&#21521;&#65289; -->
  <marker id="arrow-start" markerHeight="10" markerWidth="10" orient="auto" refY="3">
   <path d="m9 0v6l-9-3z"/>
  </marker>
 </defs>
 <g stroke="#000">
  <!-- Connections (lines) &#21452;&#31661;&#22836; -->
  <line x1="600.12" x2="368.28" y1="79.791" y2="207.67" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width="2"/>
  <line x1="599.73" x2="660.04" y1="79.562" y2="215.41" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width="2"/>
  <line x1="704.97" x2="752.9" y1="310.77" y2="431.66" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width=".80161"/>
  <g stroke-width="2">
   <line x1="300.13" x2="208.21" y1="249.94" y2="400.01" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)"/>
   <line x1="199.77" x2="302.68" y1="449.83" y2="600.13" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)"/>
   <line x1="349.84" x2="537.6" y1="649.74" y2="749.38" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)"/>
   <line x1="629.36" x2="538.76" y1="316.08" y2="401.45" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)"/>
  </g>
  <line x1="753.66" x2="669.13" y1="539.06" y2="754.4" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width="1.8286"/>
  <line x1="1036.9" x2="707.44" y1="411.13" y2="771.11" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width="1.9858"/>
  <line x1="702.62" x2="949.43" y1="109.26" y2="319.72" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width="2.0955"/>
  <line x1="719.17" x2="650.39" y1="529.87" y2="583.29" marker-end="url(#arrow-end)" marker-start="url(#arrow-start)" stroke-width="1.3395"/>
 </g>
 <g fill="blue" font-family="serif" font-size="20" text-anchor="middle">
  <!-- Distance labels -->
  <text x="430" y="150">85 km</text>
  <text x="598.17426" y="189.20297">217 km</text>
  <text x="774.0871" y="381.63113">173 km</text>
  <text x="240" y="340">80 km</text>
  <text x="609.91656" y="377.53476">186 km</text>
  <text x="774.88416" y="626.56165">167 km</text>
  <text x="260" y="560">250 km</text>
  <text x="460" y="740">84 km</text>
  <text x="614.73596" y="557.49768">183 km</text>
  <text x="987.81281" y="515.77393">502 km</text>
 </g>
 <!-- Cities -->
 <ellipse cx="600" cy="80" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="600" y="95" fill="black" font-family="serif" font-size="24" text-anchor="middle">Frankfurt [0]</text>
 <ellipse cx="240.78" cy="208.29" rx="120.07" ry="43.395" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="246.61725" y="211.61723" fill="#000000" font-family="serif" font-size="24px" text-anchor="middle">Mannheim [1]</text>
 <ellipse cx="664.78" cy="265.85" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="664.78" y="280.85" fill="black" font-family="serif" font-size="24" text-anchor="middle">Würzburg [2]</text>
 <ellipse cx="1026.2" cy="356.49" rx="100" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="1021.22" y="348.15" fill="black" font-family="serif" font-size="24" text-anchor="middle">Kassel [3]</text>
 <ellipse cx="104.91" cy="424.98" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="104.91196" y="439.97684" fill="#000000" font-family="serif" font-size="24px" text-anchor="middle">Karlsruhe [4]</text>
 <ellipse cx="350" cy="650" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="350" y="665" fill="black" font-family="serif" font-size="24" text-anchor="middle">Augsburg [5]</text>
 <ellipse cx="600" cy="800" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="600" y="815" fill="black" font-family="serif" font-size="24" text-anchor="middle">München [6]</text>
 <ellipse cx="500" cy="450" rx="100" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="500" y="465" fill="black" font-family="serif" font-size="24" text-anchor="middle">Erfurt [7]</text>
 <ellipse cx="775.67" cy="484.2" rx="120" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="789.85" y="495.03" fill="black" font-family="serif" font-size="24" text-anchor="middle">Nürnberg [8]</text>
 <ellipse cx="594.76" cy="627.48" rx="110" ry="50" fill="#fff" stroke="#000" stroke-width="2"/>
 <text x="594.76" y="642.48" fill="black" font-family="serif" font-size="24" text-anchor="middle">Stuttgart [9]</text>
</svg>
 Outer Range - Vertices                        Inner Range - Edges                                
    ┌──────────┬──┐       ┌───┬──────┬───┐        ┌───┬──────┬───┐        ┌───┬──────┬───┐        
 0  │Frankfürt │ ─┼──────▶│ 1 │  85  │ ──┼───────▶│ 4 │ 217  │ ──┼───────▶│ 6 │173   │ / │        
    ├──────────┼──┤       ├───┼──────┼───┤        ┢━━━┿━━━━━━┿━━━┪        └───┴──────┴───┘        
 1  │Mannheim  │ ─┼──────▶│ 2 │  80  │ ━━┿━━━━━━━▶┃ 0 │  85  │ / ┃                                
    ├──────────┼──┤       ├───┼──────┼───┤        ┣━━━┿━━━━━━┿━━━┫                                
 2  │Karlsruhe │ ─┼──────▶│ 3 │  250 │ ━━┿━━━━━━━▶┃ 1 │  80  │ / ┃                                
    ├──────────┼──┤       ├───┼──────┼───┤        ┣━━━┿━━━━━━┿━━━┫                                
 3  │Augsburg  │ ─┼──────▶│ 8 │  84  │ ━━┿━━━━━━━▶┃ 2 │  250 │ / ┃                                
    ├──────────┼──┤       ├───┼──────┼───┤        ┡━━━┿━━━━━━┿━━━┩        ┏━━━┯━━━━━━┯━━━┓        
 4  │Würzburg  │ ─┼──────▶│ 5 │  103 │ ──┼───────▶│ 7 │ 186  │ ━━┿━━━━━━━▶┃ 0 │  217 │ / ┃        
    ├──────────┼──┤       ├───┼──────┼───┤        ├───┼──────┼───┤        ┣━━━┿━━━━━━┿━━━┫        
 5  │Nürnberg  │ ─┼──────▶│ 8 │  502 │ ──┼───────▶│ 9 │ 183  │ ━━┿━━━━━━━▶┃ 4 │  103 │ / ┃        
    ├──────────┼──┤       ├───┼──────┼───┤        ┢━━━┿━━━━━━┿━━━┪        ┗━━━┷━━━━━━┷━━━┛        
 6  │Kassel    │ ─┼──────▶│ 8 │  85  │ ━━┿━━━━━━━▶┃ 0 │ 173  │ / ┃                                
    ├──────────┼──┤       ┢━━━┿━━━━━━┿━━━┪        ┗━━━┷━━━━━━┷━━━┛                                
 7  │Erfurt    │ ━┿━━━━━━▶┃ 4 │  186 │ / ┃                                                        
    ├──────────┼──┤       ┣━━━┿━━━━━━┿━━━┫        ┏━━━┯━━━━━━┯━━━┓        ┏━━━┯━━━━━━┯━━━┓        
 8  │München   │ ━┿━━━━━━▶┃ 3 │  84  │ ━━╋━━━━━━━▶┃ 5 │ 167  │ ━━╋━━━━━━━▶┃ 6 │  502 │ / ┃        
    ├──────────┼──┤       ┣━━━┿━━━━━━┿━━━┫        ┗━━━┷━━━━━━┷━━━┛        ┗━━━┷━━━━━━┷━━━┛        
 9  │Stuttgart │ ━┿━━━━━━▶┃ 5 │  183 │ / ┃                                                        
    └──────────┴──┘       ┗━━━┷━━━━━━┷━━━┛                                                        

一、Adjacency List(邻接表)

图示含义

邻接表是一种图的常见表示形式:

  • 外层(Outer Range - Vertices):是顶点(城市,如 Frankfurt、Mannheim…)
  • 内层(Inner Range - Edges):是该顶点连接到的边(目标城市和距离)

举例解释

0 │Frankfürt│ ─▶│ 1 │  85  │ ─▶│ 4 │ 217  │ ─▶│ 6 │173   │ / │

意思是:

  • 顶点 0 (Frankfurt) 有三条边:
    • 到顶点 1(Mannheim),距离 85
    • 到顶点 4(Würzburg),距离 217
    • 到顶点 6(Kassel),距离 173

二、C++ 数据结构定义

route — 表示一条边

struct route {
    int target_id;      // 目标顶点的编号
    double distance;    // 边的权重(距离)
};

edges_type — 邻接链表

using edges_type = std::list<route>;

每个顶点的所有出边,用一个 std::list<route> 表示。

vertex — 表示一个顶点

struct vertex {
    edges_type edges;   // 该顶点的所有出边
    std::string name;   // 顶点的名称(如 “Frankfurt”)
};

vertices_type — 整个图的节点集合

using vertices_type = std::vector<vertex>;

即:整个图是一组顶点(每个顶点又包含一个边表)。

🗺 三、SVG 图形结构

SVG 文件是这个邻接表的可视化版本。

  • <ellipse class="city">:绘制城市节点(椭圆)
  • <line class="line">:绘制边(带箭头的线)
  • <text class="distance">:显示边上的距离数字
  • <text class="city-text">:显示城市名与编号
    每个城市的坐标(cx, cy)手动或程序生成,例如:
<ellipse class="city" cx="600" cy="80" rx="120" ry="50"/>
<text class="city-text" x="600" y="95">Frankfurt [0]</text>

四、Edge List(边列表)

另一种图表示方法:
直接列出所有的边,用表格表示。

From To Distance
Frankfurt Mannheim 85
Frankfurt Würzburg 217
Frankfurt Kassel 173
Mannheim Karlsruhe 80
Karlsruhe Augsburg 250
Augsburg München 84
Würzburg Erfurt 186
Würzburg Nürnberg 103
Nürnberg Stuttgart 183
Nürnberg München 167
Kassel München 502
也可以用数字索引表示:
source_id target_id distance
--------- --------- --------
0 1 85
0 4 217
0 6 173
1 2 80
2 3 250
3 8 84
4 7 186
4 5 103
5 9 183
5 8 167
6 8 502

五、图的其他种类

类型 说明
Bipartite Graph(二部图) 顶点分成两组,只允许跨组连接
n-partite Graph(多部图) 多个独立组间的连接
Hypergraph(超图) 一条边可以连接两个以上顶点(本系统暂不支持)

六、命名规范(Naming Conventions)

为了在算法模板中统一命名:

模板参数 示例变量 含义
G g 图对象
V u, v, x, y 顶点(引用)
VId uid, vid 顶点编号
VR ur, vr 顶点范围
VI ui, vi 顶点迭代器
VV val 顶点的用户自定义值
VVF vvf 顶点值函数
E uv, vw 边引用
ER er 边范围
EI uvi, vwi 边迭代器
EV val 边值
EVF evf 边值函数
这套命名通常出现在泛型算法模板中(如 Dijkstra、BFS、DFS、Prim 等)。

七、总结

层级 内容 对应代码或图形
图结构定义 顶点与边的抽象 route, vertex, vertices_type
存储方式 邻接表(vector) Adjacency List
可视化表示 SVG 文件绘图 各城市及边箭头
算法输入形式 Edge List source_id, target_id, distance
命名规范 模板参数与变量命名约定 泛型算法模板接口

一个完整的 “城市路网图(Graph of Cities & Routes)” 的定义、遍历和最短路径算法示例。下面我给出逐步解析,帮助你全面理解每一部分的含义和设计思想。

一、原始数据(Raw Data – Cities & Routes)

using city_id_type = int32_t;
using city_name_type = string;
vector<city_name_type> city_names = {
    "Frankfürt", "Mannheim", "Karlsruhe", "Augsburg", "Würzburg",
    "Nürnberg", "Kassel", "Erfurt", "München", "Stuttgart"
};

含义:

  • 城市编号 city_id_type(用作顶点ID)。
  • 城市名称 city_name_type
  • city_names 是一个顶点表,共 10 个城市。

边数据(Edge List)

using route_data = copyable_edge_t<city_id_type, double>; // {source_id, target_id, value}
vector<route_data> routes_doubled = {
    {0, 1, 85.0},  {0, 4, 217.0}, {0, 6, 173.0},
    {1, 0, 85.0},  {1, 2, 80.0},
    {2, 1, 80.0},  {2, 3, 250.0},
    {3, 2, 250.0}, {3, 8, 84.0},
    {4, 0, 217.0}, {4, 5, 103.0}, {4, 7, 186.0},
    {5, 4, 103.0}, {5, 8, 167.0}, {5, 9, 183.0},
    {6, 0, 173.0}, {6, 8, 502.0},
    {7, 4, 186.0},
    {8, 3, 84.0},  {8, 5, 167.0}, {8, 6, 502.0},
    {9, 5, 183.0},
};

含义:

  • 每条边 {source_id, target_id, distance} 表示一条有向路。
  • 因为是双向路网(Frankfurt↔Mannheim),所以成对出现(正反方向各一条)。
    例如:
{0, 1, 85.0} 表示 Frankfürt → Mannheim 距离 85 km
{1, 0, 85.0} 表示 Mannheim → Frankfürt 距离 85 km

二、构建图结构(Graph Construction)

struct route {
    city_id_type target_id = 0;
    double distance = 0.0; // km
};
using AdjList = vector<list<route>>;
// G 是基于 city_names 与 routes 构建的图
using G = rr_adaptor<AdjList, city_names_type>;
G g(city_names, routes_doubled);

解析:

  • 每个城市对应一个 list<route>(邻接表);
  • 整个图 G 是一个邻接表的集合;
  • rr_adaptor 是一个“range of ranges”封装器,用于迭代访问图的顶点和边;
  • routes_doubled 提供连接关系和权重信息。

三、遍历图(Graph Traversal with Views)

cout << "Traverse the vertices & outgoing edges" << endl;
for (auto&& [uid, u] : vertexlist(g)) {        // [id, vertex&]
    cout << city_id(g, uid) << endl;           // 打印城市名 [id]
    for (auto&& [vid, uv] : incidence(g, uid)) { // [target_id, edge&]
        cout << "   --> " << city_id(g, vid) << endl; // 打印相邻城市
    }
}

输出示例:

Traverse the vertices & outgoing edges
Frankfürt [0] --> Mannheim [1] --> Würzburg [4] --> Kassel [6]
Mannheim [1] --> Frankfürt [0] --> Karlsruhe [2]
Karlsruhe [2] --> Mannheim [1] --> Augsburg [3]
Augsburg [3] --> Karlsruhe [2] --> München [8]
Würzburg [4] --> Frankfürt [0] --> Nürnberg [5] --> Erfurt [7]
...
München [8] --> Augsburg [3] --> Nürnberg [5] --> Kassel [6]
Stuttgart [9] --> Nürnberg [5]

理解要点:

  • 外层循环:遍历每个顶点。
  • 内层循环:遍历该顶点的所有出边(incidence)。
  • city_id(g, uid) 显示城市编号;city(g, uid) 则可取城市名。

四、Dijkstra 最短路径(Dijkstra’s Shortest Paths)

void dijkstra_clrs(
    G&& g, vertex_id_t<G> seed,
    Distance& distance, Predecessor& predecessor,
    WF weight
);

解释:

  • g:图对象;
  • seed:起点(顶点编号);
  • distance[uid]:保存起点到各顶点的最短距离;
  • predecessor[uid]:保存最短路径中上一个顶点;
  • WF:权重函数(从边获取权重值)。

五、按“段数”计算最短路径(不考虑公里数)

auto weight_1 = [](edge_reference_t<G> uv) -> int {
    return 1;
};
std::vector<int> distance(size(vertices(g)));
std::vector<vertex_id_t<G>> predecessor(size(vertices(g)));
dijkstra_clrs(g, frankfurt_id, distance, predecessor, weight_1);
cout << "Shortest distance (segments) from " << city(g, frankfurt_id) << endl;
for (vertex_id_t<G> uid = 0; uid < size(vertices(g)); ++uid)
    if (distance[uid] > 0)
        cout << "  --> " << city_id(g, uid)
             << " - " << distance[uid] << " segments" << endl;

解释:

  • 每条边权重恒为 1(return 1),表示“经过一段路”;
  • 最终结果显示每个城市距离 Frankfurt 经过的“段数”。
    输出示例:
Shortest distance (segments) from Frankfürt
  --> Mannheim [1] - 1 segments
  --> Karlsruhe [2] - 2 segments
  --> Augsburg [3] - 3 segments
  --> Würzburg [4] - 1 segments
  --> Nürnberg [5] - 2 segments
  --> Kassel [6] - 1 segments
  --> Erfurt [7] - 2 segments
  --> München [8] - 2 segments
  --> Stuttgart [9] - 3 segments

六、整体结构总结

层级 含义 示例
顶点(vertex) 城市(City) Frankfurt, Mannheim…
边(edge) 城市间路线 {source_id, target_id, distance}
邻接表(AdjList) 各城市出边集合 vector<list<route>>
图(Graph) 城市网络 G g(city_names, routes_doubled)
遍历(Traversal) 查看所有城市及连接 vertexlist(g) + incidence(g, uid)
最短路径算法 Dijkstra dijkstra_clrs()
权重函数 weight(uv) 可返回距离或“段数”

七、知识拓展

概念 说明
routes_doubled 双向边结构,确保每条路两端互通
rr_adaptor “range of ranges” 工具,用于在 C++ ranges 框架中遍历图
copyable_edge_t 边类型包装器,确保可拷贝性
vertexlist(g) 获取所有顶点及引用
incidence(g, uid) 获取指定顶点的所有出边
weight_1 常量权重函数(适用于 BFS 式最短路径)

Dijkstra 算法(Dijkstra’s shortest path) 计算从法兰克福(Frankfürt)出发到所有其他城市的最短路径,并进一步找出“距离最远的城市”,以及“返回路径”的输出。下面是详细讲解

一、核心逻辑:按距离(公里)计算最短路径

auto weight = [&g](edge_reference_t<G> uv) { 
    return edge_value(g, uv);  // 返回边的距离(公里)
};

这是权重函数,告诉 Dijkstra 算法每条边的代价(即距离)。
然后:

std::vector<double> distance(size(vertices(g)));
std::vector<vertex_id_t<G>> predecessor(size(vertices(g)));
  • distance[uid]:从法兰克福到城市 uid 的最短距离(公里)
  • predecessor[uid]:最短路径上,uid 的前驱城市 id(用来还原路径)
    运行:
dijkstra_clrs(g, frankfurt_id, distance, predecessor, weight);

用法兰克福作为起点,计算所有最短路径。

二、输出最短路径(公里)

cout << "Shortest distance (km) from " << city(g, frankfurt_id) << endl;
for (vertex_id_t<G> uid = 0; uid < size(vertices(g)); ++uid)
    if (distance[uid] > 0)
        cout << "  --> " << city_id(g, uid) 
             << " - " << distance[uid] << "km" << endl;

输出示例:

Shortest distance (km) from Frankfürt
--> Mannheim [1] - 85km
--> Karlsruhe [2] - 165km
--> Augsburg [3] - 415km
--> Würzburg [4] - 217km
--> Nürnberg [5] - 320km
--> Kassel [6] - 173km
--> Erfurt [7] - 403km
--> München [8] - 487km
--> Stuttgart [9] - 503km

这表示:

  • 法兰克福 出发,
  • 到每个城市的最短路程(以公里计)都被算出来了。

三、找出“最远的城市”

vertex_id_t<G> farthest_id = frankfurt_id;
double farthest_dist = 0.0;
for (vertex_id_t<G> uid = 0; uid < size(vertices(g)); ++uid) {
    if (distance[uid] > farthest_dist) {
        farthest_dist = distance[uid];
        farthest_id = uid;
    }
}

这段代码遍历所有城市,找出:

  • distance[uid] 最大的城市 → 就是最远城市
    输出:
The farthest city from Frankfürt is Stuttgart at 503km

也就是:

离法兰克福最远的城市是 斯图加特(Stuttgart),距离 503 公里

四、输出最远路径的路线

cout << "The shortest path from " << city(g, farthest_id)
     << " to " << city(g, frankfurt_id) << " is: " << endl
     << "  ";
for (vertex_id_t<G> uid = farthest_id; uid != frankfurt_id; 
     uid = predecessor[uid]) {
    if (uid != farthest_id)
        cout << " -- ";
    cout << city_id(g, uid);
}
cout << " -- " << city_id(g, frankfurt_id) << endl;

这部分利用 predecessor 数组反向追踪最短路径
示例输出:

The shortest path from Stuttgart to Frankfürt is:
Stuttgart [9] -- Nürnberg [5] -- Würzburg [4] -- Frankfürt [0]

意思是:

从斯图加特回到法兰克福的最短路径经过:
斯图加特 → 纽伦堡 → 维尔茨堡 → 法兰克福

总结(理解)

步骤 含义
1⃣ 定义权重函数 边的权重 = 公里距离
2⃣ 运行 Dijkstra 从法兰克福出发,计算所有城市最短路径
3⃣ 输出结果 打印每个城市的最短距离(公里)
4⃣ 找最远城市 找出最大 distance 的城市(斯图加特)
5⃣ 还原路径 根据 predecessor 反向输出路径顺序
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <queue>
#include <limits>
#include <algorithm>
using namespace std;
// ============================
// 类型定义
// ============================
// 城市 ID 类型(用 int32_t 确保跨平台一致性)
using city_id_type = int32_t;
// 城市名称类型
using city_name_type = string;
// ============================
// 图的边结构(用于输入数据)
// ============================
// copyable_edge_t 表示一个“可复制的边”
// 其中包含源顶点 ID、目标顶点 ID 和权值(即距离)
template <typename VertexId, typename Value>
struct copyable_edge_t {
    VertexId source_id;
    VertexId target_id;
    Value value;
};
// ============================
// 路径结构(用于邻接表)
// ============================
struct route {
    city_id_type target_id = 0;  // 目标城市 ID
    double distance = 0.0;       // 边的权重(单位:公里)
};
// ============================
// 邻接表定义
// ============================
using AdjList = vector<list<route>>;  // 每个城市对应一条边的链表
// ============================
// 图适配器(rr_adaptor)
// ============================
// 封装邻接表和城市名列表,实现对图的访问接口
// 支持操作:获取邻接表、按索引访问边列表、获取名称等
// ============================
template <typename AdjListType, typename NamesType>
class rr_adaptor {
private:
    AdjListType adj_list;    // 邻接表(每个节点存储所有出边)
    const NamesType& names;  // 引用城市名称数组(避免复制)
public:
    // 构造函数:从城市名和边表初始化图
    rr_adaptor(const NamesType& city_names,
               const vector<copyable_edge_t<city_id_type, double>>& edges)
        : names(city_names) {
        adj_list.resize(city_names.size());
        for (const auto& edge : edges) {
            route r;
            r.target_id = edge.target_id;
            r.distance = edge.value;
            adj_list[edge.source_id].push_back(r);
        }
    }
    size_t size() const { return adj_list.size(); }  // 顶点数量
    const AdjListType& get_adj_list() const { return adj_list; }
    const NamesType& get_names() const { return names; }
    // 下标访问符:访问指定城市的边表
    list<route>& operator[](size_t idx) { return adj_list[idx]; }
    const list<route>& operator[](size_t idx) const { return adj_list[idx]; }
};
// 定义图类型别名
using G = rr_adaptor<AdjList, vector<city_name_type>>;
// ============================
// 一些辅助类型定义与函数
// ============================
template <typename Graph>
using vertex_id_t = city_id_type;  // 顶点 ID 类型简化
template <typename Graph>
using edge_reference_t = const route&;  // 边引用类型简化
// 根据城市 ID 获取城市名
string city(const G& g, city_id_type id) { return g.get_names()[id]; }
// 获取带 ID 的城市信息字符串,例如 “Frankfurt [0]”
string city_id(const G& g, city_id_type id) {
    return g.get_names()[id] + " [" + to_string(id) + "]";
}
// 获取边的权重值(距离)
double edge_value(const G& g, const route& r) { return r.distance; }
// ============================
// Dijkstra 算法实现(CLRS 版本)
// ============================
template <typename Graph, typename Distance, typename Predecessor, typename WeightFunc>
void dijkstra_clrs(Graph& g, vertex_id_t<Graph> seed, Distance& distance, Predecessor& predecessor,
                   WeightFunc weight) {
    const auto INF = numeric_limits<typename Distance::value_type>::max();
    // 初始化距离数组与前驱节点
    fill(distance.begin(), distance.end(), INF);
    fill(predecessor.begin(), predecessor.end(), -1);
    distance[seed] = 0;
    // 使用最小堆(优先队列)按最短距离排序
    using pii = pair<typename Distance::value_type, vertex_id_t<Graph>>;
    priority_queue<pii, vector<pii>, greater<pii>> pq;
    pq.push({0, seed});
    while (!pq.empty()) {
        auto [dist, u] = pq.top();
        pq.pop();
        // 若弹出的节点已不是最短路径(过期),则跳过
        if (dist > distance[u]) continue;
        // 遍历所有相邻边
        for (const auto& edge : g[u]) {
            vertex_id_t<Graph> v = edge.target_id;
            auto w = weight(edge);  // 获取边的权值
            // 松弛操作(Relaxation)
            if (distance[u] + w < distance[v]) {
                distance[v] = distance[u] + w;
                predecessor[v] = u;
                pq.push({distance[v], v});
            }
        }
    }
}
// ============================
// 主程序入口
// ============================
int main() {
    // ---------- 定义城市 ----------
    vector<city_name_type> city_names = {"Frankfurt", "Mannheim", "Karlsruhe", "Augsburg",
                                         "Würzburg",  "Nürnberg", "Kassel",    "Erfurt",
                                         "München",   "Stuttgart"};
    // ---------- 定义城市间的道路(边表) ----------
    using route_data = copyable_edge_t<city_id_type, double>;
    vector<route_data> routes_doubled = {
        {0, 1, 85.0},  {0, 4, 217.0}, {0, 6, 173.0}, {1, 0, 85.0},  {1, 2, 80.0},  {2, 1, 80.0},
        {2, 3, 250.0}, {3, 2, 250.0}, {3, 8, 84.0},  {4, 0, 217.0}, {4, 5, 103.0}, {4, 7, 186.0},
        {5, 4, 103.0}, {5, 8, 167.0}, {5, 9, 183.0}, {6, 0, 173.0}, {6, 8, 502.0}, {7, 4, 186.0},
        {8, 3, 84.0},  {8, 5, 167.0}, {8, 6, 502.0}, {9, 5, 183.0}};
    // ---------- 创建图 ----------
    G g(city_names, routes_doubled);
    city_id_type frankfurt_id = 0;
    // ---------- 遍历所有城市与出边 ----------
    cout << "=== Traverse the vertices & outgoing edges ===" << endl;
    for (size_t uid = 0; uid < g.size(); ++uid) {
        cout << city_id(g, uid) << endl;
        for (const auto& edge : g[uid]) {
            cout << "   --> " << city_id(g, edge.target_id) << endl;
        }
    }
    cout << endl;
    // ---------- 最短路径(按“边数”计) ----------
    cout << "=== Shortest Paths - Segments ===" << endl;
    auto weight_1 = [](edge_reference_t<G> uv) -> int { return 1; };  // 每条边权重为 1
    vector<int> distance_segments(g.size());
    vector<vertex_id_t<G>> predecessor_segments(g.size());
    dijkstra_clrs(g, frankfurt_id, distance_segments, predecessor_segments, weight_1);
    cout << "Shortest distance (segments) from " << city(g, frankfurt_id) << endl;
    for (vertex_id_t<G> uid = 0; uid < g.size(); ++uid) {
        if (distance_segments[uid] > 0) {
            cout << "  --> " << city_id(g, uid) << " - " << distance_segments[uid] << " segments"
                 << endl;
        }
    }
    cout << endl;
    // ---------- 最短路径(按“公里”计) ----------
    cout << "=== Shortest Paths - Kilometers ===" << endl;
    auto weight = [&g](edge_reference_t<G> uv) { return edge_value(g, uv); };
    vector<double> distance(g.size());
    vector<vertex_id_t<G>> predecessor(g.size());
    dijkstra_clrs(g, frankfurt_id, distance, predecessor, weight);
    cout << "Shortest distance (km) from " << city(g, frankfurt_id) << endl;
    for (vertex_id_t<G> uid = 0; uid < g.size(); ++uid) {
        if (distance[uid] > 0) {
            cout << "  --> " << city_id(g, uid) << " - " << distance[uid] << "km" << endl;
        }
    }
    cout << endl;
    // ---------- 查找最远城市 ----------
    cout << "=== Farthest City ===" << endl;
    vertex_id_t<G> farthest_id = frankfurt_id;
    double farthest_dist = 0.0;
    for (vertex_id_t<G> uid = 0; uid < g.size(); ++uid) {
        if (distance[uid] > farthest_dist) {
            farthest_dist = distance[uid];
            farthest_id = uid;
        }
    }
    cout << "The farthest city from " << city(g, frankfurt_id) << " is " << city(g, farthest_id)
         << " at " << distance[farthest_id] << "km" << endl;
    // ---------- 输出从最远城市回到法兰克福的最短路径 ----------
    cout << "The shortest path from " << city(g, farthest_id) << " to " << city(g, frankfurt_id)
         << " is: " << endl
         << "  ";
    // 沿前驱节点回溯路径
    for (vertex_id_t<G> uid = farthest_id; uid != frankfurt_id; uid = predecessor[uid]) {
        if (uid != farthest_id) cout << " -- ";
        cout << city_id(g, uid);
    }
    cout << " -- " << city_id(g, frankfurt_id) << endl;
    return 0;
}

非常好 你现在这份代码是一个完整的、面向对象封装的 Dijkstra 最短路径算法实现,并且使用了模板与泛型编程的技巧。下面我帮你逐层讲解这份程序的逻辑理解,让你能完全吃透它的结构与设计思想。

一、整体功能概览

这个程序模拟了一个“城市与道路”的图结构,并执行以下操作:

  1. 定义了若干城市(顶点);
  2. 定义了它们之间的道路(边,带距离);
  3. 构造了一个邻接表表示的图;
  4. 打印出图中所有城市及出边;
  5. 使用 Dijkstra 算法计算:
    • 最短路径(按边数)
    • 最短路径(按公里数)
  6. 找出距离法兰克福(Frankfurt)最远的城市;
  7. 打印该最远城市到法兰克福的最短路径。

二、主要数据结构

1⃣ copyable_edge_t

template <typename VertexId, typename Value>
struct copyable_edge_t {
    VertexId source_id;  // 源城市 ID
    VertexId target_id;  // 目标城市 ID
    Value value;         // 边的权重(例如距离)
};

这相当于是输入阶段的“边表”(edge list)。
每条边表示一条从城市 A 到城市 B 的道路。

2⃣ route

struct route {
    city_id_type target_id = 0;
    double distance = 0.0;
};

这是邻接表中的“边记录”类型。
每个城市的出边链表中保存若干 route,记录“我能到哪些城市”以及“距离多远”。

3⃣ AdjList

using AdjList = vector<list<route>>;

表示邻接表结构:

  • 每个城市对应一个 list<route>
  • 整个图是一个 vector,每个下标对应城市 ID;
  • 例如:adj_list[0] 是 Frankfurt 的出边链表。

4⃣ rr_adaptor

template <typename AdjListType, typename NamesType>
class rr_adaptor {
    AdjListType adj_list;
    const NamesType& names;
};

rr_adaptor 就是“图适配器”:
它把“输入的边表 + 城市名”转换为一个可遍历的邻接表图对象。
作用:

  • 在构造时,根据 edges 构建邻接表;
  • 提供接口让你能:
    • 获取邻接表;
    • 获取城市名;
    • 通过 operator[] 访问某城市的所有出边。
      例如:
G g(city_names, routes_doubled);
for (auto& e : g[0]) {  // 遍历 Frankfurt 的所有出边
    cout << e.target_id << " " << e.distance;
}

三、辅助函数理解

函数名 功能
city(g, id) 返回城市名称,如 "Frankfurt"
city_id(g, id) 返回 "Frankfurt [0]" 带编号的名称
edge_value(g, r) 返回边的距离值
这些都是让输出更易读的工具函数。

四、Dijkstra 算法逻辑

模板函数:

template <typename Graph, typename Distance, typename Predecessor, typename WeightFunc>
void dijkstra_clrs(Graph& g, vertex_id_t<Graph> seed, Distance& distance, Predecessor& predecessor,
                   WeightFunc weight)

含义:

  • Graph:图类型;
  • seed:起点(source);
  • distance:保存起点到每个点的最短距离;
  • predecessor:保存每个点的“上一个节点”;
  • weight:计算边权值的函数(支持不同度量方式)。

算法步骤:

  1. 初始化
    fill(distance.begin(), distance.end(), INF);
    distance[seed] = 0;
    predecessor[seed] = -1;
    
  2. 优先队列(小根堆)
    priority_queue<pair<double,int>, vector<...>, greater<...>> pq;
    pq.push({0, seed});
    
    队列中保存 (当前距离, 顶点ID)
  3. 主循环
    • 弹出当前距离最小的顶点;
    • 对它的每条出边 (u,v) 执行“松弛”:
      if (distance[u] + w < distance[v]) {
          distance[v] = distance[u] + w;
          predecessor[v] = u;
          pq.push({distance[v], v});
      }
      
    • 直到队列为空。

🌏 五、主函数逻辑

1⃣ 定义城市

vector<string> city_names = {"Frankfurt", "Mannheim", ...};

2⃣ 定义双向道路

vector<route_data> routes_doubled = {
    {0, 1, 85.0}, {1, 0, 85.0},  // Frankfurt ↔ Mannheim
    ...
};

3⃣ 构造图

G g(city_names, routes_doubled);

4⃣ 打印城市和出边

输出:

Frankfurt [0]
   --> Mannheim [1]
   --> Würzburg [4]
   --> Kassel [6]
...

5⃣ 按“段数”计算最短路径

auto weight_1 = [](auto) { return 1; };  // 每条边权值为1
dijkstra_clrs(g, frankfurt_id, distance_segments, predecessor_segments, weight_1);

意思是只看“经过几条边”,不管距离。

6⃣ 按“公里”计算最短路径

auto weight = [&g](edge_reference_t<G> uv) { return edge_value(g, uv); };
dijkstra_clrs(g, frankfurt_id, distance, predecessor, weight);

这才是真正的 Dijkstra 算法,按距离最短计算。

7⃣ 查找最远城市

for (uid in all vertices)
    if (distance[uid] > farthest_dist)
        farthest_id = uid;

找到最远的城市(从 Frankfurt 出发最远的节点)。

8⃣ 回溯路径

for (uid = farthest_id; uid != frankfurt_id; uid = predecessor[uid])
    输出城市路径

通过 predecessor 反向追踪路径。

六、程序输出总结

输出大致如下:

=== Traverse the vertices & outgoing edges ===
Frankfurt [0]
   --> Mannheim [1]
   --> Würzburg [4]
   --> Kassel [6]
...
=== Shortest Paths - Segments ===
  --> Mannheim [1] - 1 segments
  --> Karlsruhe [2] - 2 segments
  --> München [8] - 3 segments
...
=== Shortest Paths - Kilometers ===
  --> Mannheim [1] - 85 km
  --> Karlsruhe [2] - 165 km
  --> München [8] - 487 km
...
=== Farthest City ===
The farthest city from Frankfurt is München at 487 km
The shortest path from München to Frankfurt is:
  München [8] -- Nürnberg [5] -- Würzburg [4] -- Frankfurt [0]

七、总结:代码设计思想

模块 思想
数据结构 使用模板实现灵活图结构(可复用)
算法 经典 Dijkstra(优先队列 + 松弛)
权重策略 支持“按边数”或“按距离”自定义
输出 清晰显示最短路径和路径构造
结构设计 解耦“图定义”与“算法逻辑”

Dijkstra 最短路径接口设计与相关概念,并顺带解释其他算法的分类与候选项。内容较抽象,我会逐步拆解,尽量用理解方便掌握。

1⃣ Dijkstra Shortest Paths 接口

你提供的接口是一个 泛型模板函数

template <
    adjacency_list G,
    ranges::random_access_range Distance,
    ranges::random_access_range Predecessor,
    class WF = std::function<ranges::range_value_t<Distance>(edge_reference_t<G>)>
>
requires ranges::random_access_range<vertex_range_t<G>> &&
         integral<vertex_id_t<G>> &&
         is_arithmetic_v<ranges::range_value_t<Distance>> &&
         convertible_to<vertex_id_t<G>, ranges::range_value_t<Predecessor>> &&
         edge_weight_function<G, WF>
void dijkstra_clrs(
    G&& g,                  // 图对象
    vertex_id_t<G> seed,    // 起始顶点ID
    Distance& distance,      // 输出:从 seed 到各顶点的最短距离
    Predecessor& predecessor,// 输出:前驱顶点 ID,用于构建最短路径
    WF weight = [](edge_reference_t<G> uv) { return ranges::range_value_t<Distance>(1); }
);

解析:

  1. 模板参数
    • G:图类型,必须满足 adjacency_list 概念;
    • Distance:存储距离的容器,要求随机访问;
    • Predecessor:存储前驱节点的容器,要求随机访问;
    • WF:边权重函数,默认每条边权值为 1
  2. 约束条件 (requires)
    • 顶点集合可以随机访问;
    • 顶点 ID 是整数;
    • 距离类型是算术类型(int、float、double 等);
    • 前驱数组可以存储顶点 ID;
    • 权重函数返回值是数值类型。
  3. 函数参数
    • g:图对象;
    • seed:起点顶点 ID;
    • distance:输出数组,记录从 seed 到每个顶点的距离;
    • predecessor:输出数组,记录最短路径上每个顶点的前驱;
    • weight:获取边权重的函数。

核心思想:这是 CLRS 风格的 Dijkstra 泛型版本,可以用于任意满足概念的图结构,而不仅限于邻接表或 vector 形式。

2⃣ 边权函数 (edge_weight_function)

template <class G, class F>
concept edge_weight_function = is_arithmetic_v<invoke_result_t<F, edge_reference_t<G>>>;

理解:

  • 边权函数是一个 callable(可调用对象);
  • 输入是边引用,输出是一个数值类型;
  • Dijkstra 算法会使用这个函数来获取每条边的权重;
  • 默认是 weight(uv) -> 1,即每条边权值为 1(用于“段数最短”计算)。

3⃣ 顶点相关概念 (vertex_range)

template <class G>
using vertex_range_t = decltype(vertices(declval<G&&>()));
  • vertices(g) 返回图的顶点集合;
  • 顶点类型可以遍历(range)且可以获取顶点 ID;
  • vertex_iterator_tvertex_reference_t 分别是迭代器类型和引用类型;
  • 约束:顶点集合必须可以正向遍历、已知大小。

4⃣ 边相关概念 (targeted_edge)

template <class G>
using vertex_edge_range_t = decltype(edges(declval<G&&>(), declval<vertex_reference_t<G>>()));
  • 每个顶点可以访问它的出边集合;
  • 每条边可以获取目标顶点 ID 或者目标顶点对象;
  • 对于邻接表图来说,就是遍历 adj_list[u]

5⃣ 邻接表概念 (adjacency_list)

template <class G>
concept adjacency_list = vertex_range<G> &&
                         targeted_edge<G, edge_t<G>> &&
                         requires(G&& g, vertex_reference_t<G> u, vertex_id_t<G> uid) {
                             { edges(g, u) } -> ranges::forward_range;
                             { edges(g, uid) } -> ranges::forward_range;
                         };

理解:

  • 图必须满足:
    1. 顶点集合可遍历;
    2. 每个顶点的出边可遍历;
    3. 可以通过顶点对象或顶点 ID 获取出边集合。

6⃣ 源边邻接表概念 (sourced_adjacency_list)

  • source_id(g, uv)source(g, uv) 可以获取边的源顶点;
  • edge_id(g, uv) 可获取边编号;
  • 增强版邻接表,便于在算法中获取边的来源信息。

7⃣ 已确认与候选算法列表

已确认算法(Confirmed)

  • Dijkstra 最短路径
  • Bellman-Ford 最短路径
  • Connected Components(连通分量)
  • Strongly Connected Components(强连通分量)
  • Biconnected Components(双连通分量)
  • Articulation Points(割点)
  • Minimum Spanning Tree(最小生成树)

候选算法(Candidates)

  • Page Rank(网页排名)
  • Betweenness Centrality(中介中心性)
  • Triangle Count(三角形计数)
  • Subgraph Isomorphism(子图同构)
  • Kruskal 最小生成树
  • Prim 最小生成树
  • Louvain(社区发现)
  • Label Propagation(社区发现)

理解:

  • Confirmed 表示已经实现或设计确认的算法;
  • Candidates 表示可能在后续加入或作为实验实现的算法。

总结

  • 模板与概念化接口可以支持任意类型的邻接表图,灵活调用 Dijkstra。
  • 顶点、边、权重函数、前驱数组、距离数组都通过泛型和约束实现类型安全。
  • 默认权重为 1 支持“边数最短”模式。
  • 概念化设计与 CLRS 算法结合,保证算法通用性。
  • 已实现算法主要是图的基本分析和最短路径,候选算法偏向图分析和图挖掘。
#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <queue>
#include <limits>
#include <algorithm>
#include <concepts>
#include <ranges>
#include <functional>
#include <type_traits>
using namespace std;
namespace ranges = std::ranges;
// ==================== 类型特征和概念 ====================
// 为图算法提供泛型接口所需的类型提取
// 顶点相关类型
template <typename G>
using vertex_range_t = decltype(vertices(declval<G&&>())); // 获取图的顶点范围类型
template <typename G>
using vertex_iterator_t = ranges::iterator_t<vertex_range_t<G&&>>; // 顶点迭代器类型
template <typename G>
using vertex_reference_t = ranges::range_reference_t<vertex_range_t<G>>; // 顶点引用类型
template <typename G>
using vertex_id_t = decltype(vertex_id(declval<G&&>(), declval<vertex_iterator_t<G>>())); // 顶点 ID 类型
// 边相关类型
template <typename G>
using vertex_edge_range_t = decltype(edges(declval<G&&>(), declval<vertex_reference_t<G>>())); // 顶点出边范围类型
template <typename G>
using edge_reference_t = ranges::range_reference_t<vertex_edge_range_t<G>>; // 边引用类型
template <typename G>
using edge_t = ranges::range_value_t<vertex_edge_range_t<G>>; // 边值类型
// 边权函数概念:返回数值类型
template <typename G, typename F>
concept edge_weight_function = is_arithmetic_v<invoke_result_t<F, edge_reference_t<G>>>;
// 顶点范围概念
template <typename G>
concept vertex_range =
    ranges::forward_range<vertex_range_t<G>> && ranges::sized_range<vertex_range_t<G>> &&
    requires(G&& g, vertex_iterator_t<G> ui) {
        { vertices(g) } -> ranges::forward_range;
        vertex_id(g, ui);
    };
// 目标边概念:可以获取目标顶点 ID 和引用
template <typename G>
concept targeted_edge = requires(G&& g, edge_reference_t<G> uv) {
    target_id(g, uv);
    target(g, uv);
};
// 邻接表概念
template <typename G>
concept adjacency_list = vertex_range<G> && targeted_edge<G> &&
                         requires(G&& g, vertex_reference_t<G> u, vertex_id_t<G> uid) {
                             { edges(g, u) } -> ranges::forward_range;
                             { edges(g, uid) } -> ranges::forward_range;
                         };
// 源边概念:可以获取源顶点 ID 和引用
template <typename G, typename E>
concept sourced_edge = requires(G&& g, E& uv) {
    source_id(g, uv);
    source(g, uv);
};
// 源邻接表概念
template <typename G>
concept sourced_adjacency_list = adjacency_list<G> && sourced_edge<G, edge_t<G>> &&
                                 requires(G&& g, edge_reference_t<G> uv) { edge_id(g, uv); };
// ==================== 图的实现 ====================
using city_id_type = int32_t; // 顶点 ID 类型
using city_name_type = string; // 顶点名称类型
// 边结构体
struct route {
    city_id_type target_id = 0; // 目标顶点 ID
    double distance = 0.0;      // 距离(权值)
};
// 顶点结构体
struct vertex {
    city_id_type id;       // 顶点 ID
    list<route> edges;    // 出边列表(邻接表)
};
// 图类
class Graph {
private:
    vector<vertex> vertices_;       // 顶点集合
    vector<city_name_type> city_names_; // 顶点名称
public:
    // 构造函数:传入城市名和边列表
    Graph(const vector<city_name_type>& names,
          const vector<tuple<city_id_type, city_id_type, double>>& edge_list)
        : city_names_(names) {
        vertices_.resize(names.size());
        // 初始化顶点 ID
        for (size_t i = 0; i < names.size(); ++i) {
            vertices_[i].id = i;
        }
        // 初始化边
        for (const auto& [src, tgt, dist] : edge_list) {
            route r{tgt, dist};
            vertices_[src].edges.push_back(r);
        }
    }
    // 获取顶点集合(可修改/只读)
    auto& get_vertices() { return vertices_; }
    const auto& get_vertices() const { return vertices_; }
    // 获取城市名称集合
    const auto& get_city_names() const { return city_names_; }
    // 支持索引访问顶点
    vertex& operator[](size_t idx) { return vertices_[idx]; }
    const vertex& operator[](size_t idx) const { return vertices_[idx]; }
};
// ==================== 图接口函数 ====================
// 返回顶点范围
auto vertices(Graph& g) {
    return ranges::subrange(g.get_vertices().begin(), g.get_vertices().end());
}
auto vertices(const Graph& g) {
    return ranges::subrange(g.get_vertices().begin(), g.get_vertices().end());
}
// 返回迭代器指向顶点的 ID
city_id_type vertex_id(const Graph& g, auto it) { return it->id; }
// 返回某顶点的出边范围
auto edges(Graph& g, vertex& v) { return ranges::subrange(v.edges.begin(), v.edges.end()); }
auto edges(const Graph& g, const vertex& v) {
    return ranges::subrange(v.edges.begin(), v.edges.end());
}
auto edges(Graph& g, city_id_type uid) { return edges(g, g[uid]); }
auto edges(const Graph& g, city_id_type uid) { return edges(g, g[uid]); }
// 返回边的目标顶点 ID
city_id_type target_id(const Graph& g, const route& e) { return e.target_id; }
// 返回边的目标顶点引用
const vertex& target(const Graph& g, const route& e) { return g[e.target_id]; }
// 辅助函数:根据 ID 获取城市名称
string city(const Graph& g, city_id_type id) { return g.get_city_names()[id]; }
string city_id_str(const Graph& g, city_id_type id) {
    return city(g, id) + " [" + to_string(id) + "]";
}
// 获取边权值
double edge_value(const Graph& g, const route& r) { return r.distance; }
// ==================== Dijkstra 算法 ====================
template <adjacency_list G, ranges::random_access_range Distance,
          ranges::random_access_range Predecessor,
          class WF = function<ranges::range_value_t<Distance>(edge_reference_t<G>)>>
    requires ranges::random_access_range<vertex_range_t<G>> && integral<vertex_id_t<G>> &&
             is_arithmetic_v<ranges::range_value_t<Distance>> &&
             convertible_to<vertex_id_t<G>, ranges::range_value_t<Predecessor>> &&
             edge_weight_function<G, WF>
void dijkstra_clrs(
    G&& g,                     // 图对象
    vertex_id_t<G> seed,       // 起点 ID
    Distance& distance,        // 输出:从起点到各顶点距离
    Predecessor& predecessor,  // 输出:最短路径前驱顶点
    WF weight = [](edge_reference_t<G> uv) { return ranges::range_value_t<Distance>(1); }) {
    using dist_type = ranges::range_value_t<Distance>;
    const auto INF = numeric_limits<dist_type>::max(); // 无穷大初始化
    // 初始化距离和前驱
    ranges::fill(distance, INF);
    ranges::fill(predecessor, -1);
    distance[seed] = 0;
    // 优先队列存储 (distance, vertex_id),用于贪心选择
    using pii = pair<dist_type, vertex_id_t<G>>;
    priority_queue<pii, vector<pii>, greater<pii>> pq;
    pq.push({0, seed});
    // 主循环
    while (!pq.empty()) {
        auto [dist, u] = pq.top();
        pq.pop();
        if (dist > distance[u]) continue; // 已经有更短路径,跳过
        // 松弛邻居边
        for (const auto& edge : edges(g, u)) {
            vertex_id_t<G> v = target_id(g, edge);
            auto w = weight(edge);
            if (distance[u] != INF && distance[u] + w < distance[v]) {
                distance[v] = distance[u] + w;
                predecessor[v] = u;
                pq.push({distance[v], v});
            }
        }
    }
}
// ==================== 主程序 ====================
int main() {
    // 城市名称数据
    vector<city_name_type> city_names = {"Frankfurt", "Mannheim", "Karlsruhe", "Augsburg",
                                         "Würzburg",  "Nürnberg", "Kassel",    "Erfurt",
                                         "München",   "Stuttgart"};
    // 边数据(双向)
    vector<tuple<city_id_type, city_id_type, double>> routes = {
        {0, 1, 85.0},  {0, 4, 217.0}, {0, 6, 173.0}, {1, 0, 85.0},  {1, 2, 80.0},  {2, 1, 80.0},
        {2, 3, 250.0}, {3, 2, 250.0}, {3, 8, 84.0},  {4, 0, 217.0}, {4, 5, 103.0}, {4, 7, 186.0},
        {5, 4, 103.0}, {5, 8, 167.0}, {5, 9, 183.0}, {6, 0, 173.0}, {6, 8, 502.0}, {7, 4, 186.0},
        {8, 3, 84.0},  {8, 5, 167.0}, {8, 6, 502.0}, {9, 5, 183.0}};
    // 构建图
    Graph g(city_names, routes);
    city_id_type frankfurt_id = 0;
    // 遍历顶点及其出边
    cout << "=== Traverse the vertices & outgoing edges ===" << endl;
    for (const auto& v : vertices(g)) {
        cout << city_id_str(g, v.id) << endl;
        for (const auto& e : edges(g, v)) {
            cout << "   --> " << city_id_str(g, e.target_id) << endl;
        }
    }
    cout << endl;
    // ================== 最短路径 - 按段数 (unweighted) ==================
    cout << "=== Shortest Paths - Segments ===" << endl;
    auto weight_1 = [](const route& uv) -> int { return 1; }; // 每条边权值为1
    vector<int> distance_seg(city_names.size());
    vector<city_id_type> predecessor_seg(city_names.size());
    dijkstra_clrs(g, frankfurt_id, distance_seg, predecessor_seg, weight_1);
    cout << "Shortest distance (segments) from " << city(g, frankfurt_id) << endl;
    for (city_id_type uid = 0; uid < city_names.size(); ++uid) {
        if (distance_seg[uid] > 0) {
            cout << "  --> " << city_id_str(g, uid) << " - " << distance_seg[uid] << " segments"
                 << endl;
        }
    }
    cout << endl;
    // ================== 最短路径 - 按公里数 (weighted) ==================
    cout << "=== Shortest Paths - Kilometers ===" << endl;
    auto weight_km = [&g](const route& uv) { return edge_value(g, uv); };
    vector<double> distance(city_names.size());
    vector<city_id_type> predecessor(city_names.size());
    dijkstra_clrs(g, frankfurt_id, distance, predecessor, weight_km);
    cout << "Shortest distance (km) from " << city(g, frankfurt_id) << endl;
    for (city_id_type uid = 0; uid < city_names.size(); ++uid) {
        if (distance[uid] > 0) {
            cout << "  --> " << city_id_str(g, uid) << " - " << distance[uid] << "km" << endl;
        }
    }
    cout << endl;
    // ================== 找最远城市 ==================
    cout << "=== Farthest City ===" << endl;
    city_id_type farthest_id = frankfurt_id;
    double farthest_dist = 0.0;
    for (city_id_type uid = 0; uid < city_names.size(); ++uid) {
        if (distance[uid] < numeric_limits<double>::max() && distance[uid] > farthest_dist) {
            farthest_dist = distance[uid];
            farthest_id = uid;
        }
    }
    cout << "The farthest city from " << city(g, frankfurt_id) << " is " << city(g, farthest_id)
         << " at " << distance[farthest_id] << "km" << endl;
    // ================== 输出最短路径 ==================
    cout << "The shortest path from " << city(g, farthest_id) << " to " << city(g, frankfurt_id)
         << " is:" << endl
         << "  ";
    vector<city_id_type> path;
    for (city_id_type uid = farthest_id; uid != frankfurt_id; uid = predecessor[uid]) {
        path.push_back(uid);
    }
    path.push_back(frankfurt_id);
    for (size_t i = 0; i < path.size(); ++i) {
        if (i > 0) cout << " -- ";
        cout << city_id_str(g, path[i]);
    }
    cout << endl;
    return 0;
}

VIEWS(视图接口) 和相关算法概念用逐步理解整理一下。这里主要是现代 C++ 图算法库的 “迭代器式接口”视图封装 设计思想。

1⃣ 顶点视图 vertexlistvertex_view

用法:

G g = ...;
for (auto&& uu : vertexlist(g)) {
    vertex_id_t<G> uid = uu.id;   // 顶点 ID
    vertex_reference_t<G> u = uu.vertex; // 顶点引用
    // 做一些操作
}
// C++17/20结构化绑定写法
for (auto&& [uid, u] : vertexlist(g)) {
    // 做一些操作
}

视图结构:

template <class VId, class V, class VV>
struct vertex_view {
    VId id;   // 顶点 ID
    V vertex; // 顶点引用
    VV value; // 可选顶点值
};
  • 可以不带值:
vertex_view<VId, V, void> { id, vertex }
  • 可以只存 ID:
vertex_view<VId, void, void> { id }
  • 可以带顶点值:
vertex_view<VId, V, VV> { id, vertex, value }

支持的重载:

  • vertexlist(g) → 返回所有顶点的 vertex_view<VId,V,void>
  • vertexlist(g, vvf) → 通过 vvf(u) 获取顶点值,返回 vertex_view<VId,V,VV>

理解vertexlist 提供遍历顶点的统一接口,支持额外顶点值访问(value function)。

2⃣ 出边视图 incidenceedge_view

用法:

vertex_reference_t<G> u = ...;
for (auto&& [vid, uv] : incidence(g, uid)) {
    // vid: 目标顶点 ID
    // uv: 边引用
}
  • 可以加边权值函数:
auto edge_fn = [&g](edge_reference_t<G> uv) { return edge_value(g, uv); };
for (auto&& [vid, uv, val] : incidence(g, uid, edge_fn)) {
    // val: 边权值
}

视图结构:

template <class VId, bool Sourced, class E, class EV>
struct edge_view {
    VId source_id; // 源顶点(Sourced = true)
    VId target_id; // 目标顶点
    E edge;        // 边引用
    EV value;      // 边值(可选)
};
  • 不带值的情况:
edge_view<VId, false, E, void> { target_id, edge }

理解incidence 是顶点的出边迭代器,统一提供目标 ID、边引用、可选权值。

3⃣ 邻居视图 neighborsneighbor_view

用法:

for (auto&& [vid, v] : neighbors(g, uid)) {
    // vid: 邻居顶点 ID
    // v: 顶点引用
}
// 带顶点值
auto vvf = [&g](vertex_reference_t<G> v) { return vertex_value(g, v); };
for (auto&& [vid, v, val] : neighbors(g, uid, vvf)) {
    // val: 顶点值
}

视图结构:

template <class VId, bool Sourced, class V, class VV>
struct neighbor_view {
    VId source_id; // 源顶点
    VId target_id; // 邻居顶点
    V target;      // 顶点引用
    VV value;      // 可选值
};

理解neighbors 是出边的简化视图,只关心邻居顶点,适合访问顶点属性而不必关注边对象。

4⃣ 边集合视图 edgelist

用法:

for (auto&& [uid, vid, uv] : edgelist(g)) {
    // uid: 源顶点 ID
    // vid: 目标顶点 ID
    // uv: 边引用
}
// 带边值
auto evf = [&g](edge_reference_t<G2> uv) { return edge_value(g, uv); };
for (auto&& [uid, vid, uv, val] : edgelist(g, evf)) {
    // val: 边权值
}
  • edgelist 提供全图边的统一遍历接口。

5⃣ 深度优先搜索 DFS (depth_first_search)

  • 提供顶点和边遍历接口:
for(auto&& [vid,v] : vertices_depth_first_search(g,seed)) ...
for(auto&& [vid,uv] : edges_depth_first_search(g,seed)) ...
  • 可以带顶点值/边值函数:
for(auto&& [vid,v,val] : vertices_depth_first_search(g,seed,vvf)) ...
for(auto&& [vid,uv,val] : edges_depth_first_search(g,seed,evf)) ...
  • 支持 sourced_edges_depth_first_search 获取源顶点信息。

理解:DFS 提供按访问顺序遍历顶点和边的迭代器接口,和 vertex_view/edge_view 结合使用。

6⃣ 广度优先搜索 BFS (breadth_first_search)

  • 接口设计与 DFS 一样,只是访问顺序不同(层序访问)。
  • 顶点和边遍历都可用:
vertices_breadth_first_search(g, seed)
edges_breadth_first_search(g, seed)
sourced_edges_breadth_first_search(g, seed)
  • 同样可选顶点值/边值。

7⃣ 拓扑排序 Topological Sort

  • 顶点迭代器:
vertices_topological_sort(g, seed)
  • 边迭代器:
edges_topological_sort(g, seed)
sourced_edges_topological_sort(g, seed)
  • 可带顶点值/边值函数。

理解:和 DFS/BFS 一样,提供 vertex_viewedge_view,但遍历顺序为拓扑序。

总结理解

  1. 统一视图设计
    • vertex_viewedge_viewneighbor_view 都是用 模板结构体封装顶点/边信息
    • 支持 ID、引用、可选值(通过函数计算);
    • 支持源边/目标边区分(Sourced 模板参数)。
  2. 遍历接口
    • vertexlist(g):顶点集合;
    • neighbors(g,u):邻居集合;
    • incidence(g,u):出边集合;
    • edgelist(g):全图边集合。
  3. 算法遍历
    • DFS、BFS、Topological Sort 都返回这些视图,统一访问顶点/边;
    • 支持结构化绑定 [uid, vertex, value],可简化算法开发。
  4. 可扩展性
    • 支持额外顶点值/边值函数;
    • 支持泛型图结构(不仅限 vector/list)。

总体思路:通过视图接口,把图的结构和属性解耦,统一访问顶点、邻居、边,方便泛型算法编写

#include <iostream>
#include <vector>
#include <list>
#include <string>
#include <queue>
#include <stack>
#include <limits>
#include <algorithm>
#include <functional>
#include <type_traits>
#include <unordered_set>
using namespace std;
// ==================== 类型定义 ====================
// 顶点 ID 类型
using city_id_type = int32_t;
// 顶点名称类型
using city_name_type = string;
// ==================== 边结构 ====================
// 表示城市间的路线
struct route {
    city_id_type target_id = 0;  // 目标城市 ID
    double distance = 0.0;       // 距离(单位 km)
};
// ==================== 顶点结构 ====================
// 表示图的顶点
struct vertex {
    city_id_type id;    // 顶点 ID
    string name;        // 顶点名称
    list<route> edges;  // 顶点的出边列表
};
// ==================== 视图结构 ====================
// 顶点视图(带可选值类型 VV)
template <class VId, class V, class VV>
struct vertex_view {
    VId id;    // 顶点 ID
    V vertex;  // 顶点对象
    VV value;  // 可选顶点值
};
// 仅顶点对象,不带 VV
template <class VId, class V>
struct vertex_view<VId, V, void> {
    VId id;
    V vertex;
};
// 仅顶点值,不带 V
template <class VId, class VV>
struct vertex_view<VId, void, VV> {
    VId id;
    VV value;
};
// 仅 ID
template <class VId>
struct vertex_view<VId, void, void> {
    VId id;
};
// 边视图(可带 source_id / 是否来源 Sourced /边对象 E /边值 EV)
template <class VId, bool Sourced, class E, class EV>
struct edge_view {
    VId source_id;  // 源顶点 ID
    VId target_id;  // 目标顶点 ID
    E edge;         // 边对象
    EV value;       // 边值
};
// Sourced 为 true 时
template <class VId, class E, class EV>
struct edge_view<VId, true, E, EV> {
    VId source_id;
    VId target_id;
    E edge;
    EV value;
};
// Sourced 为 true,但没有 EV
template <class VId, class E>
struct edge_view<VId, true, E, void> {
    VId source_id;
    VId target_id;
    E edge;
};
// Sourced 为 false
template <class VId, class E, class EV>
struct edge_view<VId, false, E, EV> {
    VId target_id;
    E edge;
    EV value;
};
template <class VId, class E>
struct edge_view<VId, false, E, void> {
    VId target_id;
    E edge;
};
// 邻居视图
template <class VId, bool Sourced, class V, class VV>
struct neighbor_view {
    VId source_id;
    VId target_id;
    V target;
    VV value;
};
// Sourced 为 false
template <class VId, class V, class VV>
struct neighbor_view<VId, false, V, VV> {
    VId target_id;
    V target;
    VV value;
};
template <class VId, class V>
struct neighbor_view<VId, false, V, void> {
    VId target_id;
    V target;
};
// ==================== 图类 ====================
class Graph {
private:
    vector<vertex> vertices_;  // 顶点数组
public:
    // 构造函数:通过顶点名列表和边列表初始化图
    Graph(const vector<city_name_type>& names,
          const vector<tuple<city_id_type, city_id_type, double>>& edge_list) {
        vertices_.resize(names.size());
        for (size_t i = 0; i < names.size(); ++i) {
            vertices_[i].id = i;  // 顶点 ID 从 0 开始
            vertices_[i].name = names[i];
        }
        // 初始化边
        for (const auto& [src, tgt, dist] : edge_list) {
            route r{tgt, dist};
            vertices_[src].edges.push_back(r);
        }
    }
    size_t size() const { return vertices_.size(); }
    auto& get_vertices() { return vertices_; }
    const auto& get_vertices() const { return vertices_; }
    vertex& operator[](size_t idx) { return vertices_[idx]; }
    const vertex& operator[](size_t idx) const { return vertices_[idx]; }
};
// ==================== 类型别名 ====================
template <typename G>
using vertex_id_t = city_id_type;
template <typename G>
using vertex_reference_t = vertex&;
template <typename G>
using edge_reference_t = const route&;
// ==================== 基本图接口 ====================
inline auto& vertices(Graph& g) { return g.get_vertices(); }
inline const auto& vertices(const Graph& g) { return g.get_vertices(); }
inline auto& edges(Graph& g, vertex& v) { return v.edges; }
inline const auto& edges(const Graph& g, const vertex& v) { return v.edges; }
inline auto& edges(Graph& g, city_id_type uid) { return g[uid].edges; }
inline const auto& edges(const Graph& g, city_id_type uid) { return g[uid].edges; }
inline city_id_type target_id(const Graph& g, const route& e) { return e.target_id; }
inline const vertex& target(const Graph& g, const route& e) { return g[e.target_id]; }
inline double edge_value(const Graph& g, const route& r) { return r.distance; }
inline string vertex_value(const Graph& g, const vertex& v) { return v.name; }
inline string city(const Graph& g, city_id_type id) { return g[id].name; }
inline string city_id_str(const Graph& g, city_id_type id) {
    return city(g, id) + " [" + to_string(id) + "]";  // 显示城市名和 ID
}
// ==================== 视图生成器 ====================
// vertexlist:遍历所有顶点
class vertexlist_range {
    Graph& g_;
public:
    vertexlist_range(Graph& g) : g_(g) {}
    struct iterator {
        Graph& g;
        size_t idx;
        iterator(Graph& graph, size_t i) : g(graph), idx(i) {}
        auto operator*() { return vertex_view<city_id_type, vertex&, void>{g[idx].id, g[idx]}; }
        iterator& operator++() {
            ++idx;
            return *this;
        }
        bool operator!=(const iterator& other) const { return idx != other.idx; }
    };
    iterator begin() { return iterator(g_, 0); }
    iterator end() { return iterator(g_, g_.size()); }
};
inline auto vertexlist(Graph& g) { return vertexlist_range(g); }
// incidence:顶点的出边视图
class incidence_range {
    Graph& g_;
    city_id_type uid_;
public:
    incidence_range(Graph& g, city_id_type uid) : g_(g), uid_(uid) {}
    struct iterator {
        Graph& g;
        city_id_type source;
        list<route>::iterator it;
        iterator(Graph& graph, city_id_type src, list<route>::iterator i)
            : g(graph), source(src), it(i) {}
        auto operator*() {
            return edge_view<city_id_type, false, const route&, void>{it->target_id, *it};
        }
        iterator& operator++() {
            ++it;
            return *this;
        }
        bool operator!=(const iterator& other) const { return it != other.it; }
    };
    iterator begin() { return iterator(g_, uid_, g_[uid_].edges.begin()); }
    iterator end() { return iterator(g_, uid_, g_[uid_].edges.end()); }
};
inline auto incidence(Graph& g, city_id_type uid) { return incidence_range(g, uid); }
// neighbors:顶点的邻居顶点视图
class neighbors_range {
    Graph& g_;
    city_id_type uid_;
public:
    neighbors_range(Graph& g, city_id_type uid) : g_(g), uid_(uid) {}
    struct iterator {
        Graph& g;
        list<route>::iterator it;
        iterator(Graph& graph, list<route>::iterator i) : g(graph), it(i) {}
        auto operator*() {
            return neighbor_view<city_id_type, false, vertex&, void>{it->target_id,
                                                                     g[it->target_id]};
        }
        iterator& operator++() {
            ++it;
            return *this;
        }
        bool operator!=(const iterator& other) const { return it != other.it; }
    };
    iterator begin() { return iterator(g_, g_[uid_].edges.begin()); }
    iterator end() { return iterator(g_, g_[uid_].edges.end()); }
};
inline auto neighbors(Graph& g, city_id_type uid) { return neighbors_range(g, uid); }
// edgelist:遍历所有边
class edgelist_range {
    Graph& g_;
public:
    edgelist_range(Graph& g) : g_(g) {}
    struct iterator {
        Graph& g;
        size_t vid;
        list<route>::iterator eit;
        iterator(Graph& graph, size_t v, list<route>::iterator e) : g(graph), vid(v), eit(e) {
            skip_empty();
        }
        void skip_empty() {
            while (vid < g.size() && eit == g[vid].edges.end()) {
                ++vid;
                if (vid < g.size()) eit = g[vid].edges.begin();
            }
        }
        auto operator*() {
            return edge_view<city_id_type, true, const route&, void>{static_cast<city_id_type>(vid),
                                                                     eit->target_id, *eit};
        }
        iterator& operator++() {
            ++eit;
            skip_empty();
            return *this;
        }
        bool operator!=(const iterator& other) const {
            return vid != other.vid || (vid < g.size() && eit != other.eit);
        }
    };
    iterator begin() {
        return iterator(g_, 0, g_.size() > 0 ? g_[0].edges.begin() : list<route>::iterator());
    }
    iterator end() { return iterator(g_, g_.size(), list<route>::iterator()); }
};
inline auto edgelist(Graph& g) { return edgelist_range(g); }
// ==================== 深度优先搜索 DFS ====================
class vertices_dfs_range {
    Graph& g_;
    city_id_type seed_;
public:
    vertices_dfs_range(Graph& g, city_id_type seed) : g_(g), seed_(seed) {}
    struct iterator {
        Graph& g;
        stack<city_id_type> stk;              // DFS 栈
        unordered_set<city_id_type> visited;  // 已访问顶点
        city_id_type current;
        bool done;
        iterator(Graph& graph, city_id_type start, bool is_end)
            : g(graph), current(start), done(is_end) {
            if (!done) {
                stk.push(start);
                advance();
            }
        }
        void advance() {
            while (!stk.empty()) {
                current = stk.top();
                stk.pop();
                if (visited.find(current) == visited.end()) {
                    visited.insert(current);
                    for (auto& e : g[current].edges) {
                        if (visited.find(e.target_id) == visited.end()) {
                            stk.push(e.target_id);
                        }
                    }
                    return;
                }
            }
            done = true;
        }
        auto operator*() { return vertex_view<city_id_type, vertex&, void>{current, g[current]}; }
        iterator& operator++() {
            advance();
            return *this;
        }
        bool operator!=(const iterator& other) const { return done != other.done; }
    };
    iterator begin() { return iterator(g_, seed_, false); }
    iterator end() { return iterator(g_, seed_, true); }
};
inline auto vertices_depth_first_search(Graph& g, city_id_type seed) {
    return vertices_dfs_range(g, seed);
}
// ==================== 广度优先搜索 BFS ====================
class vertices_bfs_range {
    Graph& g_;
    city_id_type seed_;
public:
    vertices_bfs_range(Graph& g, city_id_type seed) : g_(g), seed_(seed) {}
    struct iterator {
        Graph& g;
        queue<city_id_type> q;
        unordered_set<city_id_type> visited;
        city_id_type current;
        bool done;
        iterator(Graph& graph, city_id_type start, bool is_end)
            : g(graph), current(start), done(is_end) {
            if (!done) {
                q.push(start);
                visited.insert(start);
                advance();
            }
        }
        void advance() {
            if (q.empty()) {
                done = true;
                return;
            }
            current = q.front();
            q.pop();
            for (auto& e : g[current].edges) {
                if (visited.find(e.target_id) == visited.end()) {
                    visited.insert(e.target_id);
                    q.push(e.target_id);
                }
            }
        }
        auto operator*() { return vertex_view<city_id_type, vertex&, void>{current, g[current]}; }
        iterator& operator++() {
            advance();
            return *this;
        }
        bool operator!=(const iterator& other) const { return done != other.done; }
    };
    iterator begin() { return iterator(g_, seed_, false); }
    iterator end() { return iterator(g_, seed_, true); }
};
inline auto vertices_breadth_first_search(Graph& g, city_id_type seed) {
    return vertices_bfs_range(g, seed);
}
// ==================== Dijkstra 最短路径算法 ====================
template <typename G, typename Distance, typename Predecessor, typename WF>
void dijkstra_clrs(G& g, vertex_id_t<G> seed, Distance& distance, Predecessor& predecessor,
                   WF weight) {
    using dist_type = typename Distance::value_type;
    const auto INF = numeric_limits<dist_type>::max();
    fill(distance.begin(), distance.end(), INF);       // 初始化距离为无穷大
    fill(predecessor.begin(), predecessor.end(), -1);  // 初始化前驱顶点为 -1
    distance[seed] = 0;                                // 起点到自身距离为 0
    // 优先队列,用于选择当前距离最小的顶点
    using pii = pair<dist_type, vertex_id_t<G>>;
    priority_queue<pii, vector<pii>, greater<pii>> pq;
    pq.push({0, seed});
    while (!pq.empty()) {
        auto [dist, u] = pq.top();  // 取出队列中距离最小的顶点
        pq.pop();
        // 如果当前距离已经大于记录的最短距离,则跳过
        if (dist > distance[u]) continue;
        // 遍历 u 的所有邻居
        for (const auto& edge : edges(g, u)) {
            vertex_id_t<G> v = target_id(g, edge);  // 目标顶点
            auto w = weight(edge);                  // 边权值
            // 松弛操作
            if (distance[u] != INF && distance[u] + w < distance[v]) {
                distance[v] = distance[u] + w;  // 更新最短距离
                predecessor[v] = u;             // 更新前驱节点
                pq.push({distance[v], v});      // 将新距离压入队列
            }
        }
    }
}
// ==================== 主程序 ====================
int main() {
    // 顶点名称列表
    vector<city_name_type> city_names = {"Frankfurt", "Mannheim", "Karlsruhe", "Augsburg",
                                         "Würzburg",  "Nürnberg", "Kassel",    "Erfurt",
                                         "München",   "Stuttgart"};
    // 边列表:源城市 ID,目标城市 ID,距离(km)
    vector<tuple<city_id_type, city_id_type, double>> routes = {
        {0, 1, 85.0},  {0, 4, 217.0}, {0, 6, 173.0}, {1, 0, 85.0},  {1, 2, 80.0},  {2, 1, 80.0},
        {2, 3, 250.0}, {3, 2, 250.0}, {3, 8, 84.0},  {4, 0, 217.0}, {4, 5, 103.0}, {4, 7, 186.0},
        {5, 4, 103.0}, {5, 8, 167.0}, {5, 9, 183.0}, {6, 0, 173.0}, {6, 8, 502.0}, {7, 4, 186.0},
        {8, 3, 84.0},  {8, 5, 167.0}, {8, 6, 502.0}, {9, 5, 183.0}};
    // 创建图对象
    Graph g(city_names, routes);
    city_id_type frankfurt_id = 0;  // Frankfurt 的顶点 ID
    // ==================== 演示:vertexlist ====================
    cout << "=== vertexlist: 遍历所有顶点 ===" << endl;
    for (auto&& [uid, u] : vertexlist(g)) {
        cout << city_id_str(g, uid) << endl;
    }
    cout << endl;
    // ==================== 演示:incidence ====================
    cout << "=== incidence: Frankfurt 的出边 ===" << endl;
    for (auto&& [vid, uv] : incidence(g, frankfurt_id)) {
        cout << "  --> " << city_id_str(g, vid) << " (" << uv.distance << "km)" << endl;
    }
    cout << endl;
    // ==================== 演示:neighbors ====================
    cout << "=== neighbors: Frankfurt 的邻居城市 ===" << endl;
    for (auto&& [vid, v] : neighbors(g, frankfurt_id)) {
        cout << "  --> " << city_id_str(g, vid) << endl;
    }
    cout << endl;
    // ==================== 演示:edgelist ====================
    cout << "=== edgelist: 图中所有边 ===" << endl;
    for (auto&& [uid, vid, uv] : edgelist(g)) {
        cout << city_id_str(g, uid) << " --> " << city_id_str(g, vid) << " (" << uv.distance
             << "km)" << endl;
    }
    cout << endl;
    // ==================== 演示:DFS ====================
    cout << "=== Depth First Search (DFS) 从 Frankfurt ===" << endl;
    for (auto&& [vid, v] : vertices_depth_first_search(g, frankfurt_id)) {
        cout << city_id_str(g, vid) << endl;
    }
    cout << endl;
    // ==================== 演示:BFS ====================
    cout << "=== Breadth First Search (BFS) 从 Frankfurt ===" << endl;
    for (auto&& [vid, v] : vertices_breadth_first_search(g, frankfurt_id)) {
        cout << city_id_str(g, vid) << endl;
    }
    cout << endl;
    // ==================== 演示:Dijkstra ====================
    cout << "=== Dijkstra 最短路径 (km) 从 Frankfurt ===" << endl;
    auto weight_km = [&g](const route& uv) { return edge_value(g, uv); };
    vector<double> distance(city_names.size());           // 存储最短距离
    vector<city_id_type> predecessor(city_names.size());  // 存储前驱节点
    dijkstra_clrs(g, frankfurt_id, distance, predecessor, weight_km);
    // 输出每个城市的最短路径距离
    for (city_id_type uid = 0; uid < city_names.size(); ++uid) {
        if (distance[uid] > 0 && distance[uid] < numeric_limits<double>::max()) {
            cout << "  --> " << city_id_str(g, uid) << " - " << distance[uid] << "km" << endl;
        }
    }
    cout << endl;
    // 找最远的城市
    city_id_type farthest_id = frankfurt_id;
    double farthest_dist = 0.0;
    for (city_id_type uid = 0; uid < city_names.size(); ++uid) {
        if (distance[uid] < numeric_limits<double>::max() && distance[uid] > farthest_dist) {
            farthest_dist = distance[uid];
            farthest_id = uid;
        }
    }
    cout << "从 " << city(g, frankfurt_id) << " 最远的城市是 " << city(g, farthest_id) << ",距离 "
         << distance[farthest_id] << "km" << endl;
    // 输出从 Frankfurt 到最远城市的最短路径
    cout << "最短路径: ";
    vector<city_id_type> path;
    for (city_id_type uid = farthest_id; uid != frankfurt_id && uid >= 0; uid = predecessor[uid]) {
        path.push_back(uid);
        if (predecessor[uid] == uid) break;  // 防止自环
    }
    path.push_back(frankfurt_id);
    for (size_t i = 0; i < path.size(); ++i) {
        if (i > 0) cout << " -- ";
        cout << city_id_str(g, path[i]);
    }
    cout << endl;
    return 0;
}

CSR 图、图容器以及相关接口、概念的内容整理成详细的理解说明,条理清晰并尽量保留原文信息。

一、图容器概述

1. 图容器的特点

  • 独特性:每个容器都是唯一的,不能与其他图容器混淆。
  • 范围嵌套:图是“范围的范围”(range of ranges),即顶点集合、每个顶点的边集合。
  • 自由函数接口:所有函数都是自由函数(free functions),类似 STL 的 begin, end, size, empty
  • 自定义点:所有函数都是可定制的(customization points)。
  • 可选用户值:顶点、边和图可以包含用户自定义的值,但不是必需的。

2. CSR 图(Compressed Sparse Row)

  • 压缩稀疏行矩阵表示,高性能且内存紧凑。
  • 静态结构:一旦构造完成,顶点和边的结构不能改变,但顶点/边的值可以修改。
  • 值存储分离:结构(adjacency)和数据(权值)是分离的。
    模板参数
template <class EV, class VV, class GV = void, integral VId = uint32_t, class Alloc = allocator<uint32_t>>
class csr_graph;
  • EV:边的值类型
  • VV:顶点的值类型
  • GV:图的值类型(可选)
  • VId:顶点 ID 类型
  • Alloc:内部容器的分配器类型
    使用示例
using G = std::graph::csr_graph<double, std::string_view, std::string>;

3. CSR 图构造函数

  1. 仅边构造
template <ranges::forward_range ERng, class EProj = identity>
csr_graph(const ERng& erng, EProj eproj = {}, const Alloc& alloc = Alloc());
  • ERng:边的范围
  • EProj:边投影函数,将用户边对象转换为标准 edge_view {source_id, target_id[, value]}
  1. 边 + 顶点构造
template <ranges::forward_range ERng, class EProj = identity,
          ranges::forward_range VRng, class VProj = identity>
csr_graph(const ERng& erng, const VRng& vrng, EProj eproj = {}, VProj vproj = {}, const Alloc& alloc = Alloc());
  • 同时提供顶点值范围 VRng 和顶点投影 VProj

二、图和顶点函数

函数 返回类型 默认复杂度 注释
graph_value(g) graph_value_t<G> 常量 可选,返回图的用户值
vertices(g) vertex_range_t<G> 常量 返回顶点范围
vertex_id(g, ui) vertex_id_t<G> 常量 根据迭代器获取顶点 ID
vertex_value(g,u) vertex_value_t<G> 常量 可选,返回顶点值
degree(g,u) 整数类型 常量 顶点出度 = size(edges(g,u))
find_vertex(g, uid) vertex_iterator_t<G> 常量 返回顶点迭代器

三、边函数

函数 返回类型 默认复杂度 注释
edges(g,u) vertex_edge_range_t<G> 常量 返回顶点 u 的边范围
edges(g,uid) vertex_edge_range_t<G> 常量 默认调用 edges(g, *find_vertex(g, uid))
target_id(g, uv) vertex_id_t<G> 常量 返回边的目标顶点 ID
target(g, uv) vertex_t<G> 常量 返回目标顶点对象引用
edge_value(g, uv) edge_value_t<G> 常量 可选,返回边的值
find_vertex_edge(g,u,vid) vertex_edge_t<G> 线性 根据目标顶点 ID 查找边
contains_edge(g, uid, vid) bool 线性 检查是否存在某条边

四、源边函数(Sourced Edge)

函数 返回类型 默认实现
source_id(g, uv) vertex_id_t<G> 常量,返回源顶点 ID
source(g, uv) vertex_t<G> 默认 *(begin(vertices(g)) + source_id(g, uv))
edge_id(g, uv) edge_id_t<G> 返回 (source_id, target_id)

五、类型别名与 Traits

顶点相关类型

  • graph_reference_t<G>:图引用类型
  • vertex_range_t<G>:顶点范围类型
  • vertex_iterator_t<G>:顶点迭代器类型
  • vertex_t<G>:顶点值类型
  • vertex_reference_t<G>:顶点引用类型
  • vertex_id_t<G>:顶点 ID 类型
  • vertex_value_t<G>:顶点值类型(可选)

边相关类型

  • vertex_edge_range_t<G>:顶点边范围类型
  • vertex_edge_iterator_t<G>:边迭代器类型
  • edge_t<G>:边值类型
  • edge_reference_t<G>:边引用类型
  • edge_value_t<G>:边值类型(可选)
  • edge_id_t<G>:边 ID 类型 (source_id, target_id)

六、Traits / 概念示例

  • has_degree<G>:是否支持 degree(g,u)
  • has_find_vertex<G>:是否支持 find_vertex(g, uid)
  • has_find_vertex_edge<G>:是否支持查找顶点边
  • has_contains_edge<G>:是否支持 contains_edge(g, uid, vid)
  • 无序边(unordered_edge) / 有序边(ordered_edge)
    用于标记边类型是否是无序/有序的。
  • 邻接矩阵概念
template <class G> concept adjacency_matrix = is_adjacency_matrix_v<G>;

用于标记图是否采用邻接矩阵存储。

七、外部图集成

  1. 必需函数
    • vertices(g)
    • edges(g,u)
    • target_id(g,uv)
  2. 可选函数
    • graph_value(g)
    • vertex_value(g,u)
    • edge_value(g,uv)
    • vertex_id(g,ui)
    • source_id(g,uv)
  3. Niebloid / tag_invoke 机制
    • rr_adaptor 通过 tag_invoke 自定义 vertices()
    • 支持将外部图数据适配为标准接口。

八、其他图容器示例

rr_adaptor

  • 包装 range of ranges 的外部图。
  • 支持顶点值向量 + 边范围 + 可选重复边。
  • 示例类型:
using RR = std::vector<std::list<route>>; 
using routes_rr_graph_type = rr_adaptor<RR, city_names_type>;

dynamic_graph

  • 动态图,支持 sourced_edge
  • 使用 vofl_graph_traits 进行类型配置:
using Traits = vofl_graph_traits<double, std::string, std::string>;
using G = dynamic_adjacency_graph<Traits>;

九、总结

  1. CSR 图:静态高性能图,适合查询和 Dijkstra 最短路径。
  2. rr_adaptor / dynamic_graph:外部图或动态图适配器。
  3. 接口统一vertices, edges, target_id 是核心函数。
  4. 可选值 / Traits / Concepts:支持泛型算法和概念检查。
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐