JAVA——图的遍历

1.图遍历的定义

从图中某个顶点出发访问遍图中的所有顶点,并且每个顶点仅仅被访问一次。
其中图的遍历分为两种,一种是图的深度优先遍历算法,一种是图的广度优先遍历算法。

2.连通图的深度优先遍历算法(DFS)和广度优先遍历算法(BFS)

图的深度遍历算法步骤:
(1)首先选定一个未被访问过的顶点V作为起始顶点(或者访问指定的起始顶点V),并将其标记为已访问过;

(2)然后搜索与顶点V邻接的所有顶点,判断这些顶点是否被访问过,如果有未被访问过的顶点,则任选一个顶点W进行访问;再选取与顶点W邻接的未被访问过的任一个顶点并进行访问,依次重复进行。当一个顶点的所有的邻接顶点都被访问过时,则依次回退到最近被访问的顶点。若该顶点还有其他邻接顶点未被访问,则从这些未被访问的顶点中取出一个并重复上述过程,直到与起始顶点V相通的所有顶点都被访问过为止。

(3)若此时图中依然有顶点未被访问,则再选取其中一个顶点作为起始顶点并访问之,转(2)。反之,则遍历结束。
图的广度遍历算法步骤:
图的广度优先遍历有点像树的层次遍历,首先把节点的相邻的节点全部存入队列。然后再出队,寻找该节点的相邻节点。
【实现代码】

package Graph;

import java.util.LinkedList;
import java.util.Queue;

//创建图类
class Graph_1{
	final static int INF=100000;
	final int max=100;
	//顶点坐标
	int[] vexs=new int[max];
	//矩阵
	int[][] edges=new int[max][max];
	
	//创建图的邻接矩阵
	public void createGraph(Graph_1 graph,int[][] A,int[] vs ){
		vexs=vs;
		for(int i=0;i<A.length;i++){
			for(int j=0;j<A.length;j++){
				graph.edges[i][j]=A[i][j];
			}
		}
	}
	
	//输出邻接矩阵
	public void print_Graph(Graph_1 graph){
		for(int i=0;i<graph.vexs.length;i++){
			for(int j=0;j<graph.vexs.length;j++){
				if(graph.edges[i][j]==INF){
					System.out.printf("%4s", "/");
				}else{
					System.out.printf("%4d", graph.edges[i][j]);
				}
			}
			System.out.println("\n");
		}
	}
	
	//找到和v点相连的邻接点
	public int getFirst(Graph_1 graph,int v){
		for(int i=0;i<graph.vexs.length;i++){
			if(graph.edges[v][i]==1){
				return i;
			}
		}
		return -1;
	}
	
	//若v的相邻点k已经访问过,则从下一个点开始遍历
	public int getNext(Graph_1 graph,int v,int k) {
		for(int i=k+1;i<graph.vexs.length;i++) {
			if(graph.edges[v][i]==1) {
				return i;
			}
		}
		return -1;
	}
	
	
	//深度优先遍历
	public void DFS(Graph_1 graph,int v,int[] visited){
		int next;
		System.out.println(v);
		//把已经遍历的点设置为1
		visited[v]=1;
		next=graph.getFirst(graph, v);
		while(next!=-1){
			//相邻点没有访问过则继续遍历
			if(visited[next]==0){
				graph.DFS(graph, next, visited);
			}
			//假如访问过则寻找下一相邻点
			next=graph.getNext(graph, v, next);
		}
		
	}
	
	//广度优先遍历,类似于树的层次遍历
	public void BFS(Graph_1 graph,int v,int[] visited){
		Queue<Integer> queue=new LinkedList<>();
		int next;
		queue.add(v);
		visited[v]=1;
		while(!queue.isEmpty()){
			next=queue.remove();
			System.out.println(next);
			int vex=graph.getFirst(graph, next);
			while(vex!=-1){
				if(visited[vex]==0){
					queue.add(vex);
					visited[vex]=1;
				}
				vex=graph.getNext(graph, next, vex);
			}
		}
	}
}

public class depthSearch {

	private static final int INF = 100000;

