软件构造lab1实验中编写的Problem3部分,在此处做个记录
涉及的数据类,方法
-
Graph类
表示无向图。其下有几个重要变量:
persons:为ArrayList,用于保存graph中的节点。其定义为:public List<Person> persons = new ArrayList<Person>();
relationships:为嵌套的Vector,保存节点间的关系矩阵。若两节点直接相连,则对应位置为1;若两节点不直接相连,则对应位置为0。其定义为:
public Vector<Vector<Integer>> relationships = new Vector<Vector<Integer>>();
-
Person类
表示图中的顶点。具有name
与index
两个私有变量,name
为String类型,表示名称,是顶点的标识;index
为int型,代表顶点在关系矩阵中对应第index行与第index列。若顶点不在图中,则index
为默认值-1。 -
getDistance方法
输入参数为两个Person顶点,返回值为两顶点间的最短距离。为了方便说明,规定相连两点间距为1。方法声明为:public int getDistance(Person p1, Person p2)
算法基本思想
-
首先对异常情况进行判断,若输入两顶点相同 / 有不在图中的顶点,则直接返回-1。实现方法为具体根据person的index与name成员判断。
-
进行BFS深搜。具体的实现如下:
首先构造以下三个变量:int distance =0; // 起始点到终点的距离,初始为0 Queue<Person> queue = new LinkedList<Person>(); // 队列,用于BFS搜索 int[] visit = new int[persons.size()]; // visit数组(visit为标志是否访问过的数组,访问过为1,否则为0) boolean[] isQueueEnd = new boolean[persons.size()]; // isQueueEnd标志节点i是否是某轮bfs深搜的终点,若是,其为true,,需要使distance++
在我们的算法中,先将初始顶点
p1
加入队列,因为其访问过,且为第0轮深搜的终点,故设置visit[]
与isQueueEnd[]
对应位为1。然后弹出该顶点,依次搜索与该顶点直接相连的顶点入队,将这些顶点的visit[]
位均设置为1,且设置当前轮最后一个入队顶点的isQueueEnd[]
对应位为1,由此,便完成了一轮BFS搜索。 -
终结情况判断:
当队首为终结顶点p2
时,正常结束,返回distance
。
当检测到队空时,说明没有从p1
到p2
的通路,返回-1
以下图为例来解释我们的算法:
假设计算从P1到P6的距离:
1. 先将P1入队
2.弹出P1,加入P2,P3,P4,并标记P4为当前轮终点,distance++。
3.之后按顺序依次进行:
直到队首为P6,返回此时的distance值。
具体实现
person类
public class Person {
private String name;
private int index = -1;
public Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
getDistance()方法
/**
* Add a new edge from p1 to p2.
*
* @param p1 The starting vertex of the new edge
* @param p2 The ending vertex of the new edge
* @return The shortest distance between p1 and p2. If there are no any paths between them, return -1.
*/
public int getDistance(Person p1, Person p2) {
// 异常情况处理
// p1与p2有其一不在关系图中
if (p1.getIndex() == -1) {
System.out.print(p1.getName() + "不在关系图中");
return -1;
}
if (p2.getIndex() == -1) {
System.out.print(p2.getName() + "不在关系图中");
return -1;
}
// p1与p2相等
if (p1.getIndex() == p2.getIndex()) {
return 0;
}
Queue<Person> queue = new LinkedList<Person>(); // 队列,用于BFS搜素
int distance = 0;
Person temp = new Person("");
Person queueEnd = new Person("");
Vector<Integer> tempCol = new Vector<Integer>();
// visit数组(visit为标志是否访问过的数组,访问过为1,否则为0)
int[] visit = new int[persons.size()];
// isQueueEnd标志节点i是否是某轮bfs深搜的终点,若是,其为true,,需要使distance++
boolean[] isQueueEnd = new boolean[persons.size()];
// 初始化,对p1进行设定
queue.add(p1);
visit[p1.getIndex()] = 1;
isQueueEnd[p1.getIndex()]=true;
while (queue.peek() != p2) {
temp = queue.poll(); // 弹出并保存queue的头元素
// 将与queue头元素直接相连,且未访问过的元素入队
tempCol = relationships.get(temp.getIndex()); // tempCol保存头元素对应的关系矩阵行
for (int i = 0; i < tempCol.size(); i++) { // 头元素对应的关系矩阵行,遍历此行中的所有元素,找到与头元素直接相邻的元素
if (tempCol.get(i) == 1) {
// 查找index为i的person,并将其加入队列,同时把其标记为访问过
for (Person t : persons) {
if (t.getIndex() == i && visit[i] == 0) {
queue.add(t);
visit[i] = 1;
queueEnd = t; // 记录当前队尾
break;
}
}
}
}
// 最后队列空,说明没有p1到p2的直接通路
if (queue.isEmpty())
return -1;
// 记录当前队尾,并使distance++
if (isQueueEnd[temp.getIndex()]) {
isQueueEnd[queueEnd.getIndex()]=true;
distance++;
}
}
return distance;
}