java图的基础算法

java图的基础算法

遍历器的实现

public class Iterator {

	//传入顶点坐标
    // 返回图中一个顶点的所有邻边
    // 由于java使用引用机制,返回一个Vector不会带来额外开销,
    public Iterable<Integer> adj(int v) {
        assert v >= 0 && v < n;
        return g[v];
    }

    // 返回图中一个顶点的所有邻边
    // 由于java使用引用机制,返回一个Vector不会带来额外开销,
    public Iterable<Integer> adj(int v) {
        assert v >= 0 && v < n;
        Vector<Integer> adjV = new Vector<Integer>();
        for(int i = 0 ; i < n ; i ++ )
            if( g[v][i] )
                adjV.add(i);
      //返回遍历器与遍历对象
        return adjV;
    }
	
	
	//根据遍历器获取遍历的值
    for( int i: G.adj(v) ){
        if( !visited[i] )
            dfs(i);
    }
}

图深度优先获取图的连通分量

public class Components {
private int[] id;           // 每个节点所对应的联通分量标记
Graph G;                    // 图的引用
private boolean[] visited;  // 记录dfs的过程中节点是否被访问
private int ccount;         // 记录联通分量个数	

//从一个点开始找与它相邻的点,然后从这个点不停的往下尝试,直到尝试不下去这条路就已经走完了,退回到一开始的节点
void dfs( int v ){
    visited[v] = true;
    id[v] = ccount;
    for( int i: G.adj(v) ){
        if( !visited[i] )
            dfs(i);
    }
}	

// 算法初始化
G = graph;
visited = new boolean[G.V()];
id = new int[G.V()];
ccount = 0;
for( int i = 0 ; i < G.V() ; i ++ ){
    visited[i] = false;
    id[i] = -1;
}

//连通分量与连通分量没有任何的边相连,找图中所有没有遍历的点	
for( int i = 0 ; i < G.V() ; i ++ )
	//记录图中的环,	每一个点是否遍历过,如果遍历过下面的遍历不需要走了	
    if( !visited[i] ){
        dfs(i);
        ccount ++;
    }
}
}

寻路

public class Path {
    private Graph G;   // 图的引用
    private int s;     // 起始点
    private boolean[] visited;  // 记录dfs的过程中节点是否被访问
    private int[] from;         // 记录路径, from[i]表示查找的路径上i的上一个节点

    visited = new boolean[G.V()];
    from = new int[G.V()];
    for( int i = 0 ; i < G.V() ; i ++ ){
        visited[i] = false;
        from[i] = -1;
    }   
	//遍历的同时存是从哪个节点过来的
    // 图的深度优先遍历
    private void dfs( int v ){
        visited[v] = true;
        for( int i : G.adj(v) )
            if( !visited[i] ){
                from[i] = v;
                dfs(i);
            }
    }	
    
	//倒推出从0到任意节点的路径
    // 查询从s点到w点的路径, 存放在vec中
    Vector<Integer> path(int w){

        assert hasPath(w) ;

        Stack<Integer> s = new Stack<Integer>();
        // 通过from数组逆向查找到从s到w的路径, 存放到栈中
        int p = w;
        while( p != -1 ){
            s.push(p);
            p = from[p];
        }

        // 从栈中依次取出元素, 获得顺序的从s到w的路径
        Vector<Integer> res = new Vector<Integer>();
        while( !s.empty() )
            res.add( s.pop() );

        return res;
    }	
	
}

广度优先与最短路径

public class ShortestPath {
	
    private Graph G;   // 图的引用
    private int s;     // 起始点
    private boolean[] visited;  // 记录dfs的过程中节点是否被访问
    private int[] from;         // 记录路径, from[i]表示查找的路径上i的上一个节点
    private int[] ord;          // 记录路径中节点的次序。ord[i]表示i节点在路径中的次序。	

	
	
    visited = new boolean[G.V()];
    from = new int[G.V()];
    ord = new int[G.V()];
    for( int i = 0 ; i < G.V() ; i ++ ){
        visited[i] = false;
        from[i] = -1;
        ord[i] = -1;
    }
    this.s = s;

    // 无向图最短路径算法, 从s开始广度优先遍历整张图
    Queue<Integer> q = new LinkedList<Integer>();

    q.add(s);
    visited[s] = true;
    ord[s] = 0;
    while( !q.isEmpty() ){
    	//从队列中取出元素相当于遍历了这个节点,一次将图中所有相邻的节点全部遍历
        int v = q.remove();
        for( int i : G.adj(v) )
        	//再去遍历相邻节点的相邻节点,加入到队列的元素不用再遍历了
            if( !visited[i] ){
                q.add(i);
                visited[i] = true;
                from[i] = v;
                ord[i] = ord[v] + 1;
            }
    }
}	
    
// 查询从s点到w点是否有路径
public boolean hasPath(int w){
    assert w >= 0 && w < G.V();
    return visited[w];
}

