Java算法学习8——有向环及拓扑排序

一、有向环

有向环的寻找其实很简单,直接在DFS的基础上修改就行,即当识别遇到marked已经为true时,就可以返回了,因为此时是有向的,然后还可以用edgeTo数组将有向环打印出来

二、拓扑排序

1.描述
输入格式为一行两个数,其中第一行的第一个数为顶点个数,第二个数为边数N,然后接下来的N行中的第一个为起点顶点编号,第二个为终点编号。最后按照拓扑排序输出各个图的顶点。

2.算法实现思路

拓扑排序,其实就是寻找一个入度为0的顶点,该顶点是拓扑排序中的第一个顶点序列,将之标记删除,然后将与该顶点相邻接的顶点的入度减1,再继续寻找入度为0的顶点,直至所有的顶点都已经标记删除或者图中有环。

从上可以看出,关键是寻找入度为0的顶点。

一种方式是遍历整个图中的顶点,找出入度为0的顶点,然后标记删除该顶点,更新相关顶点的入度,由于图中有V个顶点,每次找出入度为0的顶点后会更新相关顶点的入度,因此下一次又要重新扫描图中所有的顶点。故时间复杂度为O(V^2)

由于删除入度为0的顶点时,只会更新与它邻接的顶点的入度,即只会影响与之邻接的顶点。但是上面的方式却遍历了图中所有的顶点的入度。

改进的另一种方式是:先将入度为0的顶点放在栈或者队列中。当队列不空时,删除一个顶点v,然后更新与顶点v邻接的顶点的入度。只要有一个顶点的入度降为0,则将之入队列。此时,拓扑排序就是顶点出队的顺序。该算法的时间复杂度为O(V+E)

3.拓扑排序的实现方法

该算法借助队列来实现时,感觉与 二叉树的 层序遍历算法很相似啊。说明这里面有广度优先的思想。

第一步:遍历图中所有的顶点,将入度为0的顶点 入队列。

第二步:从队列中出一个顶点,打印顶点,更新该顶点的邻接点的入度(减1),如果邻接点的入度减1之后变成了0,则将该邻接点入队列。

第三步:一直执行上面 第二步,直到队列为空。

4.实现代码1——用Map

(1)先实现图的存储结构
注意要学会这个存储的方法,该存储方法的实现内容如下:

①DirectedGraph这个大类里面一共要包含四个小类,分别是:DirectedGraph构造函数、顶点类Vertex、边类Edge、构图类bulidGraph,注意这四个类都要设成private的形式

②构造函数DirectedGraph类中需要建立一个双向链式哈希循环表作为图的存储结构,即LinkedHashmap

③Vertex类需要三个部分:顶点标识符、下一个连接的顶点(可以用LinkedList)来表示、入度数

④Edge类,这个类是给Vertex中的下一个连接的顶点的这个部分提供的,因为入度的顶点号就是等于边号

⑤buildGraph类,就是用来读取输入,然后将将输入分割为起点和终点,对他们分别在Hsapmap中进行扫描,如果存在了,那么对于起点来说就直接加一个边集就行,对于终点来说,就是要入度+1。如果不存在,那么就直接Put到HashMap里面

该图的总的存储结构如下:
在这里插入图片描述

