基于软件构造Lab2的广度优先搜索算法

       软件构造Lab2中的P2要求我们使用P1已经完成的图数据结构重写Lab1中的FriendshipGraph类。在FriendshipGraph中,向图中加入边,加入点的操作重写很简单,直接调用即可。这一部分的核心应该在于求图中任意两点的最短距离,即getDistance方法的重写实现。

        我在求解这一最短距离时使用了广度优先搜索算法,我的算法在实现上可能有所不同,代码上可能有些繁琐,但我认为是比较好理解的。

        直接给出我写的代码:

public int getDistance(Person sourceed, Person targeted)
	{
		Person target, tempP;
		Person FG = new Person("block");
		target = sourceed;
		int i = 0, sizeL, distance = 1, edgei;
		Set<Person> targetSet = new HashSet<>();
		Queue<Person> Q = new LinkedBlockingQueue<Person>();
		Iterator<Person> targetIt = targetSet.iterator();
		if(sourceed == targeted)
		{
			return 0;
		}
		Q.add(target);
		Q.add(FG);
		target.setFlag(1);
		while(!Q.isEmpty())
		{
			target = Q.poll();
			if(target == FG)
			{
				distance++;
				if(!Q.isEmpty())
				{
					target = Q.poll();
				}
				else
				{
					return -1;
				}
				Q.add(FG);
			}
			if(graph.targets(target).containsKey(targeted))
			{
				return distance;
			}
			else
			{
				targetSet = graph.targets(target).keySet();
				targetIt = targetSet.iterator();
				while(targetIt.hasNext())
				{
					tempP = targetIt.next();
					if(tempP.getFlag() == 0)
					{
						tempP.setFlag(1);
						Q.add(tempP);
					}
				}
			}
		}
		return -1;
	}

       我的代码实现思路如下:对于Person类,我在里面设置了一个标记量flag,并提供了get,set方法。这一变量用于标记这一点在广度优先搜索时是否被遍历到,以保证每一次遍历时不会遍历到前面遍历过的点。同时在代码开端,我设置了一个分隔对象,Person FG,这一对象的名字被命名为block,用于分割不同层次。如果我们这么设计,需要在代码开端的规约中强调图中有实际意义的点不应被命名为"block"。

        代码的具体实现依赖于一个队列。首先判断输入的起始点与目标点时同一点的话则返回0,如果不是同一点,将起始点入队列,并将这一点flag设置为1,同时入队列一个分隔符FG(第一层只有起始点一个点)。随后进入一个while循环,如果队列不空,则执行循环。循环中先从队列弹出一点作为这一次循环的比较点,如果弹出的为分隔符,那么说明其所对的这一层点已经都遍历完成,我们将距离加一,并再弹出一点作为这一次循环的比较点,同时再入一个分隔符。使用图中已经实现的targets方法返回一个Map,利用Map的containsKey方法可以简单判断这一点的所有儿子是否存在目标点,如果存在,搜索完成,返回distance值。如果不存在,利用KeySet方法将包含其所有儿子的set取出,设置一个迭代器遍历,把遍历到的每一个点入队列并设置标记flag为1 。

       一些可能会有的疑问:

       为什么不在每一次循环结束时加一个分隔符?

       分隔符的作用本质上是为了计算diatance。distance值是顺着层次递增的,每多一层则distance应加一,否则distance应不变。所以分隔符应该分隔的是不同层次。而同一层次可能包含两个或两个以上的点,例如:A->B,A->C,那么B,C再同一层次,A到B,A到C的距离应该都为1。如果每一次循环结束都加入一个分隔符,则B,C之间会加入一个分隔符。在代码上认为B,C属于两个层次。A到B,C的距离会变成一个2,一个3。具体哪个是2取决于迭代器先遍历到哪个。

       为什么要在每一次弹出分隔符时再入一个分隔符?

        每一次弹出分隔符证明这一分隔符所对应的层所有点已经搜索完毕,即这一层所对应的所有点的儿子恰好都已经被遍历并入队列。可以理解的是,这一层所有点的所有儿子组成下一层的所有点,也就是此时下一层的所有点恰好都已经入队列,此时再入队列应该不再属于这一层了,所以应该入一个分隔符。

       值得注意的是,弹出分隔符时,再弹出下一点之前应该判断队列是否为空,因为最后一层后面也会跟一个分隔符。

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值