No, the loops have a definite purpose.
If you look at the Wikipedia Disjoint-set data structure article, the loops essentially perform i = find(p); and j = find(q); .
Remember, id[p] tells you only the parent of p. To find the root of p, you need to first look at i = id[p], then at i = id[i] repeatedly, until i == id[i]. Only then you have found the root, i, for element p. This is how a disjoint set works, and the aforelinked Wikipedia article explains it pretty well.
An example illustrates the issue quite nicely, I believe.
Consider the following disjoint set:
0 1 2 3 4 5 6 7 8 9 (indexes)
0 0 2 3 2 3 4 5 8 9 (values in the id array)
Logically, 0 1 form one set, 2 4 6 form another set, 3 5 7 form yet another set, and 8 and 9 are their own sets too.
If we want to perform Union(6,7), we cannot just modify id[6] and id[7]; that would detach them from their current sets.
Instead, we must first find the root of 6, i.e. i = find(6):
i = id[6]; (so i == 4)
i = id[i]; (so i == 2)
i == id[i];
so the root of 6 is 2, i = find(6) = 3.
The root of 7 is j = find(7):
j = id[7]; (so j == 5)
j = id[j]; (so j == 3)
j == id[j];
so the root of 7 is 3, j = find(7) == 3.
The union only modifies the root entries. Your code uses
id[i] = j;
so the original disjoint set becomes
0 1 2 3 4 5 6 7 8 9 (indexes)
0 0 3 3 2 3 4 5 8 9 (values in the id array)
Now, entries 0 1 form one disjoint set, 2 3 4 5 6 7 for another set, and 8 and 9 are their own sets too.
If we perform path compression on all entries (as it happens, only need to do it on entries 6 and 7 in this particular case), the disjoint set becomes
0 1 2 3 4 5 6 7 8 9 (indexes)
0 0 3 3 3 3 3 3 8 9 (values in the id array)
and it is much easier to see which set each entry belongs to.
If you have graphviz -- it's available in repositories for all Linux distributions, no need to download from the web --, you can very easily illustrate disjoint sets. The .dot file for the two cases above are
Code:
digraph {
rankdir=lr;
splines=true;
overlap=false;
esep="0,1";
"0" [ pos="0,0"; pin="true" ];
"1" [ pos="1,0"; pin="true" ];
"2" [ pos="2,0"; pin="true" ];
"3" [ pos="3,0"; pin="true" ];
"4" [ pos="4,0"; pin="true" ];
"5" [ pos="5,0"; pin="true" ];
"6" [ pos="6,0"; pin="true" ];
"7" [ pos="7,0"; pin="true" ];
"8" [ pos="8,0"; pin="true" ];
"9" [ pos="9,0"; pin="true" ];
"1" -> "0";
"4" -> "2";
"5" -> "3";
"6" -> "4";
"7" -> "5";
}
which neato renders as
and
Code:
digraph {
rankdir=lr;
splines=true;
overlap=false;
esep="0,1";
"0" [ pos="0,0"; pin="true" ];
"1" [ pos="1,0"; pin="true" ];
"2" [ pos="2,0"; pin="true" ];
"3" [ pos="3,0"; pin="true" ];
"4" [ pos="4,0"; pin="true" ];
"5" [ pos="5,0"; pin="true" ];
"6" [ pos="6,0"; pin="true" ];
"7" [ pos="7,0"; pin="true" ];
"8" [ pos="8,0"; pin="true" ];
"9" [ pos="9,0"; pin="true" ];
"1" -> "0";
"2" -> "3";
"4" -> "2";
"5" -> "3";
"6" -> "4";
"7" -> "5";
}
which neato renders as
It is trivial to add a function that outputs the final state of the id[] array describing the disjoint set data structure in Dot format. That is actually what I often do, with other abstract data structures, too. Graphviz has been an indispensable tool for me in exploring some horribly complicated structures. Even though it is free and open source, it is a top-notch tool.