	public static void main(String[] args){
		int[] vs={0,1,2,3,4};
		int[][] A= {
				{INF,1,INF,1,INF},
				{1,INF,1,INF,INF},
				{INF,1,INF,1,1},
				{1,INF,1,INF,1},
				{INF,INF,1,1,INF}
		};
		Graph_1 graph=new Graph_1();
		graph.createGraph(graph, A, vs);
		graph.print_Graph(graph);
		int[] visited=new int[100];
		int[] visited_1=new int[100];
		graph.DFS(graph, 0, visited);
		System.out.println("------------");
		graph.BFS(graph, 0, visited_1);
	}
	
}

3.最小生成树(Prime算法)

Prime算法的核心步骤是:在带权连通图中V是包含所有顶点的集合, U已经在最小生成树中的节点,从图中任意某一顶点v开始,此时集合U={v},重复执行下述操作:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边,将(u,w)这条边加入到已找到边的集合,并且将点w加入到集合U中,当U=V时,就找到了这颗最小生成树。
其实,算法的核心步骤就是:在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边。
【实现代码】

//prime最小生成树
	//从start开始
	public void prim(int start){
		int num=vexs.length; //顶点个数
		int index=0; //prim最小树的索引,即prims数组的索引
		int[] prims=new int[num]; //prim最小数的结果数组
		int[] weights=new int[num]; //顶点间的权重
		
		prims[index++]=vexs[start];
		
		//初始化顶点的权重数组
		for(int i=0;i<num;i++)
			weights[i]=edges[start][i];
		
		//第start个顶点的权值初始化为0
		weights[start]=0;
		
		for(int i=0;i<num;i++){
			//从start开始,不需要在对第start个顶点进行处理
			if(start==i)
				continue;
			
			int j=0;
			int k=0;
			int min=INF;
			
			//从未被加入到最小生成树的顶点中,找出权重最小的顶点
			while(j<num){
				//若weights[j]=0意味着第j个节点已经排序过,已经加入最小生成树中
				if(weights[j]!=0&&weights[j]<min){
					min=weights[j];
					k=j;
				}
				j++;
			}
			
			//经过上面处理找到权重最小的顶点第k个顶点
			//将第k个顶点加入到最小深耕书的结果数组中
			prims[index++]=vexs[k];
			//已排序过设置为0
			weights[k]=0;
			//当第k个顶点被加入到最小成树的结果数组中之后,更新其它的权重
			for(j=0;j<num;j++){
				if(weights[j]!=0&&edges[k][j]<weights[j])
					weights[j]=edges[k][j];
			}
		}
		
		//就散最小生成树的权值
		int sum=0;
		for(int i=1;i<index;i++){
			int min=INF;
			//获取prims[i]在edges中的位置
			int n=getPosition(prims[i]);
			//在vexs中找到j的权重最小的顶点
			for(int j=0;j<i;j++){
				int m=getPosition(prims[j]);
				if(edges[m][n]<min){
					min=edges[m][n];
				}
			}
			sum+=min;
		}
		//打印最小生成树

        System.out.printf("PRIM(%d)=%d: ", vexs[start], sum);
        for (int i = 0; i < index; i++)
            System.out.printf("%d ", prims[i]);
        System.out.printf("\n");
	}
4.迪杰斯特拉(求最短路径)

1)算法思想:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。

