C#,图论与图算法,寻找加权有向图中所有顶点对之间的最短路径的约翰逊算法(Johnson‘s Algorithm)与源程序

article/2025/9/25 19:43:05

一、最短路径问题

问题是找到给定加权有向图中每对顶点之间的最短路径,权重可能为负。我们已经讨论了这个问题的Floyd-Warshall算法。Floyd-Warshall算法的时间复杂度为Θ(V3)。利用Johnson算法,我们可以在O(V2log V+VE)时间内找到所有对的最短路径。Johnson的算法使用Dijkstra和Bellman Ford作为子程序。如果我们对每个顶点应用Dijkstra的单源最短路径算法,将每个顶点作为源,我们可以在O(V*VLogV)时间内找到所有对最短路径。因此,使用Dijkstra的单源最短路径似乎比Floyd Warshall的算法更好(https://www.geeksforgeeks.org/floyd-warshall-algorithm-dp-16/?ref=lbp),但Dijkstra算法的问题是,它不适用于负权重边。Johnson算法的思想是重新加权所有边并使它们都为正,然后对每个顶点应用Dijkstra算法。如何将给定的图转换为具有所有非负权重边的图?可以考虑一种简单的方法,找到最小权重边并将此权重添加到所有边。不幸的是,这不起作用,因为在不同的路径中可能有不同数量的边(请参见此示例)。如果从顶点u到v有多条路径,则所有路径都必须以相同的数量增加,以便最短路径在变换后的图中保持最短。Johnson算法的思想是为每个顶点分配一个权重。设指定给顶点u的权重为h[u]。我们使用顶点权重重新加权边。例如,对于权重为w(u,v)的边(u,v),新权重将变为w(u,v)+h[u]–h[v]。这种重新加权的好处在于,任意两个顶点之间的所有路径集都以相同的数量增加,所有负权重都变为非负权重。考虑两个顶点s和t之间的任何路径,每条路径的权重都会增加h[s]–h[t],并且从s到t的路径上所有顶点的h[]值都会相互抵消。我们如何计算h[]值?Bellman-Ford算法用于此目的。下面是完整的算法。新顶点将添加到图形中,并连接到所有现有顶点。从新顶点到所有现有顶点的最短距离值为h[]值。算法:1)将给定的图设为G。向图中添加一个新顶点s,将新顶点的边添加到G的所有顶点。将修改后的图设为G’。2) 以s为源在G’上运行Bellman-Ford算法。设Bellman Ford计算的距离为h[0],h[1]。。h【V-1】。如果我们发现一个负的权重周期,那么返回。请注意,新顶点s无法创建负权重循环,因为s没有边。所有边都来自s。3)重新加权原始图形的边。对于每条边(u,v),将新权重指定为“原始权重+h[u]–h[v]”。4) 删除添加的顶点s,并对每个顶点运行Dijkstra算法。转换如何确保非负权重边?以下属性对于h[]值始终为真,因为它们是最短距离。

h【v】<=h【u】+w(u,v)

该属性仅表示从s到v的最短距离必须小于或等于从s到u的最短距离加上边的权重(u,v)。新权重为w(u,v)+h[u]–h[v]。由于不等式“h[v]<=h[u]+w(u,v)”,新权重的值必须大于或等于零。示例:让我们考虑下图。Johnson 1我们添加一个源s,并将s的边添加到原始图的所有顶点。下图中s为4。Johnson2我们使用Bellman-Ford算法计算从4到所有其他顶点的最短距离。从4到0、1、2和3的最短距离分别为0、-5、-1和0,即h[]={0、-5、-1、0}。获得这些距离后,我们移除源顶点4,并使用以下公式重新加权边。w(u,v)=w(u,v)+h[u]–h[v]。Johnson 3由于所有权重现在都为正,我们可以对每个顶点运行Dijkstra最短路径算法作为源。

二、约翰逊算法

Johnson算法寻找加权有向图中所有顶点对之间的最短路径。它允许一些边权重为负数,但可能不存在负权重循环。它使用Bellman-Ford算法重新加权原始图,移除所有负权重。Dijkstra算法应用于重加权图,以计算所有顶点对之间的最短路径。

算法描述

使用Dijkstra算法,可以找到O(V2logV)中所有顶点对之间的最短路径。然而,Dijkstra不适用于负权重。为了避免这个问题,Johnson的算法使用了一种称为重新加权的技术。

重新加权是一个过程,通过该过程,每个边的权重都会发生变化,以满足两个特性-

对于图中的所有顶点对u、v,如果重新加权之前这些顶点之间存在最短路径,则它也必须是重新加权之后这些顶点之间的最短路径。

对于图中的所有边(u,v),它们必须具有非负权重(u,v)。

Johnson的算法使用Bellman Ford重新加权边缘。Bellman Ford还能够检测到原始图表中存在的负重量周期。

三、Johnson的算法源代码

using System;
using System.Text;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace Legalsoft.Truffer.Algorithm
{

    public class Neighbour
    {
        public int destination { get; set; } = 0;
        public int weight { get; set; } = 0;

        public Neighbour(int d, int w)
        {
            this.destination = d;
            this.weight = w;
        }
    }

    public class Graph_Johnson_Algorithm
    {

        public int vertices;
        public List<List<Neighbour>> adjacencyList;

        public Graph_Johnson_Algorithm()
        {
        }

        public Graph_Johnson_Algorithm(int vertices, int[,] adjacencyMatrix)
        {
            this.vertices = vertices;

            adjacencyList = new List<List<Neighbour>>(vertices);
            for (int i = 0; i < vertices; i++)
            {
                adjacencyList.Add(new List<Neighbour>());
            }

            for (int i = 0; i < vertices; i++)
            {
                for (int j = 0; j < vertices; j++)
                {
                    if (adjacencyMatrix[i, j] != 0)
                    {
                        addEdge(i, j, adjacencyMatrix[i, j]);
                    }
                }
            }
        }

        public void addEdge(int source, int destination, int weight)
        {
            adjacencyList[source].Add(new Neighbour(destination, weight));
        }

        public int[] dijkstra(int source)
        {
            bool[] isVisited = new bool[vertices];
            int[] distance = new int[vertices];

            for (int i = 0; i < distance.Length; i++)
            {
                distance[i] = Int32.MaxValue;
            }
            distance[0] = 0;

            for (int vertex = 0; vertex < vertices; vertex++)
            {
                int minDistanceVertex = findMinDistanceVertex(distance, isVisited);
                isVisited[minDistanceVertex] = true;

                foreach (Neighbour neighbour in adjacencyList[minDistanceVertex])
                {
                    int destination = neighbour.destination;
                    int weight = neighbour.weight;

                    if (!isVisited[destination] && distance[minDistanceVertex] + weight < distance[destination])
                    {
                        distance[destination] = distance[minDistanceVertex] + weight;
                    }
                }
            }

            return distance;
        }

        public int findMinDistanceVertex(int[] distance, bool[] isVisited)
        {
            int minIndex = -1;
            int minDistance = Int32.MaxValue;

            for (int vertex = 0; vertex < vertices; vertex++)
            {
                if (!isVisited[vertex] && distance[vertex] <= minDistance)
                {
                    minDistance = distance[vertex];
                    minIndex = vertex;
                }
            }

            return minIndex;
        }

        public int[] bellmanford(int source)
        {
            int[] distance = new int[vertices];

            for (int i = 0; i < distance.Length; i++)
            {
                distance[i] = Int32.MaxValue;
            }
            distance[0] = 0;

            for (int i = 0; i < vertices - 1; i++)
            {
                for (int currentVertex = 0; currentVertex < vertices; currentVertex++)
                {
                    foreach (Neighbour neighbour in adjacencyList[currentVertex])
                    {
                        if (distance[currentVertex] != Int32.MaxValue && distance[currentVertex] + neighbour.weight < distance[neighbour.destination])
                        {
                            distance[neighbour.destination] = distance[currentVertex] + neighbour.weight;
                        }
                    }
                }
            }

            for (int currentVertex = 0; currentVertex < vertices; currentVertex++)
            {
                foreach (Neighbour neighbour in adjacencyList[currentVertex])
                {
                    if (distance[currentVertex] != Int32.MaxValue && distance[currentVertex] + neighbour.weight < distance[neighbour.destination])
                    {
                        return null;
                    }
                }
            }

            return distance;
        }

        public int[,] johnsons()
        {
            this.vertices++;
            adjacencyList.Add(new List<Neighbour>());
            for (int i = 0; i < vertices - 1; i++)
            {
                adjacencyList[vertices - 1].Add(new Neighbour(i, 0));
            }
            int[] h = bellmanford(vertices - 1);
            if (h == null)
            {
                return null;
            }
            for (int u = 0; u < vertices; u++)
            {
                List<Neighbour> neighbours = adjacencyList[u];
                foreach (Neighbour neighbour in neighbours)
                {
                    int v = neighbour.destination;
                    int w = neighbour.weight;
                    neighbour.weight = w + h[u] - h[v];
                }
            }

            adjacencyList.RemoveRange(adjacencyList.Count - vertices - 1, vertices);
            vertices--;

            int[,] distances = new int[vertices, vertices];

            for (int s = 0; s < vertices; s++)
            {
                int[] da = dijkstra(s);
                for (int i = 0; i < da.Length; i++)
                {
                    distances[s, i] = da[i];
                }
            }

            for (int u = 0; u < vertices; u++)
            {
                for (int v = 0; v < vertices; v++)
                {
                    if (distances[u, v] == Int32.MaxValue)
                    {
                        continue;
                    }
                    distances[u, v] += (h[v] - h[u]);
                }
            }

            return distances;
        }
    }


    public static partial class GraphDrives
    {
        public static string Jhonsons()
        {
            int vertices = 4;
            int[,] matrix = {
                { 0,  0, -2, 0 },
                { 4,  0,  3, 0 },
                { 0,  0,  0, 2 },
                { 0, -1,  0, 0 }
            };

            Graph_Johnson_Algorithm graph = new Graph_Johnson_Algorithm(vertices, matrix);

            int[,] distances = graph.johnsons();

            if (distances == null)
            {
                return ("Negative weight cycle detected.");
            }
            return Algorithm_Gallery.ToHtml(distances);
        }
    }
}
 


http://chatgpt.dhexx.cn/article/2Q8kCJXh.shtml

相关文章

最短路径算法——Dijkstra,Bellman-Ford,Floyd-Warshall,Johnson

本文内容框架&#xff1a; 1 Dijkstra算法 2 Bellman-Ford算法 3 Floyd-Warshall算法 4 Johnson算算法 5 问题归约 6 小结 常用的最短路径算法有&#xff1a;Dijkstra算法、Bellman-Ford算法、Floyd-Warshall算法、Johnson算法 最短路径算法可以分为单源点最短路径和全源最短路…

0018算法笔记——【动态规划】流水作业调度问题与Johnson法则

1、问题描述&#xff1a; n个作业{1&#xff0c;2&#xff0c;…&#xff0c;n}要在由2台机器M1和M2组成的流水线上完成加工。每个作业加工的顺序都是先在M1上加工&#xff0c;然后在M2上加工。M1和M2加工作业i所需的时间分别为ai和bi。流水作业调度问题要求确定这n个作业的…

转:johnson算法的现实意义

Johnson算法是一种用于解决边数与节点数之间关系为O(n^2)的带权图的最短路径问题的算法。它是一种结合了Dijkstra算法和Bellman-Ford算法的技术&#xff0c;通过使用一个负权重的环检测器来消除负权重的影响。这种算法的时间复杂度为O(n^2m log n)。 Johnson算法是一种用于解决…

软件定义网络SDN基础实验:MiniNet常用命令、创建网络拓扑、OpenFlow流表操作

此实验基于《软件定义网络实验1-5》&#xff0c;主要内容为&#xff1a; MiniNet常用命令如何创建网络拓扑OpenFlow流表操作 00x1 搭建SDN环境 SDN 环境配置&#xff1a;Mininet Ryu 1. 测试环境是否搭建成功 启动Ryu&#xff0c;进入 /ryu/app&#xff0c;启动一个交换机…

软件定义网络基础(SDN②)

一.传统网络设备 1.传统设备控制平面和数据平面 2.数据平面的任务 在传统网络中&#xff0c;数据平面是指实际传输和处理数据的部分。它包括网络设备&#xff08;如交换机和路由器&#xff09;&#xff0c;它们通过将数据包从一个接口转发到另一个接口来实现数据传输。数据平面…

软件定义网络SDN

一、为什么使用软件定义网络 传统网络及其设备只可配置&#xff0c;不可编程。网络的分布式控制与管理架构带来的制约&#xff0c;网络的部署、配置与管理需要落到每台设备上去手工完成&#xff0c;每个设备下都紧耦合了三个平面&#xff08;管理平面、控制平面、数据平面&…

软件定义网络技术现状分析

作者&#xff1a;郭春梅&#xff0c;启明星辰资深研究员&#xff0c;研究方向为云计算、虚拟化、SDN技术及安全 转载自&#xff1a;https://mp.weixin.qq.com/s?__bizMzAxNzExNjQ5NA&mid211287920&idx1&snd49893e9187e6055e79db8bb37e44408&scene1&fromg…

SDN软件定义网络相关概念

总结 软件定义网络&#xff0c;已经逐渐成为云计算所依赖的重要技术支一&#xff0c;软件定义网络作为一种新型的网络架构&#xff0c;把网络控制平面和数据平面进行分离&#xff0c;其中控制平面拥有网络全局视图&#xff0c;集中控制网络资源&#xff0c;数据平面只转发数据…

软件定义网络基础(SDN④)

一&#xff1a;SDN控制平面 一个或多个SDN控制器组成&#xff0c;是网络的大脑。 对底层网络交换设备进行集中管理&#xff0c;状态监测、转发决策以及处理和调 度数据平面的流量&#xff1b;通过北向接口向上层应用开放多个层次的可编程能力。 1.典型的SDN控制器体系架构 SDN控…

软件定义网络(SDN)工作原理

传统网络分布式控制架构: 管理平面: 管理设备&#xff08;SNMP&#xff09; 主要包括设备管理系统和业务管理系统&#xff0c;设备管理系统负责网络拓扑、设备接口、设备特性的管理&#xff0c;同时可以给设备下发配置脚本。业务管理系统用于对业务进行管理&#xff0c;比如业务…

认识软件定义网络(SDN)(一)

一、SDN体系结构简介 在传统IP网络中&#xff0c;网络设备内部同时集成了控制逻辑和数据逻辑&#xff0c;控制平面需要实现各种类型的网络协议和功能&#xff0c;为数据平面构造和配置路由转发表&#xff0c;而数据平面则根据路由转发表实现数据包的转发。一般来说&#xff0c…

SDN(Software Defined Network) 软件定义网络学习

SDN&#xff08;Software Defined Network&#xff09; 软件定义网络学习 SDN是啥&#xff1f; 简单来说就是软件定义网络&#xff01;其旨在对现有的网络架构进行重构&#xff0c;使得我们能够像安装软件一样对网络进行修改&#xff0c;加快部署&#xff0c;提高网络可编程能…

软件定义网络 Software Defined Network (一)概述

软件定义网络 Software Defined Network 本文将从以下3个问题对SDN进行阐述 1、为什么要有SDN&#xff1f; 伴随云计算、移动互联网和物联网的蓬勃兴起&#xff0c;应用与业务日益多元&#xff0c;而且快速且多变。网络系统的亚健康问题逐渐明显起来。传统网络工程的弊端日益…

浅谈软件定义网络(SDN)技术研究现状和发展趋势

浅谈软件定义网络(SDN)技术研究现状和发展趋势 友情全文PDF链接&#xff1a;浅谈软件定义网络(SDN)技术研究现状和发展趋势.pdf-网络基础文档类资源-CSDN下载 长久以来&#xff0c;硬件在网络世界中保持着至高无上的地位。直到2008年斯坦福大学的学者提出 OpenFlow[1]&#xf…

软件定义的网络(中)

SDN的出现打破了传统网络设备制造商独立而封闭的控制面结构体系&#xff0c;将改变网络设备形态和网络运营商的工作模式&#xff0c;对网络的应用和发展将产生直接影响。 从技术层面和应用层面来看&#xff0c;SDN的特点主要体现在以下几个方面&#xff1a; 数据平面与控制平…

软件定义网络(PART 3)

软件定义网络PART 3 数据平面SDN数据平面传统网络数据平面传统网络数据平面的特点SDN数据平面SDN数据平面的特点OpenFlow Switch通用转发模型通用可编程转发模型 OpenFlow 概述OpenFlow架构三个组成部分OpenFlow主要版本及特性单流表到多级流表的架构 Open Flow 流表什么是流表…

软件定义网络SDN----新型网络体系结构

1.传统路由器 传统路由器的功能&#xff1a; 为主机间的通信提供转发服务路由选择 路由器之间的传送信息&#xff1a; 主机间的分组路由信息 SDN这种新型网络体系结构的核心思想&#xff1a;把网络的控制层面和数据层面分离&#xff0c;而让控制层面利用软件来控制数据层面…

软件定义网络SDN(计算机网络-网络层)

目录 软件定义网络SDN 数据平面和控制平面 SDN 最重要的三个特征 控制平面与数据平面分离 SDN 的数据平面 软件定义网络SDN SDN的本质特点是控制平面和数据平面的分离以及网络的可编程性&#xff0c;从而实现了网络流量的灵活控制&#xff0c;方便用户管理和配置网络以及部…

软件定义的网络(下)

我们在这里对这两种SDN实现方案分别做个简单描述。 &#xff08;1&#xff09;以网络为中心的SDN 以网络为中心的SDN的技术核心是OpenFlow协议&#xff0c;OpenFlow技术最早由斯坦福大学于2008年提出&#xff0c;它是一种通信协议&#xff0c;用来提供对网络设备诸如交换机和…