软件构造思考2:Java中的广度优先搜索

之前我们在数据结构中学习了广度优先搜索,但是那是在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,特殊情况下重载即可,然后就可以直接使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值