// 查询从s点到w点的路径, 存放在vec中
public Vector<Integer> path(int w){

    assert hasPath(w) ;

    Stack<Integer> s = new Stack<Integer>();
    // 通过from数组逆向查找到从s到w的路径, 存放到栈中
    int p = w;
    while( p != -1 ){
        s.push(p);
        p = from[p];
    }

    // 从栈中依次取出元素, 获得顺序的从s到w的路径
    Vector<Integer> res = new Vector<Integer>();
    while( !s.empty() )
        res.add( s.pop() );

    return res;
}	
	
}

Kruskal计算有权无向图的最小生成树

public class Krusk {
	//有权图
	static List<List<Double[]>> totallist;
	//最小生成树
	static List<Double[]> minTree;
	static int[] rank;
	static int[] parent;
	static double mstWeight;
	
	totallist = new ArrayList<>();
	minTree = new ArrayList<>();
	rank = new int[N];
	parent = new int[N];
	for(int i=0;i<N;i++) {
		rank[i] = 1;
		parent[i] = i;
		totallist.add(new LinkedList<>());
	}	

	//找到最小队列的中权值最小的一条边
	//最小堆
	PriorityQueue<Double[]> queue = new PriorityQueue<>(new Comparator<Double[]>() {
		@Override
		public int compare(Double[] o1, Double[] o2) {
			// TODO Auto-generated method stub
			return o1[2].compareTo(o2[2]);
		}
	});	
	for(int i=0;i<M;i++) {
		int s = scanner.nextInt();
		double ds = Double.valueOf(s);
		int e = scanner.nextInt();
		double de = Double.valueOf(e);
		double w = scanner.nextDouble();
		totallist.get(s).add(new Double[] {ds,de,w});
		if(s!=e) {
			totallist.get(e).add(new Double[] {de,ds,w});
		}
		queue.offer(new Double[] {ds,de,w});
		/*if(s<e) { //s<=e
			queue.offer(new Double[] {ds,de,w});
		}*/
	}	
	
	//判断是否形成环,如果不形成环放入最小生成树
	while(!queue.isEmpty()) {
		// 从最小堆中依次从小到大取出所有的边
		Double[] top = queue.poll();
		int startIndex = top[0].intValue();
		int toIndex = top[1].intValue();
		// 如果该边的两个端点是联通的, 说明加入这条边将产生环, 扔掉这条边
		if(isConnected(startIndex, toIndex)) {
			continue;
		}
		// 否则, 将这条边添加进最小生成树, 同时标记边的两个端点联通
		minTree.add(top);
		unionElement(startIndex,toIndex);
	}
	mstWeight = minTree.get(0)[2];
	for(int i=1;i<minTree.size();i++) {
		mstWeight = mstWeight+minTree.get(i)[2];
	}
	System.out.println(mstWeight);
	
}
private static boolean isConnected(int v,int w) {
	return find(v)==find(w);
}
private static int find(int v) {
	// TODO Auto-generated method stub
	while(v!=parent[v]) {
		parent[v] = parent[parent[v]];
		v = parent[v];
	}
	return v;
}
private static void unionElement(int v,int w) {
	int vRoot = find(v);
	int wRoot = find(w);
	if(vRoot==wRoot) {
		return;
	}
	if(rank[vRoot]<rank[wRoot]) {
		parent[vRoot] = wRoot;
	}else if(rank[wRoot]<rank[vRoot]) {
		parent[wRoot] = vRoot;
	}else {
		parent[vRoot] =wRoot;
		rank[wRoot]+=1;
	}
}	
	
}

Dijkstra有向带权图单源最短路径问题

public class Dijkstra {
    static List<List<int[]>> totallist;
    static int[] distTO; //distTo[i]存储从起始点s到i的最短路径长度
	static boolean[] visited;
	static int[][] from;  //from[i]记录最短路径中, 到达i点的边是哪一条,可以用来恢复整个最短路径
	static int N;
	static int start;
	static final int _INTMAX_ = 2087654321;

	 N = scanner.nextInt();
	int M = scanner.nextInt();
	totallist = new ArrayList<>();
	for(int i=0;i<N;i++) {
		totallist.add(new LinkedList<>());
	}
	for(int i=0;i<M;i++) {
	    int s = scanner.nextInt();
	    int e = scanner.nextInt();
	    int w = scanner.nextInt();
	    totallist.get(s).add(new int[] {s,e,w});
	    //无向图
	    //totallist.get(e).add(new int[] {e,s,w});
	}
	//
	//如果没有目的地,要执行到最后dijkstra(start,-1);
	 start = 0;
	int dest = -1;
	dijkstra(start,dest);
	int minRoute = distTO[4];
	
