从图中某个顶点出发访问遍图中的所有顶点,并且每个顶点仅仅被访问一次。
其中图的遍历分为两种,一种是图的深度优先遍历算法,一种是图的广度优先遍历算法。
图的深度遍历算法步骤:
(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);
}
}
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");
}
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]);
}
}
【实现代码】
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() + "-->");
}
}
}