【问题标题】:Problem while implementing Find() method for a disjoint set为不相交集实现 Find() 方法时出现问题
【发布时间】:2018-12-13 07:33:18
【问题描述】:

一段时间以来一直在尝试解决这个问题。我得到了一个不相交集中的节点。节点的实现是一个类,它要么有不同的父节点,要么是自己的

class Node {
    Node parent;
    int rank;
    //...constructor and method declarations}

现在,我必须为这个类实现带有路径压缩的 Find 方法,称为 getParent() 方法。我写了以下代码:

Node getParent(){
    while(parent != this)
    {
        parent = parent.getParent();  
    }
    return parent;

我得到了这个代码的无限递归。我知道这是有问题的,但我不知道如何解决它。我尝试使用 this 关键字,但它导致了同样的错误。 请帮忙。此外,了解如何以迭代方式编写此代码将是一个很好的加分项。 另外,我想重构树,使得调用 getParent() 的节点和根节点之间的所有中间节点都有一个共同的父节点,即根节点。

【问题讨论】:

  • 我不明白你为什么在getParent() 方法中使用while 语句而不是return parent
  • getParent() 方法的目的是什么?返回节点的第一个父节点还是找到父节点的根?
  • @hamidghasemi 的目的是找到分支上所有父母的根,并将该根分配给分支上的所有孩子、孙子等
  • @VedantPathak 斯蒂芬回答了您问题的第一部分(查找分支中所有元素的根)。在您问题的第二部分(将该根分配给分支上的所有子代、孙代等),您是否要破坏当前的树结构并创建最大深度为 1 的树?还是我误解了你的要求?
  • @hamidghasemi 是的,这是正确的,我确实想创建一棵最大深度为 1 的树

标签: java disjoint-sets


【解决方案1】:

斯蒂芬已经回答了您问题的第一部分,我认为这已经足够完整了。

关于您问题的第二部分,我假设您将所有节点都放在一个列表中。所以你有List<Node> allNodes。重构树所需的方法是:

public void reStructureTree(List<Node> allNodes){
    for(Node item: allNodes){
        Node p = item.parent;
        List<Node> branch = new ArrayList<>();

        while (p.parent != p){
            branch.add(p);
            p = p.parent;
        }

        for(Node branchItem : branch){
            branchItem.parent = p;
        }
    }
}

这种方法在最坏情况下的顺序是O(n^2),我没有寻找更好的方法,因为你没有提到它的重要性。

【讨论】:

  • 我没有节点列表,但我认为这并不重要,因为您已经使用新列表来存储所有中间节点。另外,是的,find 的时间复杂度很重要,我错过了这一点,但是我正在处理的问题的结构是这样的,它不会出现最坏的情况,因为不会调用 getParent()所有节点。
  • @VedantPathak 这是代码中的一个错误,我对其进行了更新。在上述方法中,您将选择每个节点,并更新该节点的父节点以及该节点与根节点之间的所有中间节点。而且我认为通过这种方式,您将拥有最佳的复杂性,因为它只处理每个节点一次。
【解决方案2】:
Node getParent() {
    while (parent != this) {
        parent = parent.getParent();  
    }
    return parent;
}

问题:

  1. 您正在改变parent 字段。
  2. 您应该测试父级的父级是否是它自己......而不是这个。

您的代码应该是:

Node getParent() {
    Node p = parent;
    while(p.parent != p) {
        p = p.parent;  
    }
    return p;
}

或递归:

Node getParent() {
    return (parent == this) ? this : parent.getParent();
}

(在这种情况下,迭代版本更安全。您不应该获得“无限”递归,但仍然存在递归太深的风险,具有病态深度的数据结构。)


更新 - 显然,您的意图是 getParent 应该(逐步)折叠它访问的每个 Node 的父链。在这种情况下,递归解决方案将是:

Node getParentAndCollapse() {
    if (parent == this) {
        return this;
    } else {
        parent = parent.getParentAndCollapse();
        return parent;
    }
}

折叠整个链的迭代解决方案需要对链进行两次遍历:

  1. 遍历链找到最终父代。
  2. 再次遍历链,更新所有parent 字段以引用最终父级。
  3. 返回最终父级。

类似的东西

Node getParentAndCollapse() {
    Node p = getParent();  // iterative version
    Node n = this;
    while (n.parent != p) {
        Node np = n.parent;
        n.parent = p;
        n = np;
    }
    return p;
}

【讨论】:

  • 我理解寻找父母的父母部分,但为什么改变父母的价值会导致问题?编译器不应该分支到parent.getParent() 方法调用并仅在找到根时才返回吗?
  • 您是否想要改变节点的父字段?
  • 是的,我希望在方法调用完成执行后,分支上所有节点的父字段都指向根。
  • 您需要解决您的问题。您向我们展示了一些错误的代码并询问“问题是什么”而没有清楚地解释(在问题中)代码应该做什么,
猜你喜欢
  • 2015-10-28
  • 2022-12-12
  • 1970-01-01
  • 2019-05-31
  • 2018-03-05
  • 2017-04-08
  • 1970-01-01
  • 1970-01-01
  • 2011-10-29
相关资源
最近更新 更多