4.2问题:
给定有向图,设计一个算法,找出两个节点之间是否存在一条路径。
思考:
只需通过图的遍历,比如深度优先搜索或广度优先搜索等,就能解决这个问题。我们从两个节点的其中一个出发,在遍历过程中,检查是否找到另一个结点。在这个算法中,访问过的结点都应标记为“已访问”,以免重复访问结点。
关于邻接表与图的基本实现与操作:
import java.util.*;
/**
* 邻接表实现的图
* @author ctk
*
*/
class ListGraph {
List<GraphNode> gNodes ;//头结点链表
private int vertex ;//图的结点数目
private int edges ;//边数目
public ListGraph(int vertex){//构造方法
this.vertex = vertex ;
gNodes = new ArrayList<>(vertex);//给头结点链表分配内存空间
for(int i=0;i<vertex;i++)
{
GraphNode gnode = new GraphNode();
gnode.setNodeIndex(i);
gnode.setNext(null);
gNodes.add(gnode);
}
}
public List getNode(){//返回头结点链表
return gNodes;
}
public void addEdge(int i,int j,int weight){//添加边
if(i >= vertex || j >= vertex || i < 0 || j < 0)
{
System.out.println("输入的i和j超过范围");
return;
}
GraphNode gnode = gNodes.get(i);
boolean isAlter = false;
while(gnode.getNext() != null)
{
if(gnode.getNodeIndex() == j)
{
gnode.setData(weight);
isAlter = true;
break;
}
gnode = gnode.getNext();
}
if(i == j){
gnode.setData(weight);
isAlter = true;
}
if(!isAlter){
GraphNode edgeNode = new GraphNode();
edgeNode.setData(weight);
edgeNode.setNodeIndex(j);
edgeNode.setNext(null);
gnode.setNext(edgeNode);
}
}
public int[][] getMartix(){//生成邻接矩阵,图以邻接矩阵的形式返回
int[][] martix = new int[vertex][vertex];
GraphNode temp = null;
for(int i=0;i<gNodes.size();i++){
temp = gNodes.get(i);
while(temp != null){
martix[i][temp.getNodeIndex()] = temp.getData();
temp = temp.getNext();
}
}
return martix;
}
public int getEdge(int i,int j){ //获得某边,获取某个边的权重
int weight = 0;
if(i >= vertex || j >= vertex || i < 0 || j < 0)
{
System.out.println("输入的i和j超过范围");
return weight;
}
GraphNode temp = gNodes.get(i);
while(temp != null){
if(temp.getNodeIndex() == j){
weight = temp.getData();
break;
}
temp = temp.getNext();
}
return weight;
}
public int getVertex() {//获取全图的顶点数
return vertex;
}
public int getEdges() {//返回全图的边数
return edges;
}
/*
public static void main(String[] args) {
ListGraph graph = new ListGraph(5);
graph.addEdge(0, 1, 2);
graph.addEdge(0, 2, 3);
graph.addEdge(1, 1, 4);
graph.addEdge(2, 3, 6);
int[][] martix = graph.getMartix();
for(int i =0;i<martix.length;i++){
for(int j=0;j<martix[i].length;j++)
System.out.print(martix[i][j]+" ");
System.out.println();
}
System.out.println("获取边<1,1> :"+graph.getEdge(1, 1));
}
*/
}
class GraphNode{//节点类
private int nodeIndex;//结点标号
private int data;//结点数据
State state=State.Unvisited;//所有顶点状态都初始化为 未被访问的状态。
private GraphNode next;//结点指针
public int getNodeIndex() {//返回顶点标号
return nodeIndex;
}
public void setNodeIndex(int nodeIndex) {//设置顶点标号
this.nodeIndex = nodeIndex;
}
public int getData() {//返回结点数据
return data;
}
public void setData(int data) {//设置结点数据
this.data = data;
}
public GraphNode getNext() {//获取该结点指向的下一个结点
return next;
}
public void setNext(GraphNode next) {//设置该结点指向的下一个结点
this.next = next;
}
}
enum State{//枚举类,标识节点被访问状态。
Unvisited,Visited,Visiting;
}
路径搜索函数:
public static boolean search(ListGraph g,GraphNode start,GraphNode end){//广搜 迭代实现 DFS
//当作队列使用
for(GraphNode v:g.gNodes){ //每次搜索前先将首结点链表里的顶点状态都设置为 Unvisited .
v.state=State.Unvisited;
}
LinkedList<GraphNode> q = new LinkedList<GraphNode>();//q队列用于记录正在访问这一层和上一层的顶点。
start.state=State.Visiting;
q.add(start);
GraphNode u;
while(!q.isEmpty()){
u = q.removeFirst();//也即 dequeue()
// System.out.println("aa "+ u.getNodeIndex());
for(GraphNode v=u.getNext();v!=null;v=v.getNext()){//遍历u顶点的邻接顶点
System.out.println("bb "+ v.getNodeIndex());
if(v.state==State.Unvisited){//判断是否被访问过
if(v.getNodeIndex()==end.getNodeIndex()){//判断是否遍历到目标顶点
return true;
}else{
q.add(g.gNodes.get(v.getNodeIndex()));//将该顶点入队列,这里需要注意入栈的是首结点链表里的顶点。
// System.out.println("入队成功"+v.getNodeIndex());
}
}
}
u.state=State.Visited;
}
return false;
}
public static void BFS_path(ListGraph g,int i,int j){//广度优先搜索路径
if(search(g,g.gNodes.get(i),g.gNodes.get(j))){
System.out.println("存在路径: "+i+" ->...-> "+j);
}else{
System.out.println("不存在路径: "+i+" ->...-> "+j);
}
}
主函数内容:
public static void main(String args[]){
ListGraph graph = new ListGraph(5);
graph.addEdge(0, 1, 1);
graph.addEdge(0, 2, 1);
graph.addEdge(1, 4, 1);
graph.addEdge(2, 3, 1);
int[][] martix = graph.getMartix();
System.out.println("该图的邻接矩阵表示如下:");
for(int i =0;i<martix.length;i++){
for(int j=0;j<martix[i].length;j++)
System.out.print(martix[i][j]+" ");
System.out.println();
}
System.out.println("边<0,1>的权重为:"+graph.getEdge(0, 1));
System.out.println();
BFS_path(graph,0,1);
BFS_path(graph,0,2);
BFS_path(graph,0,3);
BFS_path(graph,0,4);
BFS_path(graph,1,2);
BFS_path(graph,1,3);
BFS_path(graph,1,4);
BFS_path(graph,2,3);
BFS_path(graph,2,4);
BFS_path(graph,3,4);
}
如需自己跑跑这个代码,还需要稍微组织一下。
测试结果如下: