我有一个目前无法解决的问题。
假设我有一个类Member
,其中包含与给定成员相关的成员列表的信息:
ArrayList<Member> links;
此外,此类的方法称为canBeLinked(Member member)
,
显然返回布尔值,该布尔值定义特定成员是否可以直接或间接与给定成员链接。
说“直接”是指共同关系A-> B;如果例如A-> B,B-> C,那么它也应该返回true。 还请记住,如果A-> B,则B->A。 这样我们得到了无向图的模型。
我的困惑是如何实现此功能;我猜它一定是递归的,但要点是它不是两个参数的函数(例如,如果它是静态函数,我们可以很容易地编写函数canBeLinked(Member a, Member b)
,那么互联网上有很多此类图的实现)。
我的第一种方法是写这样的东西:
public boolean canBeLinked(Member member) {
if (this.links.contains(member)) {
return true;
}
else {
for (Member m : links)
if (m.canBeLinked(member)) return true;
}
return false;
}
我肯定会得到一个StackOverflowException; 假设我们有这样的关系:
A -> B
B -> D
B -> E
D -> C
E -> C
致电
a.canBeLinked(c)
我们得到: 1)首先,我们检查“ a”的成员列表中是否已经包含“ c”; 2)这是不正确的,所以请进入for循环; 3)由于列表中只有一个成员(“ b”),因此我们调用b.canBeLinked(c); 4)“ c”也不是“ b”列表的成员,因此它也进入“ for”语句 5)现在我们很麻烦,因为“ b”的列表不仅包含“ e”和“ d”,而且还包含“ a”,这导致我们进入第1页;它会永远持续下去(直到内存用完)
能否请您解释如何解决此问题?我敢肯定,这很简单,但我对此一无所知,而且肯定错过了它的一些要点。
答案 0 :(得分:1)
幸运的是,修复非常简单;您需要跟踪搜索中已经访问过哪个Members
,例如使用Set<Member>
,并且不要循环访问已见过的任何Members
。在查询链接的Members
之前,请将当前的Member
添加到已访问的集合中。
由于您的递归函数现在需要传递一组访问成员,因此最好将其拆分为从公用方法调用的private
或protected
方法。
public boolean canBeLinked(Member member)
{
return canBeLinked(member, new HashSet<>());
}
private boolean canBeLinked(Member member, Set<Member> visited)
{
if (this.links.contains(member)) {
return true;
}
else {
visited.add(this);
for (Member m : links)
{
if (!visited.contains(m)) return m.canBeLinked(member, visited);
}
}
return false;
}