@@ -702,6 +702,125 @@ public class Main {
702702
703703```
704704
705+ ``` java
706+ class Edge {
707+ public int u; // 边的端点1
708+ public int v; // 边的端点2
709+ public int val; // 边的权值
710+
711+ public Edge () {
712+ }
713+
714+ public Edge (int u , int v ) {
715+ this . u = u;
716+ this . v = v;
717+ this . val = 0 ;
718+ }
719+
720+ public Edge (int u , int v , int val ) {
721+ this . u = u;
722+ this . v = v;
723+ this . val = val;
724+ }
725+ }
726+
727+ /**
728+ * SPFA算法(版本3):处理含【负权回路】的有向图的最短路径问题
729+ * bellman_ford(版本3) 的队列优化算法版本
730+ * 限定起点、终点、至多途径k个节点
731+ */
732+ public class SPFAForSSSP {
733+
734+ /**
735+ * SPFA算法
736+ *
737+ * @param n 节点个数[1,n]
738+ * @param graph 邻接表
739+ * @param startIdx 开始节点(源点)
740+ */
741+ public static int [] spfa (int n , List<List<Edge > > graph , int startIdx , int k ) {
742+ // 定义最大范围
743+ int maxVal = Integer . MAX_VALUE ;
744+ // minDist[i] 源点到节点i的最短距离
745+ int [] minDist = new int [n + 1 ]; // 有效节点编号范围:[1,n]
746+ Arrays . fill(minDist, maxVal); // 初始化为maxVal
747+ minDist[startIdx] = 0 ; // 设置源点到源点的最短路径为0
748+
749+ // 定义queue记录每一次松弛更新的节点
750+ Queue<Integer > queue = new LinkedList<> ();
751+ queue. offer(startIdx); // 初始化:源点开始(queue和minDist的更新是同步的)
752+
753+
754+ // SPFA算法核心:只对上一次松弛的时候更新过的节点关联的边进行松弛操作
755+ while (k + 1 > 0 && ! queue. isEmpty()) { // 限定松弛 k+1 次
756+ int curSize = queue. size(); // 记录当前队列节点个数(上一次松弛更新的节点个数,用作分层统计)
757+ while (curSize-- > 0 ) { // 分层控制,限定本次松弛只针对上一次松弛更新的节点,不对新增的节点做处理
758+ // 记录当前minDist状态,作为本次松弛的基础
759+ int [] minDist_copy = Arrays . copyOfRange(minDist, 0 , minDist. length);
760+
761+ // 取出节点
762+ int cur = queue. poll();
763+ // 获取cur节点关联的边,进行松弛操作
764+ List<Edge > relateEdges = graph. get(cur);
765+ for (Edge edge : relateEdges) {
766+ int u = edge. u; // 与`cur`对照
767+ int v = edge. v;
768+ int weight = edge. val;
769+ if (minDist_copy[u] + weight < minDist[v]) {
770+ minDist[v] = minDist_copy[u] + weight; // 更新
771+ // 队列同步更新(此处有一个针对队列的优化:就是如果已经存在于队列的元素不需要重复添加)
772+ if (! queue. contains(v)) {
773+ queue. offer(v); // 与minDist[i]同步更新,将本次更新的节点加入队列,用做下一个松弛的参考基础
774+ }
775+ }
776+ }
777+ }
778+ // 当次松弛结束,次数-1
779+ k-- ;
780+ }
781+
782+ // 返回minDist
783+ return minDist;
784+ }
785+
786+ public static void main (String [] args ) {
787+ // 输入控制
788+ Scanner sc = new Scanner (System . in);
789+ System . out. println(" 1.输入N个节点、M条边(u v weight)" );
790+ int n = sc. nextInt();
791+ int m = sc. nextInt();
792+
793+ System . out. println(" 2.输入M条边" );
794+ List<List<Edge > > graph = new ArrayList<> (); // 构建邻接表
795+ for (int i = 0 ; i <= n; i++ ) {
796+ graph. add(new ArrayList<> ());
797+ }
798+ while (m-- > 0 ) {
799+ int u = sc. nextInt();
800+ int v = sc. nextInt();
801+ int weight = sc. nextInt();
802+ graph. get(u). add(new Edge (u, v, weight));
803+ }
804+
805+ System . out. println(" 3.输入src dst k(起点、终点、至多途径k个点)" );
806+ int src = sc. nextInt();
807+ int dst = sc. nextInt();
808+ int k = sc. nextInt();
809+
810+ // 调用算法
811+ int [] minDist = SPFAForSSSP . spfa(n, graph, src, k);
812+ // 校验起点->终点
813+ if (minDist[dst] == Integer . MAX_VALUE ) {
814+ System . out. println(" unreachable" );
815+ } else {
816+ System . out. println(" 最短路径:" + minDist[n]);
817+ }
818+ }
819+ }
820+ ```
821+
822+
823+
705824### Python
706825``` python
707826def main ():
0 commit comments