Java 如何检测集合中的循环
与标准JavaJava 如何检测集合中的循环,java,graph-theory,Java,Graph Theory,与标准JavaSet类似,但更多 我有一门课技能,它有必备技能 @Data @Entity public class Skill { @Id @GeneratedValue private UUID id; @OneToMany private Set<Skill> prerequisites = new HashSet<>(); private String name; } @数据 @实体 公开课技能{ @身份证 @
Set类似,但更多
我有一门课技能,它有必备技能
@Data
@Entity
public class Skill {
@Id
@GeneratedValue
private UUID id;
@OneToMany
private Set<Skill> prerequisites = new HashSet<>();
private String name;
}
@数据
@实体
公开课技能{
@身份证
@生成值
私有UUID;
@独身癖
私有集先决条件=新HashSet();
私有字符串名称;
}
我想确保先决条件中没有循环
这是我开始的,当然它不起作用,因为我只处理自我循环
@UtilityClass
public class CycleChecks {
/**
* Take a root object and a function to get its edges to see if there are any cycles.
*
* @param root root object
* @param edges a function that would take an object and get its edges.
* @param <T> an object that has edges
* @return has a cycle.
*/
public <T> boolean isCyclic(T root, Function<T, Iterable<T>> edges) {
final Set<T> visited = new HashSet<>();
return doIsCyclic(root, edges, visited);
}
private <T> boolean doIsCyclic(T vertex, Function<T, Iterable<T>> edges, Set<T> visited) {
if (visited.contains(vertex)) {
return true;
}
visited.add(vertex);
for (T edgeTarget : edges.apply(vertex)) {
if (doIsCyclic(edgeTarget, edges, visited)) {
return true;
}
}
return false;
}
}
@UtilityClass
公共类自行车{
/**
*取一个根对象和一个函数来获取它的边,看看是否有循环。
*
*@param根对象
*@param edges一个函数,它将获取一个对象并获取其边。
*@param有边的对象
*@return有一个循环。
*/
公共布尔是循环的(T根,函数边){
访问的最终集=新HashSet();
返回doiscycle(根、边、访问);
}
私有布尔DoisCycle(T顶点、函数边、集合){
如果(已访问。包含(顶点)){
返回true;
}
添加(顶点);
对于(T edgeTarget:边。应用(顶点)){
if(doIsCyclic(edgeTarget、edges、visitored)){
返回true;
}
}
返回false;
}
}
下面这样的测试很好,但没有彻底测试。只有一个列表保存ID,我们可能会遇到这样的情况:多个单独的技能具有相同的先决条件,并且被错误地检测为一个循环。这在这里发生,例如:,所以使用第二个递归列表
您可以使用以下命令调用它:hasCycle(yourFirstSkill,new ArrayList(),new ArrayList())代码>
publicstaticbooleanhascycle(技能输入、访问列表、列表递归){
UUID currentId=entry.getId();
if(recursion.contains(currentId))
返回true;
if(已访问。包含(当前ID))
返回false;
已访问。添加(当前ID);
recursion.add(currentId);
对于(最终技能先决条件:entry.getpremissions()){
if(hasCycle(先决条件、访问、递归)){
返回true;
}
}
递归.remove(currentId);
返回false;
}
我的最终解决方案基于@Shadov的答案。只是做了一些调整,使其通用,我使用了Set
forvisitored
和recursion
而不是列表
@UtilityClass
public class CycleChecks {
/**
* Take a root object and a function to get its edges to see if there are any cycles.
*
* @param root root object
* @param adjacentFunction a function that would take an object and return an iterable of objects that are adjacent to it.
* @param <T> an object that has edges
* @return has a cycle.
*/
public <T> boolean isCyclic(T root, Function<T, Iterable<T>> adjacentFunction) {
final Set<T> visited = new HashSet<>();
final Set<T> recursion = new HashSet<>();
return doIsCyclic(root, adjacentFunction, visited, recursion);
}
private <T> boolean doIsCyclic(T current, Function<T, Iterable<T>> adjacentFunction, Set<T> visited, Set<T> recursion) {
if (recursion.contains(current)) {
return true;
}
if (visited.contains(current)) {
return false;
}
visited.add(current);
recursion.add(current);
for (T adjacent : adjacentFunction.apply(current)) {
if (doIsCyclic(adjacent, adjacentFunction, visited, recursion)) {
return true;
}
}
recursion.remove(current);
return false;
}
}
@UtilityClass
公共类自行车{
/**
*取一个根对象和一个函数来获取它的边,看看是否有循环。
*
*@param根对象
*@param adjacentFunction获取对象并返回与其相邻的对象的iterable的函数。
*@param有边的对象
*@return有一个循环。
*/
公共布尔是循环的(T根,函数邻接ntf函数){
访问的最终集=新HashSet();
最终集递归=新HashSet();
返回doIsCyclic(根、邻接函数、已访问、递归);
}
私有布尔doIsCyclic(T当前、函数邻接ntfunction、访问集、递归集){
if(递归包含(当前)){
返回true;
}
if(已访问。包含(当前)){
返回false;
}
已访问。添加(当前);
递归。添加(当前);
对于(T相邻:邻接函数。应用(当前)){
if(doIsCyclic(相邻、邻接函数、访问、递归)){
返回true;
}
}
递归。删除(当前);
返回false;
}
}
通过消除递归的非科学方法,速度稍微快了5毫秒
@UtilityClass
public class CycleChecks {
/**
* Take a root object and a function to get its edges to see if there are any cycles.
*
* @param root root object
* @param adjacentFunction a function that would take an object and return an iterable of objects that are adjacent to it.
* @param <T> an object that has edges
* @return has a cycle.
*/
public <T> boolean isCyclic(T root, Function<T, Iterable<T>> adjacentFunction) {
final Set<T> visited = new HashSet<>();
final Deque<T> recursion = new LinkedList<>();
final Deque<Node<T>> nodesToVisit = new LinkedList<>();
final Node<T> popRecursionNode = new Node<>();
nodesToVisit.add(new Node<>(root));
while (!nodesToVisit.isEmpty()) {
final Node<T> frontOfQueue = nodesToVisit.pop();
if (frontOfQueue.isPopRecursion()) {
recursion.pop();
continue;
}
T current = frontOfQueue.getObj();
if (recursion.contains(current)) {
return true;
}
if (visited.contains(current)) {
continue;
}
visited.add(current);
recursion.push(current);
for (T adjacent : adjacentFunction.apply(current)) {
nodesToVisit.add(new Node<>(adjacent));
}
nodesToVisit.add(popRecursionNode);
}
return false;
}
@Data
private static class Node<T> {
private final T obj;
private final boolean popRecursion;
public Node(T obj) {
this.obj = obj;
popRecursion = false;
}
public Node() {
obj = null;
popRecursion = true;
}
}
@UtilityClass
公共类自行车{
/**
*取一个根对象和一个函数来获取它的边,看看是否有循环。
*
*@param根对象
*@param adjacentFunction获取对象并返回与其相邻的对象的iterable的函数。
*@param有边的对象
*@return有一个循环。
*/
公共布尔是循环的(T根,函数邻接ntf函数){
访问的最终集=新HashSet();
final Deque recursion=new LinkedList();
final Deque nodesToVisit=新建LinkedList();
最终节点popRecursionNode=新节点();
添加(新节点(根));
而(!nodesToVisit.isEmpty()){
最终节点frontOfQueue=nodesToVisit.pop();
if(frontOfQueue.isPopRecursion()){
recursion.pop();
持续
}
T current=frontOfQueue.getObj();
if(递归包含(当前)){
返回true;
}
if(已访问。包含(当前)){
持续
}
已访问。添加(当前);
递归推(当前);
对于(T相邻:邻接函数。应用(当前)){
添加(新节点(相邻));
}
添加(popRecursionNode);
}
返回false;
}
@资料
私有静态类节点{
私人终审法院;
私有最终布尔递归;
公共节点(T obj){
this.obj=obj;
popRecursion=false;
}
公共节点(){
obj=null;
popRecursion=true;
}
}
乍一看,这个链接问题似乎解决了您在这里要做的事情。您能详细说明一下在将它转换为您的环境时遇到的困难吗?基本上,链接问题使用数组,我只是使用集合。没有任何区别。您需要查找“拓扑排序”。只需使用tes即可ted你的方法。我在这个测试技能中失败了skill1=新技能();skill2=新技能