Union + Find算法(不相交集)的应用

时间:2018-08-23 15:57:17

标签: java algorithm disjoint-sets union-find

问题陈述:

方程式以A / B = k的形式给出,其中AB是表示为字符串的变量,而k是实数(浮点数)。

给出一些查询,返回答案。如果答案不存在,则返回-1.0。

示例:给出a / b = 2.0, b / c = 3.0.

查询是:a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? 返回[6.0, 0.5, -1.0, 1.0, -1.0 ]

输入是:

vector<pair<string, string>> equations
vector<double>& values
vector<pair<string, string>> queries 

其中equations.size() == values.size(),且值为正。

这代表方程。

返回vector<double>

根据上面的示例: 方程= [ ["a", "b"], ["b", "c"] ]

值= [2.0, 3.0]

查询= [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]

输入始终有效。您可能会假设对查询进行评估不会导致被零除,也没有矛盾。

解决方案 可以使用Union + Find on Disjoint set来解决此问题,此处显示一个解决方案:

Solution

但是,我不清楚第59行背后的直觉:

rst[i] = uf.rank.get(queries[i][0]) / uf.rank.get(queries[i][1]);

以及第99行:

rank.put(aFather, quotient * rank.get(b) / rank.get(a));

1 个答案:

答案 0 :(得分:4)

不难发现会发生什么。其实很聪明!

让我们举一个更复杂的例子:

a / b = 2.0, b / c = 3.0, c / d = 4.0, d / e = 5.0

在第一步(由UnionFind uf = new UnionFind(set)触发的 MakeSet )中,将每个元素设置为其自己的父级,并将所有等级设置为1.0:

parent(a) = a, rank(a) = 1.0
...
parent(e) = e, rank(e) = 1.0

Union 步骤中,将节点的等级设置为给定商,而父级的等级保持不变(第99行)。因此,在union(a, b, 2.0) parent(a) = b, rank(a) = 2.0之后,对于任何节点n:rank(n)/rank(parent(n)) = value都保持不变,其中,值来自正在处理的方程式(quotient参数)。 最后,我们得到:

parent(a) = b, rank(a) = 2.0
parent(b) = c, rank(b) = 3.0
parent(c) = d, rank(c) = 4.0
parent(d) = e, rank(d) = 5.0
parent(e) = e, rank(e) = 1.0

Compress 步骤中,如果要搜索的节点的父级不是集合的代表节点,则通过递归搜索将其设置为父级的父级...将当前节点的等级设置为当前等级乘以父级的等级(第87行)。所以最后我们到达:

parent(a) = e, rank(a) = 120.0
parent(b) = e, rank(b) = 60.0
parent(c) = e, rank(c) = 20.0
parent(d) = e, rank(d) = 5.0
parent(e) = e, rank(e) = 1.0

所以确实确实rank(a)= rank(b)* 2.0,rank(b)= rank(c)* 3.0等。

请注意,集合(即最终父节点,在此示例中为e)的代表节点始终以1.0排名结束。这就是为什么一旦计算出节点并设置了父节点后,重复调用compressedFind并执行第87行就不会进一步更改节点的秩的原因。

现在很容易看到第59行的工作原理:如果查询是a / b,则rank(a)/ rank(b)= 120.0 / 60.0 = 2.0

此处使用的术语:https://en.wikipedia.org/wiki/Disjoint-set_data_structure