实现代码如下:

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 DirectedGraph {

    private class Vertex{
        private String vertexLabel;// 顶点标识
        private List<Edge> adjEdges;//注意这个是Edge类
        private int inDegree;// 该顶点的入度

        public Vertex(String verTtexLabel) {
            this.vertexLabel = verTtexLabel;
            inDegree = 0;
            adjEdges = new LinkedList<Edge>();
        }
    }

    private class Edge {
        private Vertex endVertex;

        // private double weight;
        public Edge(Vertex endVertex) {
            this.endVertex = endVertex;
        }
    }

    private Map<String, Vertex> directedGraph;

    public DirectedGraph(String a,String b) {
        directedGraph = new LinkedHashMap<String, DirectedGraph.Vertex>();
        buildGraph();
    }

    private void buildGraph() {
        Scanner cin=new Scanner(System.in);
        String n=cin.nextString();
        Vertex startNode, endNode;
        String startNodeLabel, endNodeLabel;
        Edge e;
        for (int i = 0; i < n; i++) {
            String a=String.valueOf(cin.nextInt());
            String b=Stirng.valueOf(cin.nextInt());
            startNodeLabel = a;
            endNodeLabel = b;
            startNode = directedGraph.get(startNodeLabel);
            if(startNode == null){
                startNode = new Vertex(startNodeLabel);
                directedGraph.put(startNodeLabel, startNode);
            }
            endNode = directedGraph.get(endNodeLabel);
            if(endNode == null){
                endNode = new Vertex(endNodeLabel);
                directedGraph.put(endNodeLabel, endNode);
            }

            e = new Edge(endNode);//每读入一行代表一条边
            startNode.adjEdges.add(e);//每读入一行数据,起始顶点添加一条边
            endNode.inDegree++;//每读入一行数据,终止顶点入度加1
        }
    }

(2) 拓扑排序实现如下

拓扑排序的实现步骤如下:

①先设置一个Vexrtex类的队列用来存放入度为0的顶点,以及设置一个Vertex类Colleection将LinkedHashMap中的value全部放进去

②利用迭代法对Collection进行一次扫描,将度数为0的放入队列。

③然后当队列不为空的时候,执行先poll,然后输出,然后再用迭代法去找这这元素的顶点链表,将相关顶点的入度减一,然后再去判断是否为0,如果有入度为0的话,则继续入队列

④最后判断是否有环,即如果最后输出的元素个数小于顶点个数的话,则必有环

代码实现如下:

public void topoSort() {
	int count=0;
	Queue<Vertex> queue=new LinkedList<Vertex>();
	Collection<Vertex> vertexs=directedGraph.value;
	
	for(Vertex vertex:vertexs){
		if(vertex.inDegree==0){
			queue.offer(vertex);
		}
	} 
	
	while(!queue.isEmpty){
		Vertex v=queue.poll();
		System.out.print(v.vertexlabel+" ");
		count++;
		for(Edge e:v.adjEdges){
			if (--e.endVertex.inDegree==0){
				queue.offer(e.endVertex);
			}
		} 
	}
	
	if(count!=directedGrapg.size()){
		System.out.print("Graph has circle");
	}
}

Ps:详情还可浏览如下链接:
https://www.cnblogs.com/hapjin/p/5432996.html

5.实现代码1——用List

输入格式:第一行有两个整型,第一个是顶点数,第二个是边数N,剩下的N行都有两个整型数,分别是起点和终点

输出格式:最后输出一串拓扑排序的数列

import java.util.*;


class DirectedGraph{
    private List<Vertex> list;

    class Vertex{
        private int Vertexlable;
        private List<Integer> nextVertex;
        private int inDegree=0;

        Vertex(int a){
            this.Vertexlable=a;
            nextVertex=new LinkedList<>();
        }

    }
    DirectedGraph(int numberof, int numberofedges){
        buildGraph(numberof,numberofedges);
    }

    private void buildGraph(int numberofvertex,int numberofedges){
        list=new ArrayList<>(numberofedges);
        for(int i=0;i<=numberofvertex+1;i++){
            Vertex c=new Vertex(0);
            list.add(c);
        }

        Scanner cin=new Scanner(System.in);
        for(int i=0;i<numberofedges;i++){
            int a=cin.nextInt();
            int b=cin.nextInt();
            Vertex K=list.get(a);
            K.Vertexlable=a;
            if(list.contains(K.Vertexlable)){

            }
            K.nextVertex.add(b);
            list.set(a,K);

            Vertex h=list.get(b);
            h.Vertexlable=b;
            h.inDegree++;
            list.set(b,h);

        }
    }

    public void toposort(int numberofvertex){
        int count=0;
        Queue<Vertex> queue=new LinkedList<>();
        for(Vertex v:list){
            if(v!=null&&v.Vertexlable>=1&&v.Vertexlable<=numberofvertex){
                if(v.inDegree==0){
                    queue.offer(v);
                }
            }
        }
        while(!queue.isEmpty()){
            Vertex x=queue.poll();
            System.out.print(x.Vertexlable+" ");
            count++;
            for(int i:x.nextVertex){
                Vertex o=list.get(i);
                o.inDegree--;
                list.set(i,o);
                if(o.inDegree==0){
                    queue.offer(o);
                }
            }
        }
        if(count!=numberofvertex){
            System.out.print("该图有环");
        }
    }
}

public class Toposort{

    public static void main(String []args){
        Scanner cin=new Scanner(System.in);
        int vertexs=cin.nextInt();
        int edges=cin.nextInt();
        DirectedGraph a=new DirectedGraph(vertexs,edges);
        a.toposort(vertexs);
    }
}

list的易错点详情请看:
https://blog.csdn.net/Warddamn/article/details/114768341

---------------------------3.29更新----------------------------------------

一、二维数组的Vector测试

import java.util.Scanner;
import java.util.Vector;

public class _testVector2 {
	static Vector<Integer>[] G;
	public static void main(String []args) {
		Scanner cin=new Scanner(System.in);
		int n=4;
		G=new Vector[n];//重点勿忘
		for(int i=0;i<n;i++) {
			G[i]=new Vector<Integer>();//重点勿忘
			int k=cin.nextInt();
			G[i].add(k);
		}
		for(int i=0;i<n;i++) {
			for(int j=0;j<G[i].size();j++) {
				System.out.print(G[i].elementAt(j));
			}
			System.out.println();
		}
	}

}

二、拓扑排序

import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
import java.util.Vector;

/*
 * 本节描述的是拓扑排序,拓扑排序主要是用来判断是否是有向无环图,采用的是邻接表来实现
 *知识点:学会构建vector数组的方法,即二维的vector 
 * Vector<Integer> []G=new Vector [manx];
 * for(int i=0;i<maxn;i++) { G[i]=new Vector<Integer>();}
 */
public class _拓扑排序 {
	static int maxn=1000;
	static Vector<Integer> G[];//邻接表
	static int n,m;static int []inDegree=new int [maxn];//顶点数和入度数组
	static boolean toposort() {
		int num=0;//记录入队列的数目
		Queue<Integer> q=new LinkedList<>();//度数为零的队列
		for(int i=0;i<n;i++) {
			if(inDegree[i]==0) {
				q.add(i);//结点进队列
			}
		}
		while(!q.isEmpty()) {
			int k=q.poll();
			num++;//进过队列的数目加一
			for(int i=0;i<G[k].size();i++) {
				inDegree[G[k].elementAt(i)]--;//这条边删了,那么对应的入度就要减一
				if(inDegree[G[k].elementAt(i)]==0) {
					q.add(G[k].elementAt(i));
				}
			}
			G[k].clear();//出边全部删掉
		}
		if(num==n) return true;//入队数等于节点数,则存在拓扑排序
		else return false;
	};
	
	public static void main (String []args) {
		Scanner cin=new Scanner(System.in);
		n=cin.nextInt();m=cin.nextInt();//顶点数和边数
		G=new Vector[n];//实例化勿忘!
		for(int i=0;i<n;i++) {
			G[i]=new Vector<Integer>();//构建一个vector类型的数组,里面的每一个元素都是一个Vector
		}
		for(int i=0;i<m;i++) {
			int l=cin.nextInt();int r=cin.nextInt();
			G[l].add(r);//有向图,这需要加入出边
			inDegree[r]++;
			//G[r].add(l); 无向图要加上这句
		}
		boolean reasult=toposort();
		System.out.print(reasult);
	}

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值