【问题标题】:Weighted Quick-Union with Path Compression algorithm带路径压缩算法的加权快速联合
【发布时间】:2017-05-26 16:56:34
【问题描述】:

有一个“带路径压缩的加权快速联合”算法。

代码:

public class WeightedQU 
{
    private int[] id;
    private int[] iz;

    public WeightedQU(int N)
    {
        id = new int[N];
        iz = new int[N];
        for(int i = 0; i < id.length; i++)
        {
            iz[i] = i;
            id[i] = i;
        }
    }

    public int root(int i)
    {
        while(i != id[i])
        {
            id[i] = id[id[i]];   // this line represents "path compression"
            i = id[i];
        }
        return i;
    }

    public boolean connected(int p, int q)
    {
        return root(p) == root(q);
    }

    public void union(int p, int q)   // here iz[] is used to "weighting"
    {
        int i = root(p);
        int j = root(q);
        if(iz[i] < iz[j])
        {
            id[i] = j;
            iz[j] += iz[i];
        }
        else
        {
            id[j] = i;
            iz[i] += iz[j];
        }
    }
}

问题:

  1. 路径压缩如何工作? id[i] = id[id[i]] 表示我们只到达节点的第二个祖先,而不是根。

  2. iz[] 包含从0N-1 的整数。 iz[] 如何帮助我们了解集合中的元素数量?

有人可以帮我澄清一下吗?

【问题讨论】:

  • 阅读 c/c++ 中的算法,第 1-4 部分,robert sedgewick,第 1 章,很好的解释。

标签: java algorithm union-find


【解决方案1】:

首先了解id 是一个森林id[i]i 的父级。如果id[i] == i 表示i 是根。

对于某些根i(其中id[i] == i),iz[i] 中以i 为根的元素的数量。

public int root(int i)
{
    while(i != id[i])
    {
        id[i] = id[id[i]];   // this line represents "path compression"
        i = id[i];
    }
    return i;
}

路径压缩是如何工作的? id[i] = id[id[i]] 表示我们只到达节点的第二个祖先,而不是根。

当我们上升树以找到根时,我们将节点从它们的父母移动到他们的祖父母。这使树部分变平。请注意,此操作不会更改节点所属的树,这就是我们感兴趣的全部。这就是路径压缩技术。

(您确实注意到了循环对吗?while(i == id[i])i 是根节点时终止)

iz[] 包含从0N-1 的整数。 iz[] 如何帮助我们知道集合中的元素数量?

代码中存在转录错误:

for(int i = 0; i < id.length; i++)
{
    iz[i] = i; // WRONG
    id[i] = i;
}

这是正确的版本:

for(int i = 0; i < id.length; i++)
{
    iz[i] = 1; // RIGHT
    id[i] = i;
}

iz[i] 是以i 为根的树的元素数(或者如果i 不是根,则iz[i] 未定义)。所以应该初始化为1,而不是i。最初,每个元素都是一个单独的“单例”树,大小为1

【讨论】:

  • 关于路径压缩,这是路径压缩的单遍变体,使路径中的每个其他节点都指向其祖父节点(路径长度减半。)而两遍更像是我们添加一个第二个循环到 root() 将每个检查节点的 id[] 设置为根。似乎与添加相关。
【解决方案2】:

id[i] = id[id[i]]; // 这一行代表“路径压缩”

上面的代码是联合查找幻灯片中提到的“更简单的一次性变体”(算法,第一部分,Kevin Wayne 和 Robert Sedgewick)。因此,您对问题 1 的猜测是正确的。每个被检查的节点都指向它的祖父母。

为了使每个检查的节点都指向根,我们需要两遍实现:

  /**
 * Returns the component identifier for the component containing site <tt>p</tt>.
 * @param p the integer representing one site
 * @return the component identifier for the component containing site <tt>p</tt>
 * @throws java.lang.IndexOutOfBoundsException unless 0 <= p < N
 */
public int find(int p) {
    int root = p;
    while (root != id[root])
        root = id[root];
    while (p != root) {
        int newp = id[p];
        id[p] = root;
        p = newp;
    }
    return root;
}

参考: http://algs4.cs.princeton.edu/15uf/WeightedQuickUnionPathCompressionUF.java.html

【讨论】:

    【解决方案3】:

    这里还有一点需要注意:

    在我们制作id[i]=id[id[i]]时找到根,即;使我在其祖父母之下

    -然后id[i] 的大小将减少 i i,e 的大小; iz[id[i]]-=iz[i]

    现在这使代码完全正确。

    对此我不确定,但直觉上我觉得, 它的缺失不会导致问题,因为我们总是在比较根的大小。

    【讨论】:

      【解决方案4】:

      问题 1。 说这行 id[i] = id[id[i]]; 是不对的。只到达根的第二个祖先。你会意识到 while 循环 while(i != id[i]) 仅在节点 i 指向根时停止,即当 i == id[i] 时。此时我们应该使用行 id[i] = id[id[i]] 将节点指向根;其中内部 id[i] 是根。

      问题 2。

      你错误的初始化 iz[i] = i;实际上应该是 iz[i] = 1;意思是,每个节点大小在开始时都初始化为 1,因为它们的大小为 1。 在联合函数中,您意识到我们有 iz[j] += iz[i];和 iz[i] += iz[j];它将根节点的大小更新为连接在一起的两个组件的大小之和。这可以有效地更新节点大小。

      【讨论】:

        猜你喜欢
        • 2022-10-23
        • 1970-01-01
        • 2013-08-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-22
        相关资源
        最近更新 更多