1.5.2 #
解答 #
quick-union 的官方实现:QuickUnionUF.java。
和上题一样的方式,增加一个记录访问数组次数的类成员变量,在每次访问数组的语句执行后自增即可。
程序输出的森林,用缩进表示子树:
|---- 0
|---- 9
|---- 1
|---- 2
|---- 3
|---- 4
|---- 5
|---- 6
|---- 7
|---- 8
数组访问:1
|---- 0
|---- 9
|---- 1
|---- 2
|---- 4
|---- 3
|---- 5
|---- 6
|---- 7
|---- 8
数组访问:1
|---- 0
|---- 9
|---- 1
|---- 2
|---- 4
|---- 3
|---- 6
|---- 7
|---- 8
|---- 5
数组访问:1
|---- 0
|---- 9
|---- 1
|---- 2
|---- 7
|---- 4
|---- 3
|---- 6
|---- 8
|---- 5
数组访问:1
|---- 0
|---- 9
|---- 1
|---- 2
|---- 7
|---- 4
|---- 3
|---- 6
|---- 8
|---- 5
数组访问:1
|---- 0
|---- 9
|---- 1
|---- 2
|---- 7
|---- 8
|---- 5
|---- 4
|---- 3
|---- 6
数组访问:7
|---- 1
|---- 2
|---- 7
|---- 8
|---- 5
|---- 4
|---- 0
|---- 9
|---- 3
|---- 6
数组访问:3
|---- 1
|---- 2
|---- 7
|---- 4
|---- 0
|---- 9
|---- 3
|---- 8
|---- 5
|---- 6
数组访问:3
代码 #
QuickUnionUF.cs,这个类继承了 UF.cs,重新实现了 Union() 和 Find() 等方法。 关于 UF.cs 可以参见原书中文版 P138 或英文版 P221 的算法 1.5。
public class QuickUnionUf : Uf
{
/// <summary>
/// 数组访问次数的计数器。
/// </summary>
/// <value>当前数组访问次数。</value>
public int ArrayVisitCount { get; private set; }
/// <summary>
/// 建立使用 QuickUnion 的并查集。
/// </summary>
/// <param name="n">并查集的大小。</param>
public QuickUnionUf(int n) : base(n) { }
/// <summary>
/// 重置数组访问计数。
/// </summary>
public virtual void ResetArrayCount()
{
ArrayVisitCount = 0;
}
/// <summary>
/// 获得 parent 数组。
/// </summary>
/// <returns>返回 parent 数组。</returns>
public int[] GetParent()
{
return Parent;
}
/// <summary>
/// 寻找一个结点所在的连通分量。
/// </summary>
/// <param name="p">需要寻找的结点。</param>
/// <returns>该结点所属的连通分量。</returns>
public override int Find(int p)
{
Validate(p);
while (p != Parent[p])
{
p = Parent[p];
ArrayVisitCount += 2;
}
return p;
}
/// <summary>
/// 将两个结点所属的连通分量合并。
/// </summary>
/// <param name="p">需要合并的结点。</param>
/// <param name="q">需要合并的另一个结点。</param>
public override void Union(int p, int q)
{
var rootP = Find(p);
var rootQ = Find(q);
if (rootP == rootQ)
{
return;
}
Parent[rootP] = rootQ;
ArrayVisitCount++;
TotalCount--;
}
}
Main 方法
var input = "9-0 3-4 5-8 7-2 2-1 5-7 0-3 4-2".Split(' ');
var quickUnion = new QuickUnionUf(10);
foreach (var s in input)
{
quickUnion.ResetArrayCount();
var numbers = s.Split('-');
var p = int.Parse(numbers[0]);
var q = int.Parse(numbers[1]);
quickUnion.Union(p, q);
var parent = quickUnion.GetParent();
for (var i = 0; i < parent.Length; i++)
{
if (parent[i] == i)
{
Console.WriteLine("|---- " + i);
Dfs(parent, i, 1);
}
}
Console.WriteLine("数组访问:" + quickUnion.ArrayVisitCount);
}
static void Dfs(int[] parent, int root, int level)
{
for (var i = 0; i < parent.Length; i++)
{
if (parent[i] == root && i != root)
{
for (var j = 0; j < level; j++)
{
Console.Write(" ");
}
Console.WriteLine("|---- " + i);
Dfs(parent, i, level + 1);
}
}
}