精华内容
下载资源
问答
  • 最短路径算法--无向图

    千次阅读 2020-03-01 10:25:52
    最短路径算法 Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法。该算法被称为是“贪心算法”的成功典范。...比如,如果顶点A 一条边到B、C和D,那么A的列表中会3条边 邻接...

                                             最短路径算法

     Dijkstra算法是最短路径算法中为人熟知的一种,是单起点全路径算法。该算法被称为是“贪心算法”的成功典范。

    1、表示图的数据结构 

    邻接列表

    邻接列表:在邻接列表实现中,每一个顶点会存储一个从它这里开始的边的列表。比如,如果顶点A 有一条边到B、C和D,那么A的列表中会有3条边

    邻接列表只描述了指向外部的边。A 有一条边到B,但是B没有边到A,所以 A没有出现在B的邻接列表中。查找两个顶点之间的边或者权重会比较费时,因为遍历邻接列表直到找到为止。

     邻接矩阵

    邻接矩阵:在邻接矩阵实现中,由行和列都表示顶点,由两个顶点所决定的矩阵对应元素表示这里两个顶点是否相连、如果相连这个值表示的是相连边的权重。例如,如果从顶点A到顶点B有一条权重为 5.6 的边,那么矩阵中第A行第B列的位置的元素值应该是5.6:

    、、

     

    图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(邻接矩阵)存储图中的边或弧的信息。

      设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:

      

    从上面可以看出,无向图的边数组是一个对称矩阵。所谓对称矩阵就是n阶矩阵的元满足aij = aji。即从矩阵的左上角到右下角的主对角线为轴,右上角的元和左下角相对应的元全都是相等的。

        从这个矩阵中,很容易知道图中的信息。

        (1)要判断任意两顶点是否有边无边就很容易了;

        (2)要知道某个顶点的度,其实就是这个顶点vi在邻接矩阵中第i行或(第i列)的元素之和;

        (3)求顶点vi的所有邻接点就是将矩阵中第i行元素扫描一遍,arc[i][j]为1就是邻接点;

        而有向图讲究入度和出度,顶点vi的入度为1,正好是第i列各数之和。顶点vi的出度为2,即第i行的各数之和。

    算法实现思路

    无向图的最短路径实现相对于带权的有向图最短路径实现要简单得多。

    源点的最短路径距离为0,从源点开始,采用广度优先的顺序,首先将与源点邻接的顶点的路径求出,然后再依次求解图中其他顶点的最短路径。

    由于顶点的最短路径的求解顺序 是一个 广度优先的顺序,因此需要一个辅助队列。初始时,将源点的最短路径距离设置为0,将源点入队列。

    然后,在一个while循环中,从队列中弹出顶点,遍历该顶点的邻接点,若该邻接点的距离未被更新过(表示该邻接点未被访问过),更新邻接点的最短路径距离为 该顶点的距离加上1,并将所有的邻接点入队列。

     

    该算法的实现与 二叉树的层序遍历,有向图的拓扑排序算法实现都非常的相似。他们都采用了广度的思想在里面。

    广度优先的思想就是:处理完某个顶点后,去处理该顶点的所有邻接点,处理完它的邻接点后,再去处理更远(更外层)的顶点。

    算法的代码如下:

    /*
         * 计算源点s到无向图中各个顶点的最短路径
         * 需要一个队列来保存图中的顶点,初始时,源点入队列,然后以广度的形式向外扩散求解其他顶点的最短路径
         */
        private void unweightedShortestPath(Vertex s){
            //初始化
            Queue<Vertex> queue = new LinkedList<>();
            s.dist = 0;
            queue.offer(s);//将源点dist设置为0并入队列
            
            while(!queue.isEmpty()){
                Vertex v = queue.poll();
                for (Edge e : v.adjEdges) {//扫描v的邻接边(点)
                    if(e.endVertex.dist == Integer.MAX_VALUE){//如果这个顶点(e.endVertex)未被访问(每个顶点只会入队列一次)
                        e.endVertex.dist = v.dist + 1;//更新该顶点到源点的距离
                        queue.offer(e.endVertex);
                        e.endVertex.preNode = v;//设置该顶点的前驱顶点
                    }//end if
                }//end for
            }//end while
        }

    完整代码实现

    import java.util.Collection;
    import java.util.LinkedHashMap;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.Queue;
    
    /*
     * 求解无向图的单源最短路径
     */
    public class NonDirectedGraph {
        private class Vertex{
            private String vertexLabel;//顶点标识
            private List<Edge> adjEdges;//与该顶点邻接的边(点)
            private int dist;//顶点距离(该顶点到起始顶点的距离)
            private Vertex preNode;
            
            public Vertex(String vertexLabel) {
                this.vertexLabel = vertexLabel;
                adjEdges = new LinkedList<>();
                dist = Integer.MAX_VALUE;
                preNode = null;
            }
        }
        private class Edge{
            private Vertex endVertex;
            public Edge(Vertex endVertex) {
                this.endVertex = endVertex;
            }
        }
        
        private Map<String, Vertex> nonDirectedGraph;//保存了图中所有的顶点,边的关系以List形式保存在Vertex类中
        private Vertex startVertex;//图的起始顶点
        
        public NonDirectedGraph(String graphContent) {
            nonDirectedGraph = new LinkedHashMap<>();
            buildGraph(graphContent);
        }
        
        private void buildGraph(String graphContent){
            String[] lines = graphContent.split("\n");
            
            String startNodeLabel, endNodeLabel;
            Vertex startNode, endNode;
            for(int i = 0; i < lines.length; i++){
                String[] nodesInfo = lines[i].split(",");
                startNodeLabel = nodesInfo[1];
                endNodeLabel = nodesInfo[2];
                
                endNode = nonDirectedGraph.get(endNodeLabel);
                if(endNode == null){
                    endNode = new Vertex(endNodeLabel);
                    nonDirectedGraph.put(endNodeLabel, endNode);
                }
                
                startNode = nonDirectedGraph.get(startNodeLabel);
                if(startNode == null){
                    startNode = new Vertex(startNodeLabel);
                    nonDirectedGraph.put(startNodeLabel, startNode);
                }
                Edge e = new Edge(endNode);
                //对于无向图而言,起点和终点都要添加边
                endNode.adjEdges.add(e);
                startNode.adjEdges.add(e);
            }
            startVertex = nonDirectedGraph.get(lines[0].split(",")[1]);//总是以文件中第一行第二列的那个标识顶点作为源点
        }
        
        public void unweightedShortestPath(){
            unweightedShortestPath(startVertex);
        }
        
        
        /*
         * 计算源点s到无向图中各个顶点的最短路径
         * 需要一个队列来保存图中的顶点,初始时,源点入队列,然后以广度的形式向外扩散求解其他顶点的最短路径
         */
        private void unweightedShortestPath(Vertex s){
            //初始化
            Queue<Vertex> queue = new LinkedList<>();
            s.dist = 0;
            queue.offer(s);//将源点dist设置为0并入队列
            
            while(!queue.isEmpty()){
                Vertex v = queue.poll();
                for (Edge e : v.adjEdges) {//扫描v的邻接边(点)
                    if(e.endVertex.dist == Integer.MAX_VALUE){//如果这个顶点(e.endVertex)未被访问(每个顶点只会入队列一次)
                        e.endVertex.dist = v.dist + 1;//更新该顶点到源点的距离
                        queue.offer(e.endVertex);
                        e.endVertex.preNode = v;//设置该顶点的前驱顶点
                    }//end if
                }//end for
            }//end while
        }
        
        //打印图中所有顶点到源点的距离及路径
        public void showDistance(){
            Collection<Vertex> vertexs = nonDirectedGraph.values();
            for (Vertex vertex : vertexs) {
                System.out.print(vertex.vertexLabel + "<--");
                Vertex tmpPreNode = vertex.preNode;
                while(tmpPreNode != null){
                    System.out.print(tmpPreNode.vertexLabel + "<--");
                    tmpPreNode = tmpPreNode.preNode;
                }
                System.out.println("distance=" + vertex.dist);
            }
        }
    }

    图理解

    参考链接

    https://blog.csdn.net/yu121380/article/details/79810233

    这里写图片描述

     

    对于求最短路径的问题一般都会给出一幅图,或者边与边的关系。如上图。

    假设我们起点是A,我们要求到F的最短距离,我们会怎么做? 
    首先,因为A是起点,所以我们把对于每个点都有个参数,相对于A的距离,默认除了A到A为0,其他都是无穷大。 

    这里写图片描述

    从起点A开始,我们更新与A相连通的点到A的距离,并把A点标记。如图: 
    这里写图片描述

    我们遍历一次所有点与A的距离,找到最小的,这里是点B。 
    以它为起点,把它周围未被标记的点拿来做比较,显然,像F这种没有与A练过的点,当前距离就会变成min(dis[B]+maze[B][F],INF),就是B到起点的距离加上BF之间的距离。而像c这种与A直接相连的点,当前距离就会变成min(dis[B]+maze[B][C],dis[c]),所以按照我们每次只需要比较当前点到当前状态起点的和与当前点到起点的距离就可以了。 
    这里写图片描述

    然后我们遍历发现,当前未被标记且到起点距离最小点的点是C点。我们更新连接了C的所有点。同样利用上面的min公式。 

    这里写图片描述

    同理,更新D点的邻点。 

    这里写图片描述

    再把更新E点的邻点。  

    这里写图片描述

    最后再更新F点。发现F点周围所有点都被标记了,不做更新。 
    再遍历,发现图中所有点都被遍历了,算法结束。 
    这个时候,我们已经求出了所有点到起点的最小距离。 
    可以直接输出dis[F]求得A到F的最短路径。

    注意: 
    上面的图重点是看每次变化找的起点和与出发点路径的变化。 
    当前起点是当前未被标记且到出发点距离最近的点。 
    更新的点都是与该起点直接相连并未被标记的点。

    因为并没有无穷大这个数,所以我们把INF设为一个我们计算不能到达的数,这里的数接近int的上限,可以近似看作正无穷。
    ————————————————
     

    #include"iostream"
    #include"cstring"
    #include"cstdio"
     
    using namespace std;
    #define INF 0x7f7f7f7f
     
    const int N = 105; //点的个数上限
     
    int maze[N][N];
    int dis[N];
    bool vis[N];
     
    //点的个数和边的条数
    int n,m;
     
    void init()
    {
        memset(maze,INF,sizeof(maze));
        memset(dis,INF,sizeof(dis));
        memset(vis,false,sizeof(vis));
    }
     
    void dijkstra(int st)
    {
        dis[st]=0;
        for(int i=1; i<=n; i++)
        {
        //找到和起点距离最短的点
            int minx=INF;
            int minmark;
            for(int j=1; j<=n; j++)
            {
                if(vis[j]==false&&dis[j]<=minx)
                {
                    minx=dis[j];
                    minmark=j;
                }
            }
            //并标记
            vis[minmark]=true;
            //更新所有和它连接的点的距离
            for(int j=1; j<=n; j++)
            {
                if(vis[j]==false&&dis[j]>dis[minmark]+maze[minmark][j])
                    dis[j]=dis[minmark]+maze[minmark][j];
            }
        }
    }
     
     
    int main()
    {
        while(scanf("%d %d",&n,&m)!=EOF)
        {
            if(n==0&&m==0) break;
            //每组数据都要初始化
            init();
            for(int i=1; i<=m; i++)
            {
                int x,y,len;
                scanf("%d %d %d",&x,&y,&len);
                if(x!=y&&maze[x][y]>len)
                {
                    maze[y][x]=len;
                    maze[x][y]=len;
                }
            }
            //以1为起点跑一次dij
            dijkstra(1);
            //输出到n的距离
            printf("%d\n",dis[n]);
        }
    }

    在这里插入图片描述

    展开全文
  • 向图和无向图

    万次阅读 多人点赞 2019-04-13 18:51:19
    向图、无向图 向图和无向图是我们常用到的术语,本文属于简单的科普帖。 全部由无向边构成图称为无向图(Undirected Graph),全部由向边构成图称为无向图(Directed Graph)。向,顾名思义,方向。本文...

    有向图、无向图

    有向图和无向图是我们常用到的术语,本文属于简单的科普帖。

    全部由无向边构成图称为无向图(Undirected Graph),全部由有向边构成图称为有向图(Directed Graph)。有向,顾名思义,有方向。本文中顶点Vertex(V),边Edge(E)

    (1)出度和入度:如图D,以点A为例子,在所有与A关联的边中,以A为起点的边的条数称为出度。而入度则刚好相反,以A为终点的边的条数则称为入读。其中,入度+出度,我们称为A的度。注意特殊情况:如图:A有一个自环,起点和终点都是自己,此时出度算一度,入度也算一度。如图:A的出度为3,入度也为2,A的度的5。

    在这里插入图片描述
    (2)描述图的邻接矩阵和关联矩阵
    【邻接矩阵】

    定义:
    设无向图G=(V,E),其中顶点集V=v1,v2,…,vn,边集 E=e1,e2,…,eε。用aij表示顶点vi与顶点vj之间的边数,可能取值为0,1,2,…,称所得矩阵A=A(G)=(aij)n×n为图G的邻接矩阵。邻接矩阵可以描述有向图和无向图。

    示例,求图所示简单图的邻接矩阵?
    在这里插入图片描述
    解:根据定义,可求得该无向图的邻接矩阵为
    在这里插入图片描述

    邻接矩阵的存储特点:
    (a)无向图的邻接矩阵一定是一个对称矩阵,有向图不一定是。
    *(b)邻接矩阵所需的存储空间值域只与顶点数有关系。
    (c)用邻接矩阵存储图,容易判断两个点之间是否有边。
    (d)如果一个有向图的邻接矩阵为三角矩阵(主对角线为0),则它的拓扑排序一定存在。
    *(e)小技巧:
    无向图:邻接矩阵的第i行或者第i列的非零元素的个数正好是第i个顶点Vi的度;
    有向图:邻接矩阵的第i行的非零元素个数正好是第i个顶点Vi的出度,第i列非零元素的个数正好是第i个顶点Vi的入度。

    【关联矩阵】

    定义:
    设任意图G=(V,E),其中顶点集V=v1,v2,…,vn,边集E=e1,e2,…,eε。用mij表示顶点vi与边ej关联的关系,可能取值为0,1,-1,称所得矩阵M(G)=(mij)n×ε为图G的关联矩阵。
    在这里插入图片描述
    mij 表示i行j列,探究顶点Vi和边Ej之间的关系,形成下列的关联矩阵
    示例:
    在这里插入图片描述
    关联矩阵
    在这里插入图片描述

    连通图、连通分量

    连通图:无向图中,若从顶点u到顶点v有路径,称为u,v是连通的。若图中任意两个顶点均是连通的,则称为连通图。
    连通分量:无向图中极大连通子图称为连通分量。

    强连通图、强连通分量

    强连通图:有向图中,若从顶点u到顶点v有路径,称为u,v是连通的。若图中任意两个顶点均是连通的,则称为强连通图。
    连通分量:无向图中极大连通子图称为强连通分量。特:强连通图只有强连通分量(本身),非强连通图有多个强连通分量。

    另外,本文参考路了下面两位作者的优秀博客
    https://blog.csdn.net/Hanging_Gardens/article/details/55670356
    https://blog.csdn.net/legendaryhaha/article/details/83049101

    展开全文
  • 算法——图之无向图

    万次阅读 2017-05-23 19:25:51
    图的概念 ...图可以分为无向图(简单连接),向图(连接方向),加权图(连接带权值),加权向图(连接既方向又权值)。 这篇讨论无向图无向图的表示方法: 1.邻接矩阵 2.边的数组 3.邻接表

    图的概念

    图是算法中是树的拓展,树是从上向下的数据结构,结点都有一个父结点(根结点除外),从上向下排列。而图没有了父子结点的概念,图中的结点都是平等关系,结果更加复杂。


    图的分类

    图可以分为无向图(简单连接),有向图(连接有方向),加权图(连接带权值),加权有向图(连接既有方向又有权值)。


    这篇讨论无向图。

    无向图的表示方法:

    1.邻接矩阵
    2.边的数组
    3.邻接表数组


    1.邻接矩阵

    我们可以使用一个V*V的布尔矩阵graph来表示图。当顶点v和顶点w之间有边相连时,则graph[v][w]和graph[w][v]为true,否则为false。

    但是这种方法需要占用的空间比较大,因为稀疏图更常见,这就导致了很多空间的浪费。V*V的矩阵很多时候我们是不能接受的。


    2.边的数组

    我们可以使用一个数组来存放所有的边,这样的话数组的大小仅有E。但是因为我们的操作总是需要访问某个顶点的相邻节点,对于这种数据类型,要访问相邻节点的话必须遍历整个数组,造成效率的低下,所以我们在这里也不使用这个数据结构。


    3.邻接表数组

    我们使用一个链表数组来表示,数组中每个元素都是链表表头,链表中存放对应下标的节点所连接的边。

    这种数据结构使用的空间为V+E。并且可以相当方便的获取相邻节点。

    如图:




    实现如下:

    import java.util.ArrayList;
    import java.util.List;
    
    public class Graph {
    	private List<Integer>[] adj; // 邻接表
    	private int V;	// 顶点数目
    	private int E;	// 边的数目
    	
    	public Graph(int V) {
    		this.V = V;
    		adj = (List<Integer>[])new List[V];
    		E = 0;
    		for (int i = 0; i < V; i++) {
    			adj[i] = new ArrayList<Integer>();
    		}
    	}
    	
    	public void addEdge(int v, int w) {
    		adj[v].add(adj[v].size(), w);
    		adj[w].add(adj[w].size(), v);
    		E++;
    	}
    	
    	public List<Integer> adj(int v) {
    		return adj[v];
    	}
    	
    	public int V() {
    		return V;
    	}
    	
    	public int E() {
    		return E;
    	}
    	
    	public String toString() {
    		String s = V + " 个顶点, " + E + " 条边\n";
    		for (int i = 0; i < V; i++) {
    			s += i + ": ";
    			for (Integer node : adj(i)) {
    				s += node + " ";
    			}
    			s += "\n";
    		}
    		return s;
    	}
    
    }
    

    到此为止,我们已经完成了图的表示。


    有了表示,我们就需要使用图完成一些简单的应用。

    例如,图的搜索,图的连通分量,图是否有环等等。


    首先我们来实现图的搜索。

    我们在这里实现一个模板。并不实际进行搜索。

    目标:给定一个起点,在图中进行搜索。

    方案:1.深度优先搜索 2.广度优先搜索。


    深搜

    原理:

    这里打个比喻,搜索图中所有的节点,就像走迷宫一样,需要探索迷宫中所有的通道。探索迷宫所有的通道,我们需要什么呢?

    1.我们需要选择一条没有标记的路,并且一边走一遍铺上一条绳子。

    2.标记走过的路。

    3.当走到一个标记的地方时,我们需要回退,根据绳子回退到上一个地方。

    4.当回退的地方没有可以走的路了,就要继续回退。

    也就是说,首先我们需要一直走下去,但是我们一边走就要一边做标记,如果走不下去了,就回退,回退到没有被标记的的路。循环往复,我们就能探索整个图了。


    实现:

    import java.util.Stack;
    
    public class DepthFirstSearch {
    	private boolean[] isMarked;
    	private int begin;
    	private int count;
    	private Integer[] edgeTo; 
    	
    	public DepthFirstSearch(Graph g, int begin) {
    		isMarked = new boolean[g.V()];
    		edgeTo = new Integer[g.V()];
    		count = 0;
    		this.begin = begin;
    		dfs(g, begin);
    	}
    	
    	public void dfs(Graph g, int begin) {
    		isMarked[begin] = true;
    		for (Integer i : g.adj(begin)) {
    			if (!isMarked[i]) {
    				edgeTo[i] = begin;
    				count++;
    				dfs(g, i);
    			}
    		}
    	}
    	
    	
    	public boolean hasPath(int v) {
    		return isMarked[v];
    	}
    	
    	public int count() {
    		return count;
    	}
    	
    	public String pathTo(int v) {
    		if (!hasPath(v)) return "";
    		Stack<Integer> stack = new Stack<>();
    		stack.push(v);
    		for (int i = v; i != begin; i = edgeTo[i]) {
    			stack.push(edgeTo[i]);
    		}
    		
    		return stack.toString();
    	}
    	
    	
    }
    
    我们需要一个数组来标记某个节点是否已经走过了,如果走过了,我们就不会再走了。
    并且我们有一个数组去保存是从哪个节点到达当前节点。这样,我们往回追朔的时候,就可以找到一条路径了。

    这是一个模板,并没有具体的搜索某个节点,而是将所有节点都搜索了一遍,在实际过程中,我们可以判断节点是否找到,找到就停止了。


    对于无向图来说,深搜虽然可以找到一条从v到w的路径,但是这条路径是否是最优的并不是可靠的,往往都不是。

    如果我们希望找到一条最短的路径,我们就应该使用广搜。

    广搜

    原理:

    广搜并不是先一条路走到黑,而是慢慢的根据距离进行搜索。例如,一开始先根据距离是1进行搜索,先搜索所有距离为1的地方。如果没找到,再搜索距离为2的地方。以此类推。

    如果说深搜是一个人在迷宫中搜索,那么广搜就是一组人在朝着各个方向进行搜索。当然不是效率比较高的意思,只是比喻而已。


    实现:

    import java.util.LinkedList;
    import java.util.Queue;
    import java.util.Stack;
    
    public class BreadthFirstSearch {
    	
    	private boolean[] isMarked;
    	private Integer[] edgeTo;
    	private int begin;
    	private int count; // 多少个点连通
    	
    	public BreadthFirstSearch(Graph g, int begin) {
    		isMarked = new boolean[g.V()];
    		edgeTo = new Integer[g.V()];
    		this.begin = begin;
    		count = 0;
    		bfs(g, begin);
    	}
    	
    	private void bfs(Graph g, int begin) {
    		Queue<Integer> queue = new LinkedList<>();
    		isMarked[begin] = true;
    		queue.offer(begin);
    		while (!queue.isEmpty()) {
    			Integer temp = queue.poll();
    			for (Integer i : g.adj(temp)) {
    				if (!isMarked[i]) {
    					isMarked[i] = true;
    					count++;
    					edgeTo[i] = temp;
    					queue.offer(i);
    				}
    			}
    		}
    	}
    	
    	public boolean hasPath(int v) {
    		return isMarked[v];
    	}
    	
    	public int count() {
    		return count;
    	}
    	
    	public String pathTo(int v) {
    		if (!hasPath(v)) return "";
    		Stack<Integer> stack = new Stack<>();
    		stack.push(v);
    		for (int i = v; i != begin; i = edgeTo[i]) {
    			stack.push(edgeTo[i]);
    		}
    		
    		return stack.toString();
    	}
    
    }
    


    其实广搜和深搜的不同就在于搜索规则的不同,深搜使用的是stack的LIFO(后进先出)的思想,总是搜索最新的节点。而广搜则是使用queue的FIFO(先进先出)的规则。

    就如同上面的一样,节点进入队列的顺序是根据距离的,所以我们就可以实现慢慢范围的扩大搜索。

    同样的,我们也标记了进入节点的前一个节点,用来追踪路径。因为我们是根据范围搜索的,所以得到的就是最短路径。


    我们可以使用广搜和深搜来实现很多应用,例如是否有环,是否是二部图等等。这里我们就不展开了。


    我们上面的图的节点都是以数字作为标记的,而对于实际应用来讲,图的节点一般都不会是数字,而是String类型的字符串等。

    要实现这种符号图,我们只需要将我们的代码进行一些扩展,使用符号表的方法,将字符串映射到某个整数上就可以了。

    例如:


    我们只需要在将字符串映射得到一个数字,也就是使用散列表的方式,存储成键值对,就可以继续使用上面的代码了。

    展开全文
  • 图论算法——无向图的连通分量

    万次阅读 多人点赞 2019-05-22 19:19:37
    引言 深度优先搜索的一个直接应用就是找出一幅的所有连通分量。

    引言

    深度优先搜索的一个直接应用就是找出一幅图的所有连通分量。

    在深度优先搜索的递归调用期间,只要是某个顶点的可达顶点都能在一次递归调用期间访问到。

    有关图的概念可参考博文数据结构之图的概述

    连通分量

    连通分量:不连通的图是由2个或者2个以上的连通子图组成的。这些不相交的连通子图称为图的连通分量。

    比如下图中有四个连通分量

    lor_FFFFFF,t_70)

    代码

    通过id[]标识连通分量,同一连通分量的count值相同,count初始化为0

    for (int s = 0; s < g.vertexNum(); s++) {
       if (!marked[s]) {
            //s的一次递归调用能访问所有与它连通的顶点
            dfs(g,s);
            //到这里说明s的连通顶点已经访问完毕
            count++;
        }
    }
    

    实现如下:

    package com.algorithms.graph;
    
    import com.algorithms.queue.Queue;
    
    /**
     * 计算无向图的连通分量
     * @author yjw
     * @date 2019/5/22/022
     */
    public class CC {
        private boolean[] marked;
        /**
         * 标识连通分量,同一连通分量的值相同
         * 0:第一个连通分量
         * 1:第二个连通分量
         * ...
         *
         * 值为0到count - 1之间
         */
        private int[] id;
        /**
         * 连通分量数
         */
        private int count;
    
        public CC(Graph g) {
            marked = new boolean[g.vertexNum()];
            id = new int[g.vertexNum()];
            for (int s = 0; s < g.vertexNum(); s++) {
                if (!marked[s]) {
                    //s的一次递归调用能访问所有与它连通的顶点
                    dfs(g,s);
                    //到这里说明s的连通顶点已经访问完毕
                    count++;
                }
            }
        }
    
        private void dfs(Graph g,int v) {
            marked[v] = true;
            id[v] = count;//标识连通分量
            for (int w: g.adj(v)) {
                if (!marked[w]) {
                    dfs(g,w);
                }
            }
        }
    
        public boolean connected(int v,int w) {
            return id[v] == id[w];
        }
    
        public int id(int v) {
            return id[v];
        }
    
        public int count() {
            return count;
        }
    
        @SuppressWarnings("unchecked")
        public void print() {
            System.out.println(count + " components");//count个连通分量
    
            Queue<Integer>[]components = (Queue<Integer>[]) new Queue[count];
            for (int i = 0; i < components.length; i++) {
                components[i] = new Queue<>();
            }
    
    
            for (int i = 0; i < id.length; i++) {
                components[id(i)].enqueue(i);
            }
            for (Queue<Integer> queue : components) {
                System.out.println(queue);
            }
    
        }
    
        public static void main(String[] args) {
            Graph g = new Graph(10);
            g.addDiEdges(0,1,2);
            g.addDiEdges(4,5,6);
            g.addEdge(5,7);
            g.addEdge(8,9);
    
            //System.out.println(g);
            CC cc = new CC(g);
            cc.print();
        }
    }
    
    

    其中QueueStack的实现见 栈和队列的实现

    我们对示例图计算连通分量,得到输出:

    4 components
    [0 1 2]
    [3]
    [4 5 6 7]
    [8 9]
    
    展开全文
  • 无向图弗洛伊德算法求最短路径 输出路径

    千次阅读 多人点赞 2020-07-08 10:27:55
    弗洛伊德算法求最短路径 输出路径 弗洛伊德算法其实比较好理解 这里我用邻接矩阵的储存方法来写 一.算法思想 ...由于是,自己到自己是 0 剩下的边的权值我们赋值为MAX for(int i=1; i<=n; i++)
  • 它以为研究对象。图论中的是由若干给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些事物之间的某种特定关系 ,用点代表事物,用连接两点的线表示相应两个事物间具有这种关系。
  • 比如个这样的无向图(看起来很像二叉树吧,其实二叉树是一种特殊的图),通过邻接链表表示如下: 我们通过索引表示顶点,索引指向的为一个链表(表示该顶点相邻的所有顶点,比如顶点2相邻的顶点为:0,1,3)。...
  • C语言 最短路径之Dijkstra算法 无向图

    千次阅读 2018-06-12 15:39:19
    Dijkstra算法是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。 实现过程 Dijkstra算法和Prim算法非常相似(参照链接:C语言 Prim算法和Kruskal算法的实现和证明) 从上面...
  • 特点:是以起始点为中心外层层扩展(?) 基本思想: 指定起点s,即从顶点s开始计算。 引进两个集合S和U。 S:记录已求出最短路径的顶点(以及相应的最短路径长度), U:记录还未求出最短路径的顶点(以及该...
  • 带权图的多种算法向图,无向图,Dijkstra算法,到每个顶点的最短距离,佛洛依德算法(Floyd),找出每对顶点的最短路径,带权重无向图最小生成树,prim算法,Kruskal算法求最小生成树)java实现, 注释,简单...
  • Floyd算法无向图最小环

    千次阅读 2017-07-13 01:19:02
    算法适用于无向图,而 向图的最小环 ,实际上就是初始化所有点为inf(包括graph[i][i]),然后跑一个普通Floyd即可,寻找最小的graph[i][i]就是最小环。 代码(以POJ-1734为例): #include using ...
  • 无向图求最短路径——Dijkstra(迪杰斯特拉算法) Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。 问题描述: 如下无向图, 若从顶点1开始计算到其余各顶点的...
  • 无向图最短路径dijkstra算法

    万次阅读 2016-05-19 08:42:48
    // q指向p,这样表示无向图 } }//将数据更加精确 for(int i=1; i; ++i) dist[i] = maxint; /* for(int i=1; i; ++i) { for(int j=1; j; ++j) printf("%8d", c[i][j]); printf("\n"); } */ ...
  • 有向无的最短路径求解算法

    千次阅读 2018-10-29 21:45:34
    对于最短路径问题,Dijkstra算法只能求解所有边的权值都是非负的加权有向图,而Bellman-Ford算法虽然可以求解负权值边的图的最短路径,但效率并不高。对于环图,下面这种基于Dijkstra和拓扑排序的算法可以在...
  • 图论算法——环和有向无

    万次阅读 多人点赞 2019-05-20 18:21:06
    有向图相关的实际应用中,向环特别重要。一幅图可能含有大量的环,通常,我们一般只关注它们是否存在。 调度问题 给定一组任务并安排它们的执行顺序,限制条件为这些任务的执行方法、起始时间以及任务的消耗等...
  • python 无向图最短路径之Dijkstra算法

    千次阅读 2019-04-29 17:17:10
    无向图的邻接矩阵是对称矩阵,向图的邻接矩阵不是对称矩阵。 共有5个顶点(nodes),7条边(vertices) 其邻接矩阵为:num_node*num_node,矩阵中的数值表示两个相连接的节点的边的权值 节点 A B C ...
  • 给出了一个具有n个顶点和m个边的有向图G。任务是找出图中最长向路径的长度。 注:向路径的长度是指路径中的边数。 例如:下图中4个顶点,5条边。 最长的向路径的长度为 3。 从1到3,到2,到4。 算法...
  • 无向图向图的深度优先与广度优先算法
  • Dijkstra 最短路径算法详解 无向图

    万次阅读 多人点赞 2018-04-03 22:23:55
     首先,因为A是起点,所以我们把对于每个点都个参数,相对于A的距离,默认除了A到A为0,其他都是无穷大。 从起点A开始,我们更新与A相连通的点到A的距离,并把A点标记。如: 我们遍历一次所有点与A的距离,...
  • 无向图最短路径问题 Dijkstra算法

    千次阅读 2019-03-02 14:50:13
    无向图最短路径问题 思路: 具体的思路,半天也说不清楚,所以我在这里附上视屏讲解链接,里面的讲解十分清晰。 [Djkstra算法讲解](http://www.iqiyi.com/w_19ru9nrxol.html 代码实现及讲解 #include &amp;...
  • 找出无向图中所有的环的算法

    万次阅读 多人点赞 2016-07-13 16:25:47
    本文给出了一个找到无向图中所有的环的递归算法,该算法是基于DFS(深度优先搜索)的,大概的思路是:在深度优先搜索无向图的过程中,当遇到起始点的时候,会认定为出现环(在本文中只是找出了无向图中所有的长度...
  • python 无向图最短路径之floyd算法

    千次阅读 2019-04-30 17:36:47
    ... floyd算法:能够找到中任意两个节点之间的最短路径,时间复杂度为O(n**3),其中n表示中的节点数 算法思路: 假设当前的 通过floyd算法计算中任意两个节点之间的...
  • [详解]STOER-WAGNER算法求解无向图最大流最小割

    千次阅读 多人点赞 2017-09-11 17:37:40
    图文详解如何使用STOER-WAGNER算法求解无向图的最小割
  • 无向图的几个基本算法应用

    千次阅读 2016-12-26 20:03:48
    简介  最近在看一些相关的问题。实际上关于相关的研究和问题已经非常多了。在前面的几篇文章里,我也...这篇文章里重点要讨论解决的几个问题分别是检测的连通性、中间环的检测和二分的检测。  
  • 我用Dijkstra算法,写了一个无环向图/无向图(多加一条相反的路径仅此而已) 的最短路径问题的解决方案。如果是无向图也很简单,把每个无向的edge拆开成两个向的就可以解决了。为了每次弹出正确的端点,我也实现...
  • 无向图最小生成树(prim算法

    千次阅读 2016-04-15 12:47:57
    输入 第1行:2个数N,M中间用空格分隔,N为点的数量,M为边的数量。(2  N  1000, 1  M  50000) 第2 - M + 1行:每行3个数S E W,分别表示M条边的2个顶点及权值。(1  输出 ...4
  • 图论算法——dfs求向图和无向图两点间所有路径

    千次阅读 多人点赞 2019-06-12 09:28:24
    DFS作为搜索算法,最常用于,对图的遍历,探寻路径,甚至是求一些情况下的最短路。我在这里就介绍一下dfs求两点的的所有路径,这个算法最开始在数据结构大作业里面用到了,当时费了一番劲写出来后,就想oj题里面...
  • 无向图最小环算法-floyd

    千次阅读 2016-07-18 09:05:42
    floyd算法 i 出发返回 i 的最小环=min{d[i][j]+map[i][k]+map[k][j]};for(k=1;k;k++) { for(i=1;i
  • 算法无向图中的环

    千次阅读 2018-10-29 20:02:47
    无向图中的环 判断一个具有n个结点m条边的无向图中是否包含环,如果包含则输出其中一个环,要求时间复杂度为O(m+n)。可以根据对图的深度优先算法(DSF)拓展来求解此题。主要过程如下: 用数组VST[]记录图中访问过的...
  • 最短路径之Dijkstra(迪杰斯特拉)算法无向图

    万次阅读 多人点赞 2018-01-08 16:52:54
    Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心外层层扩展,直到扩展到终点为止。由for循环可知,其时间复杂度是O(n^2)。 原理 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 332,851
精华内容 133,140
关键字:

无向图路有选择算法