	//访问顶点所有的临边,得到暂时的最短的一条路径
	private static void dijkstra(int start, int dest) {
		// TODO Auto-generated method stub
		distTO = new int[N];
		visited = new boolean[N];
		from = new int[N][3];
		for(int i=0;i<N;i++) {
			distTO[i] = _INTMAX_;
		}
		//最小堆
		PriorityQueue<int[]> queue =new PriorityQueue<>(new Comparator<int[]>() {
 
			@Override
			public int compare(int[] o1, int[] o2) {
				// TODO Auto-generated method stub
				return o1[1]-o2[1];
			}
		});
 
		
		distTO[start] = 0;
		from[0]= new int[] {start,start,0}; //起始点到起始点的距离为0
		queue.add(new int[] {start,0});
	
	//访问从最短的一条路径为中专到其他顶点的路径长度,如果获得更加短的一条路径,则为最短路径
	    while(!queue.isEmpty()) { //找离起始点最短的边
	    	int[] cur = queue.poll();
	    	visited[cur[0]] = true;
	    	if(cur[0]==dest) break; //找到终点
	    	for(int[] item:totallist.get(cur[0])) {
	    		if(!visited[item[1]]) {
	    			int startIndex = item[0];
		    		int toIndex = item[1];
		    		int weight = item[2];
		    		weight+=cur[1];
		    		if(weight<distTO[toIndex]) {
		    			distTO[toIndex] = weight;
		    			from[toIndex] = item; 
		    			queue.add(new int[] {toIndex,weight});
		    		}
	    			
	    		}
	    	}
	    }   
	}
	
	private static List<int[]> shortestPath(int w){
		Deque<int[]> deque = new LinkedList<>();
		int[] e = from[w];
		while(e[0]!=start) {
			deque.push(e);
			e = from[e[0]];
		}
		deque.push(e);
		List<int[]> res = new ArrayList<>();
		while(!deque.isEmpty()) {
			int[] retItem = deque.pop();
			res.add(retItem);
		}
		return res;
	}
	private static void showPath(int w) {
		List<int[]> path = shortestPath(w);
		for(int i =0;i<path.size();i++) {
			System.out.println(path.get(i)[0]+"->");
			if(i==path.size()-1) {
				System.out.println(path.get(i)[1]);
			}
		}
	}	

}

Bellman-Ford计算负权边单源最短路径

public class BellmanFora {
	static List<List<int[]>> totallist;
	static int start;
	static int[] distTo;
	static int[][] from;
    static int N;	
	totallist = new ArrayList<>();
	distTo = new int[N];
	from = new int[N][3];
	for(int i=0;i<N;i++) {
		totallist.add(new LinkedList<>());
		from[i] = null;
	}
	for(int i=0;i<M;i++) {
		int s = scanner.nextInt();
		int e = scanner.nextInt();
		int w = scanner.nextInt();
		totallist.get(s).add(new int[] {s,e,w});
	}
	
	// 设置distTo[start] = 0, 并且让from[start]不为空, 表示初始s节点可达且距离为0
	int start = 0;
	distTo[start] = 0;
	from[start] = new int[] {start,start,0};	
	
    //对所有的点进行两次松弛操作,找到从原点开始经过两条边得到的最短路径是否权值更小
    // Bellman-Ford的过程
    // 进行V-1次循环, 每一次循环求出从起点到其余所有点, 最多使用pass步可到达的最短距离
	for(int pass=1;pass<N;pass++) {
        // 每次循环中对所有的边进行一遍松弛操作
        // 遍历所有边的方式是先遍历所有的顶点, 然后遍历和所有顶点相邻的所有边
		for(int k=0;k<N;k++) {
			for(int[] item:totallist.get(k)) {
				int startIndex = item[0];
				int toIndex = item[1];
				int weight = item[2];
				// 对于每一个边首先判断e->v()可达
                // 之后看如果e->w()以前没有到达过, 显然我们可以更新distTo[e->w()]
                // 或者e->w()以前虽然到达过, 但是通过这个e我们可以获得一个更短的距离, 即可以进行一次松弛操作, 我们也可以更新distTo[e->w()]
				if(from[startIndex]!= null&&(from[toIndex]== null||distTo[startIndex]+weight<distTo[toIndex])) {
					distTo[toIndex] = distTo[startIndex]+weight;
					from[toIndex] = item;
				}
			}
		}
	}	
	//判断是否有负权边,如果存在从一点到另外一点的最短路径,最多经过V个顶点有V-1条边	
	//判断是否有负权环
	boolean hasNegativeCycle = detectNegativeCycle();
	// 返回从s点到w点的最短路径长度
	int end = 4;
	int minRoute = distTo[end];
	System.out.println(hasNegativeCycle+" "+minRoute);
	for(int m=1;m<N;m++) {
		 System.out.println("Shortest Path to " + m + " : " + distTo[m]);
		showPath(m);
	}
	
}

// 判断图中是否有负权环
private static boolean detectNegativeCycle() {
	// TODO Auto-generated method stub
	for(int i =0;i<N;i++) {
		for(int[] item:totallist.get(i)) {
			if(from[item[0]].length>0&&distTo[item[0]]+item[2]<distTo[item[1]]) {
				return true;
			}
		}
	}
	return false;
}	
}

猜你喜欢

转载自blog.csdn.net/qq_35029061/article/details/94266775
今日推荐