之前我们在数据结构中学习了广度优先搜索,但是那是在C语言基础上,而且在实现时,我们只有一个结构体来表示图结构。在软件构造第一个实验中我们尝试使用自己构建的简陋的class表示点集,在第二个实验中,我们重新构建了一个图结构ADT。那么,这三种情况下的广度优先搜索该如何编写呢,我们可以比较一下他们的不同。但是可以肯定得的是,当我们已经构建起一个图结构ADT后,而且这个ADT有表示图中点集、边集的变量,有得到某个点源点、目标顶点的MAp集合,这时,我们再来编写广度优先搜索就变得容易许多。
情况一、无图结构ADT的Java的广度优先搜索:
我们这个函数得到的是两点间的最大距离,运用广度优先搜索。
我们首先定义了一个存储点的List集合:List<Person> VertexList = new ArrayList<>();
之后定义了一个寻找点在点集中位置的方法FindPosistion
public int FindPosistion(Person people)//寻找该点在点集中的位置,若不在返回-1
{
if(VertexList.contains(people))
for(int i=0;i<VertexList.size();i++)
if(VertexList.get(i).equals(people))
return i;
return -1;
}
我们把Person类看作是点,在这个类中,具体如下:
public class Person {
public String name;
List<Integer> SocialList = new ArrayList<>();
public Person(String name)
{
this.name=name;
}
public int listsize()//获取关系列表大小;
{
return SocialList.size();
}
public void Socialadd(int posistion)//添加联系人;
{
SocialList.add(posistion);
}
public boolean edgeexist(int posistion)//判断两者之间是否有边;
{
if(SocialList.contains(posistion))
return true;
else
return false;
}
public int getSocial(int position) //其他联系者与person的关系;
{
return SocialList.get(position);
}
}
当有了Perosn类之后,我们再来看何如使用:
public int getDistance(Person people1, Person people2) {//使用哈希表和队列,利用广度优先搜索
Map<Person, Integer> distance = new HashMap<>();
Queue<Integer> queue = new LinkedList<Integer>();
int first=FindPosistion(people1);
int last=FindPosistion(people2);
boolean []visited=new boolean[VertexList.size()];
for(int i = 0; i < visited.length; i++)//点是否被搜索的标志
visited[i] = false;
if(first==last)
return 0;
if(first==-1||last==-1)
return -1;
queue.add(first);
visited[first]=true;
distance.put(people1, 0);
while(!queue.isEmpty())
{
int currentvertex=queue.remove();
for(int i=0;i<VertexList.get(currentvertex).listsize();i++)
{
int visitnode=VertexList.get(currentvertex).getSocial(i);
if (!visited[visitnode])
{//每次让一条边根端点到起始点的距离+1成为它的子端点的距离值
//若是子端点就是目的端点退出
visited[visitnode]=true;
queue.add(visitnode);
distance.put(VertexList.get(visitnode), distance.get(VertexList.get(currentvertex))+1);
}
if(VertexList.get(visitnode).equals(people2))
return distance.get(VertexList.get(visitnode));
}
}
return -1;
}
我们可以看到,它同样很麻烦,尤其是换个问题,我们根本没法复用或者说复用效率很低。
情况二、使用图结构ADT的Java的广度优先搜索:
我们定义一个使用边集表示的图结构:当然这里的ADT是一个泛型接口下的实现类
public class ConcreteEdgesGraph<L> implements Graph<L> {
private final Set<L> vertices = new HashSet<>();
private final List<Edge<L>> edges = new ArrayList<>();
private void checkRep()
{
for (Edge<L> edge : edges) {
assert vertices.contains(edge.getsource());
assert vertices.contains(edge.gettarget());
assert edge.getweight()>0;
}
}
@Override public boolean add(L vertex) {
if(vertices.contains(vertex))
return false;
vertices.add(vertex);
return true;
//throw new RuntimeException("not implemented");
}
@Override public int set(L source, L target, int weight) {
//throw new RuntimeException("not implemented");
int x=1;
Map<L, Integer> map=new HashMap<L,Integer>();
if(!vertices.contains(source))
{
add(source);
x=0;
}
if(!vertices().contains(target))
{
add(target);
x=0;
}
map=sources(target);
if(x==0||!map.containsKey(source))
{
if(weight>0)
{
Edge<L> edge=new Edge<L>(source, target, weight);
edges.add(edge);
}
return 0;
}
for(int i=0;i<edges.size();i++)
{
if(edges.get(i).getsource().equals(source)&&edges.get(i).gettarget().equals(target))
{
x=edges.get(i).getweight();
edges.remove(i);
if(weight!=0)
{
Edge<L> e=new Edge<L>(source, target, weight);
edges.add(i,e);
}
}
}
checkRep();
return x;
}
@Override public boolean remove(L vertex) {
//throw new RuntimeException("not implemented");
if(!vertices.contains(vertex))
{
return false;
}
else {
for(L s:vertices)
{
set(s, vertex, 0);
set(vertex, s, 0);
}
checkRep();
return true;
}
}
@Override public Set<L> vertices() {
//throw new RuntimeException("not implemented");
return new HashSet<L>(vertices);
}
@Override public Map<L, Integer> sources(L target) {
//throw new RuntimeException("not implemented");
Map<L, Integer> map=new HashMap<>();
for(Edge<L> e:edges)
{
if(e.gettarget().equals(target))
{
map.put(e.getsource(), e.getweight());
}
}
checkRep();
return map;
}
@Override public Map<L, Integer> targets(L source) {
//throw new RuntimeException("not implemented");
Map<L, Integer> map=new HashMap<>();
for(Edge<L> e:edges)
{
if(e.getsource().equals(source))
{
map.put( e.gettarget(), e.getweight());
}
}
checkRep();
return map;
}
public String toString()
{
String s="";
for(Edge<L> e:edges)
{
s=s+e.toString();
}
checkRep();
return s;
}
}
class Edge<L> {
// fields
private final L source;
private final L target;
private final int weight;
private void checkRep()
{
assert source!=null;
assert target!=null;
assert weight>0;
}
// TODO methods
public Edge(L source, L target,int weight)
{
this.source=source;
this.target=target;
this.weight=weight;
}
public L getsource()
{
checkRep();
return this.source;
}
public L gettarget()
{
checkRep();
return this.target;
}
public int getweight()
{
checkRep();
return this.weight;
}
// TODO toString()
public String toString()
{
checkRep();
return this.source+"-->"+this.target+" 权重:"+weight+"\n";
}
}
这时,我们就很容易广度优先搜索了:
public int getDistance(Person people1, Person people2) {//使用哈希表和队列,利用广度优先搜索
Map<Person, Integer> distance = new HashMap<>();
Map<Person, Integer>visited = new HashMap<>();
Queue<Person> queue = new LinkedList<Person>();
if(people1.getname().equals(people2.getname()))
return 0;
if(!Graph.vertices().contains(people1)||!Graph.vertices().contains(people2))
return -1;
for(Person p:Graph.vertices())
{
visited.put(p, 0);
}
queue.add(people1);
distance.put(people1, 0);
while(!queue.isEmpty())//广度优先搜索
{
Person currentvertex=queue.remove();
visited.put(currentvertex, 1);//更改状态
for(Person s:Graph.targets(currentvertex).keySet())//遍历子节点
{
int length=distance.get(currentvertex);
if(visited.get(s)!=1)
{
visited.put(s, 1);
queue.add(s);
distance.put(s, length+1);
}
if(s.getname().equals(people2.getname()))
return distance.get(s);
}
}
return -1;
}
可能你会觉得这不是越写越麻烦吗,其实如果你单独这样使用确实很麻烦,但是如果我们以后遇到图问题时,情况一和情况二可能需要我们重新编写有关实现,情况三我们只要导入这个ADT,特殊情况下重载即可,然后就可以直接使用。