先实现无向图的数据结构。
package graph;
import simpleStructure.Bag;
/**
* 用邻接表做的图
* 用了一个Bag数组,数组的索引是顶点,索引的内容Bag里面存放与该顶点相邻的顶点
* @author Administrator
*
*/
public class Graph implements GraphItf
{
private Bag<Integer>[] adj;
private final int vertexNum;
private int edgeNum;
/**
* 构造有v个顶点的图
* 编号从0到v-1
* @param v
*/
public Graph(int v)
{
vertexNum = v;
adj = (Bag<Integer>[]) new Bag[v];
for(int i = 0;i < v;i++)
{
adj[i] = new Bag<Integer>();
}
}
@Override
public int vertexNum()
{
// TODO Auto-generated method stub
return vertexNum;
}
@Override
public int edgeNum()
{
// TODO Auto-generated method stub
return edgeNum;
}
@Override
public void addEdge(int v, int w)
{
// TODO Auto-generated method stub
adj[v].add(w);
adj[w].add(v);
edgeNum++;
}
@Override
public Iterable<Integer> adj(int v)
{
// TODO Auto-generated method stub
return adj[v];
}
}
这里用的是邻接链表,bag是一个链表,bags是一个bag的数组,bags[x]是一个bag,bag中有x节点所连接的所有节点。
接下来实现无向图的深度优先遍历
package graph;
/**
* 深度优先搜索
* @author zsj
*
*/
public class DepthFirstSearch implements SearchItf
{
private boolean[] marked;//查看一个点是否被遍历过
private int count;//遍历到的点的数量
private Graph graph;//传入的图
public DepthFirstSearch(Graph g, int s)
{
this.graph = g;
marked = new boolean[g.vertexNum()];
dfs(s);
}
//用递归实现的深度优先搜索
private void dfs(int v)
{
if (marked[v])
return;
marked[v] = true;
count++;
for (Integer i : graph.adj(v))
{
dfs(i);
}
}
@Override
public boolean marked(int v)
{
// TODO Auto-generated method stub
return marked[v];
}
@Override
public int count()
{
// TODO Auto-generated method stub
return count;
}
}
深度优先搜索就是递归的调用dfs遍历当前顶点可达的所有顶点。
在图像上看就会呈现出走一条路走到底,再回溯,走另一条路。
下面实现广度优先搜索
package graph;
import simpleStructure.Queue;
import simpleStructure.Stack2;
/**
* 广度优先搜索
* @author zsj
*
*/
public class BreadthFirstPaths implements PathsItf
{
private GraphItf g;//传入的图
private int s;//起始点
private boolean[] marked;//marked[i]显示i有没有被遍历过,有的话就是true
private int[] edgeTo;//edgeTo[i]=v说明i的父节点是v,edgeTo储存的是s广度遍历得到的路径
private Queue<Integer> queue;//广度遍历所需要的队列
public BreadthFirstPaths(GraphItf g, int s)
{
// TODO Auto-generated constructor stub
this.g = g;
this.s = s;
marked = new boolean[g.vertexNum()];
edgeTo = new int[g.vertexNum()];
queue = new Queue<Integer>();
bfs(s);
}
//使用队列实现的广度优先搜索顶点v
private void bfs(int v)
{
queue.enqueue(v);
marked[s] = true;
while (!queue.isEmpty())
{
int head = queue.dequeue();
for (int i : g.adj(head))
if (!marked[i])
{
queue.enqueue(i);
edgeTo[i] = head;
marked[i] = true;
}
}
}
@Override
public boolean hasPathTo(int v)
{
// TODO Auto-generated method stub
return marked[v];
}
@Override
public Iterable<Integer> pathTo(int v)
{
if(!marked[v]) return null;
Stack2<Integer> stack2 = new Stack2<Integer>();
while (v != s)
{
stack2.push(v);
v = edgeTo[v];
}
stack2.push(s);
return stack2;
}
}
这里使用队列实现广度优先搜索,而深度优先搜索是使用栈来实现。
基本过程是,先将开始点放入队列,然后从队列弹出一个元素做当前的head,将当前head所连接的没有被遍历过的点放入队列,再从队列弹出一个,如此循环,直到队列为空。
在图像上看就是一层一层的向下搜索,而不是一条路先走到底。
有向图的搜索与无向图类似并且更加简单,同样可以采用邻接链表的数据结构来实现有向图,不同的只是添加节点的时候只需要单向添加就行了,因为是单向的。
有了深度和广度优先搜索,才可以实现图上的各类算法,像连通分量,最短路径,最小生成树等等。