(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。

(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。

(3) 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。
【实现代码】

	//vs记录顶点,pre[i]数组记录从vs已经到i节点之前的点,也就是这个时候已经在S集合中的点
	//dist[i]是顶点vs到顶点i的最短路径长度

	public void dijkstra(int vs,int[] prev,int[] dist){
		//flag[i]=true表示顶点vs到顶点i的最短路径已获取
		boolean[] flag=new boolean[vexs.length];
		
		//初始化
		for(int i=0;i<vexs.length;i++){
			flag[i]=false;
			prev[i]=0;
			dist[i]=edges[vs][i];
		}
		//对顶点vs自身进行初始化
		flag[vs]=true;
		dist[vs]=0;
		
		//遍历vexs.length-1次,每次找出一个顶点的最短路径
		int k=0;
		for(int i=1;i<vexs.length;i++){
			//寻找当前最小的路径
			//即,在未获取最短路径的顶点中,找到离vs最近的顶点k
			int min=INF;
			for(int j=0;j<vexs.length;j++){
				if(flag[j]==false&&dist[j]<min){
					min=dist[j];
					k=j;
				}
			}
			//标记顶点k已获取到最短路径
			flag[k]=true;
			
			//修改当前最短路径和前驱顶点
			//即,当已经“顶点k的最短路径”之后,更新未获取最短路径的顶点的
			//最短路径和前驱顶点
			for(int j=0;j<vexs.length;j++){
				int tmp=(edges[k][j]==INF?INF:(min+edges[k][j]));
				if(flag[j]==false&&(tmp<dist[j])){
					dist[j]=tmp;
					prev[j]=k;
				}
			}
		
		}
		//打印dijkstra最短路径的结果
		System.out.print(vexs[vs]);
		for(int i=0;i<vexs.length;i++){
			 System.out.printf("  shortest(%d, %d)=%d\n", vexs[vs], vexs[i], dist[i]);
		}
	}
5.拓扑排序

【实现代码】

package Graph;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
 
/**
 * 拓扑排序,当前方案并没有在节点类中加入过多的内容
 * 但是在图类中加入了边的集合adjaNode
 */
public class ToposortSort {
 
	/**
	 * 拓扑排序节点类
	 */
	private static class Node {
		public Object val;
		public int pathIn = 0; // 入链路数量
		public Node(Object val) {
			this.val = val;
		}
	}
 
	/**
	 * 拓扑图类
	 */
	private static class Graph {
		// 图中节点的集合
		public Set<Node> vertexSet = new HashSet<Node>();
		// 相邻的节点,纪录边
		public Map<Node, Set<Node>> adjaNode = new HashMap<Node, Set<Node>>();
 
		// 将节点加入图中
		public boolean addNode(Node start, Node end) {
			if (!vertexSet.contains(start)) {
				vertexSet.add(start);
			}
			if (!vertexSet.contains(end)) {
				vertexSet.add(end);
			}
			if (adjaNode.containsKey(start)
					&& adjaNode.get(start).contains(end)) {
				return false;
			}
			if (adjaNode.containsKey(start)) {
				adjaNode.get(start).add(end);
			} else {
				Set<Node> temp = new HashSet<Node>();
				temp.add(end);
				adjaNode.put(start, temp);
			}
			end.pathIn++;
			return true;
		}
	}
 
	//Kahn算法
	private static class KahnTopo {
		private List<Node> result; // 用来存储结果集
		private Queue<Node> setOfZeroIndegree; // 用来存储入度为0的顶点
		private Graph graph;
 
		//构造函数,初始化
		public KahnTopo(Graph di) {
			this.graph = di;
			this.result = new ArrayList<Node>();
			this.setOfZeroIndegree = new LinkedList<Node>();
			// 对入度为0的集合进行初始化
			for(Node iterator : this.graph.vertexSet){
				if(iterator.pathIn == 0){
					this.setOfZeroIndegree.add(iterator);
				}
			}
		}
 
		//拓扑排序处理过程
		private void process() {
			while (!setOfZeroIndegree.isEmpty()) {
				Node v = setOfZeroIndegree.poll();
				
				// 将当前顶点添加到结果集中
				result.add(v);
				
				if(this.graph.adjaNode.keySet().isEmpty()){
					return;
				}
				
				// 遍历由v引出的所有边
				for (Node w : this.graph.adjaNode.get(v) ) {
					// 将该边从图中移除,通过减少边的数量来表示
					w.pathIn--;
					if (0 == w.pathIn) // 如果入度为0,那么加入入度为0的集合
					{
						setOfZeroIndegree.add(w);
					}
				}
				this.graph.vertexSet.remove(v);
				this.graph.adjaNode.remove(v);
			}
			
			// 如果此时图中还存在边,那么说明图中含有环路
			if (!this.graph.vertexSet.isEmpty()) {
				throw new IllegalArgumentException("Has Cycle !");
			}
		}
 
		//结果集
		public Iterable<Node> getResult() {
			return result;
		}
	}
	
	//测试
	public static void main(String[] args) {
		Node A = new Node("A");
		Node B = new Node("B");
		Node C = new Node("C");
		Node D = new Node("D");
		Node E = new Node("E");
		Node F = new Node("F");
		
		Graph graph = new Graph();
		graph.addNode(A, B);
		graph.addNode(B, C);
		graph.addNode(B, D);
		graph.addNode(D, C);
		graph.addNode(E, C);
		graph.addNode(C, F);
		
		KahnTopo topo = new KahnTopo(graph);
		topo.process();
		for(Node temp : topo.getResult()){
			System.out.print(temp.val.toString() + "-->");
		}
	}
	
}

你可能感兴趣的:(在线编程)