我一直在研究快速联合算法。下面的代码是实现的示例。 有人可以向我解释 root方法中发生了什么?
public class quickUnion {
private int[] id;
public void QuickUnionUF(int N){
id = new int [N];
for(int i = 0; i < N; i++){
id[i] = i;
}
}
private int root(int i){
while (i != id[i]){
i = id[i];
}
return i;
}
public boolean connected(int p, int q){
return root(p) == root(q);
}
public void union(int p, int q){
int i = root(p);
int j = root(q);
id[i] = j;
}
}
答案 0 :(得分:2)
union find的核心原则是每个元素都属于一个不相交的元素集。这意味着,如果你绘制一个森林(一组树),森林将包含所有元素,并且没有元素将在两个不同的树中。
构建这些树时,您可以想象任何节点都有父节点或根节点。在union find的这个实现中(以及在大多数union find实现中),每个元素的父元素都存储在该元素索引的数组中。因此,相当于id [i]的元素是i的父元素。
您可能会问:如果我没有父母(也就是根),该怎么办?在这种情况下,惯例是将i设置为自身(我是它自己的父)。因此,id [i] ==我只是检查我们是否已到达树的根。
将所有这些放在一起,根函数从起始节点遍历树(父对象)直到它到达根。然后它返回根。
暂且不说: 为了使这个算法更快地到达根,一般实现将“压扁”树:你需要通过根目录所需的父亲越少,根函数返回的速度就越快。因此,在许多实现中,您将看到另一个步骤,其中您将元素的父级设置为其原始祖父级(id [i] = id [id [i]])。
答案 1 :(得分:1)
这里算法的要点是:始终保持一个顶点的根等于它自己。
合并根目录
在所有情况下,我们始终保持惯例:如果x是基础根,id[x] == x
这是算法的要点。
答案 2 :(得分:0)
从课程Union find
中提供的Pdf文件我的根是id [id [id [... id [i] ...]]]。
根据给定的例子
public int root(int p){
while(p != id[p]){
p = id[p];
}
return p;
}
让我们考虑一下情况:
现在让我们来电话
root(3)
根法中循环的干运行是:
答案 3 :(得分:0)
要了解 root
方法的作用,需要了解这种数据结构如何帮助将值组织成不相交的集合。
它通过构建树来实现。每当两个独立的值 ? 和 ? 被认为属于同一个集合时, ? 就成为 ? 的孩子(然后是 ? 的父母)。然而,如果 ? 已经有一个父级,那么我们首先移动到 ? 的那个父级,以及那个父级的父级,...直到我们找到一个没有父级的祖先。这是root(p)
,我们称之为?'。如果它有父级,我们对 ? 做同样的事情。让我们称那个祖先为?'。最后,?'变成了一个孩子?'。通过这样做,我们隐式地使原始 ? 和 ? 成员成为同一棵树的成员。
我们怎么知道?和?是同一棵树的成员?通过查找他们的根源。如果它们碰巧有相同的根,那么它们必然在同一棵树中,即它们属于同一个集合。
让我们看一个示例运行:
QuickUnionUF array = new QuickUnionUF(10);
这将创建以下数组:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
这个数组代表边。边的from侧是数组中的索引(0..9),同一边的to侧是value< /em> 在该索引处找到(也是 0..9)。如您所见,数组以所有边都是自引用(循环)的方式初始化。你可以说每个值都是它自己的树的根(没有其他值)。
对任何值 0..9 调用 root
将返回相同的数字,因为对于所有 i
我们有 id[i] == i
。所以在这个阶段 root
并没有给我们太多。
现在,让我们指出两个值实际上属于同一个集合:
array.union(2, 9);
这将导致赋值id[2] = 9
,所以我们得到这个数组:
[0, 1, 9, 3, 4, 5, 6, 7, 8, 9]
图形上,这个建立的链接表示为:
9
/
2
如果现在我们调用 root(2)
,我们将得到 9 作为返回值。这告诉我们 2 与 9 在同一个集合(即树)中,而 9 恰好获得了该树的 root 角色(这是一个任意选择;它也可能是 2) .
让我们也将 3 和 4 链接在一起。这是一个与上面非常相似的案例:
array.union(3, 4);
这会分配 id[3] = 4
并导致此数组和树表示:
[0, 1, 9, 4, 4, 5, 6, 7, 8, 9]
9 4
/ /
2 3
现在让我们让它更有趣。让我们指出 4 和 9 属于同一个集合:
array.union(4, 9);
仍然 root(4)
和 root(9)
只是返回相同的数字(4 和 9)。还没有什么特别的...任务是id[4] = 9
。这导致了这个数组和图表:
[0, 1, 9, 4, 9, 5, 6, 7, 8, 9]
9
/ \
2 4
/
3
注意这个单一的分配是如何将两棵不同的树连接成一棵树的。如果现在我们要检查 2 和 3 是否在同一棵树中,我们调用
if (connected(2, 3)) /* do something */
虽然我们从来没有明确说过 2 和 3 属于同一个集合,但应该从前面的动作中暗示出来。 connected
现在将使用对 root
的调用来暗示这一事实。 root(2)
将返回 9,root(3)
将返回 9。我们可以看到 root
在做什么......它在图中向上走向它的树的根节点是在。该数组具有进行该行走所需的所有信息。给定一个 index,我们可以读取该数字的 parent(索引)数组。这可能必须重复才能到达祖父母,......等等:它可以是短途或长途步行,具体取决于给定节点与其所在树的根之间有多少“边”。>
答案 4 :(得分:-2)
/**
* Quick Find Java Implementation Eager's Approach
*/
package com.weekone.union.quickfind;
import java.util.Random;
/**
* @author Ishwar Singh
*
*/
public class UnionQuickFind {
private int[] itemsArr;
public UnionQuickFind() {
System.out.println("Calling: " + UnionQuickFind.class);
}
public UnionQuickFind(int n) {
itemsArr = new int[n];
}
// p and q are indexes
public void unionOperation(int p, int q) {
// displayArray(itemsArr);
int tempValue = itemsArr[p];
if (!isConnected(p, q)) {
itemsArr[p] = itemsArr[q];
for (int i = 0; i < itemsArr.length; i++) {
if (itemsArr[i] == tempValue) {
itemsArr[i] = itemsArr[q];
}
}
displayArray(p, q);
} else {
displayArray(p, q, "Already Connected");
}
}
public boolean isConnected(int p, int q) {
return (itemsArr[p] == itemsArr[q]);
}
public void connected(int p, int q) {
if (isConnected(p, q)) {
displayArray(p, q, "Already Connected");
} else {
displayArray(p, q, "Not Connected");
}
}
private void displayArray(int p, int q) {
// TODO Auto-generated method stub
System.out.println();
System.out.print("{" + p + " " + q + "} -> ");
for (int i : itemsArr) {
System.out.print(i + ", ");
}
}
private void displayArray(int p, int q, String message) {
System.out.println();
System.out.print("{" + p + " " + q + "} -> " + message);
}
public void initializeArray() {
Random random = new Random();
for (int i = 0; i < itemsArr.length; i++) {
itemsArr[i] = random.nextInt(9);
}
}
public void initializeArray(int[] receivedArr) {
itemsArr = receivedArr;
}
public void displayArray() {
System.out.println("INDEXES");
System.out.print("{p q} -> ");
for (int i : itemsArr) {
System.out.print(i + ", ");
}
System.out.println();
}
}
Main Class:-
/**
*
*/
package com.weekone.union.quickfind;
/**
* @author Ishwar Singh
*
*/
public class UQFClient {
/**
* @param args
*/
public static void main(String[] args) {
int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int n = 10;
UnionQuickFind unionQuickFind = new UnionQuickFind(n);
// unionQuickFind.initializeArray();
unionQuickFind.initializeArray(arr);
unionQuickFind.displayArray();
unionQuickFind.unionOperation(4, 3);
unionQuickFind.unionOperation(3, 8);
unionQuickFind.unionOperation(6, 5);
unionQuickFind.unionOperation(9, 4);
unionQuickFind.unionOperation(2, 1);
unionQuickFind.unionOperation(8, 9);
unionQuickFind.connected(5, 0);
unionQuickFind.unionOperation(5, 0);
unionQuickFind.connected(5, 0);
unionQuickFind.unionOperation(7, 2);
unionQuickFind.unionOperation(6, 1);
}
}
输出:
INDEXES
{p q} -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
{4 3} -> 0, 1, 2, 3, 3, 5, 6, 7, 8, 9,
{3 8} -> 0, 1, 2, 8, 8, 5, 6, 7, 8, 9,
{6 5} -> 0, 1, 2, 8, 8, 5, 5, 7, 8, 9,
{9 4} -> 0, 1, 2, 8, 8, 5, 5, 7, 8, 8,
{2 1} -> 0, 1, 1, 8, 8, 5, 5, 7, 8, 8,
{8 9} -> Already Connected
{5 0} -> Not Connected
{5 0} -> 0, 1, 1, 8, 8, 0, 0, 7, 8, 8,
{5 0} -> Already Connected
{7 2} -> 0, 1, 1, 8, 8, 0, 0, 1, 8, 8,
{6 1} -> 1, 1, 1, 8, 8, 1, 1, 1, 8, 8,