stevensonmt
Union Find algorithm help - am having to iterate over all the “parents” a second time
I’m trying to do a very basic union-find (not doing any path compression by rank or size).
def find(dsu, x) do
case Map.get(dsu, x) do
nil ->
{Map.put(dsu, x, x), x}
^x -> {dsu, x}
y ->
{dsu2, p} = find(dsu, y)
{Map.put(dsu2, x, p), p}
end
end
def union(%{} = dsu, x, y) do
{dsu, a} = find(dsu, x)
{dsu, b} = find(dsu, y)
if a == b do
dsu
else
[a,b] = Enum.sort([a,b])
{dsu, c} = find(dsu, a)
Map.put(dsu, b, c)
end
end
The issue I’m having is that after iterating over a set of nodes I have to iterate over all the “parents” a second time because the find function isn’t recursing on those.
@spec num_islands(grid :: [[char]]) :: integer
def num_islands(grid) do
map = LandMap.new(grid)
m = grid |> length() |> Kernel.-(1)
n = hd(grid) |> length() |> Kernel.-(1)
intermediate =
0..m
|> Enum.reduce(%{}, fn x, acc ->
0..n
|> Enum.filter(fn y -> MapSet.member?(map, {x,y}) end)
|> Enum.reduce(acc, fn y, acc2 ->
acc3 = DSU.union(acc2, {x,y}, {x,y})
LandMap.neighbors({x,y}, m, n)
|> Enum.filter(fn coord -> MapSet.member?(map, coord) end)
|> Enum.reduce(acc3, fn neighbor, acc4 -> DSU.union(acc4, neighbor, {x,y}) end)
end)
end)
intermediate
|> Map.values()
|> Enum.reduce(intermediate, fn x, islands ->
case DSU.find(islands, x) do
{^islands, ^x} -> islands
{islands, y} ->
islands
|> Enum.map(fn {k, v} = curr ->
if v == x do
{k, y}
else
curr
end
end)
|> Map.new()
true -> islands
end
end)
|> Map.values()
|> Enum.uniq()
|> Enum.count()
end
If I don’t do the second pass a node that was initially it’s own parent but later linked to others will get updated, but any nodes pointing to it as parent would not be updated. Do I need to track a “parents” map and a “children” map so that I can update any children when a “parent” is updated?
Marked As Solved
hst337
defmodule DisjoinSets do
def add(disjoint_sets, entry) do
Map.put(disjoint_sets, entry, {:root, 0})
end
def find(disjoint_sets, entry) do
with {:ok, root, _rank} <- do_find(disjoint_sets, entry) do
{:ok, root}
end
end
defp do_find(disjoint_sets, entry) do
case disjoint_sets do
%{^entry => {:root, rank}} ->
{:ok, entry, rank}
%{^entry => {:parent, parent}} ->
do_find(disjoint_sets, parent)
%{} ->
{:error, :not_present}
end
end
def union(disjoint_sets, left, right) do
with(
{:ok, left_parent, left_rank} <- do_find(disjoint_sets, left),
{:ok, right_parent, right_rank} <- do_find(disjoint_sets, right)
) do
cond do
left_rank < right_rank ->
{:ok, Map.put(disjoint_sets, left_parent, {:parent, right_parent})}
left_rank > right_rank ->
{:ok, Map.put(disjoint_sets, right_parent, {:parent, left_parent})}
left_rank == right_rank ->
disjoint_sets =
disjoint_sets
|> Map.put(right_parent, {:parent, left_parent})
|> Map.put(left_parent, {:root, left_rank + 1})
{:ok, disjoint_sets}
end
end
end
end
I wrote this simple rank-based disjoint set implementation. I chose verbosity and ease of reading over performance
Also Liked
hst337
Could you please describe original task and what “union find algorithm” is for?
stevensonmt
Leetcode Number of Islands problem: LeetCode - The World's Leading Online Programming Learning Platform
Also in case you meant what “union find” means, a lot of places refer to it as “disjoint set” I think.
code-shoily
I will look into it after work. It’s surprising how hard on memory disjoint set is despite being a simple algorithm.
I looked into number of island and I think using :digraph and connected component could help?
Back to your DS algorithm I’m interested to trace it but if it helps let me send you an implementation I did: https://github.com/code-shoily/ex_algo/blob/main/lib/ex_algo/set/disjoint_set.ex
Thank you for motivating me to look into that again, I’ll be back soon
stevensonmt
haha, I’ve had your code in an open tab the whole time I’ve been working on this and the Most Stones Removed problem. I had no chance getting it right until I read your code.
code-shoily
Do you have any “intuitive” feel towards disjoint set? I seem to lose my understanding of it after some time of not using it. I looked at my own code and was like, wtf was I thinking?







