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: https://leetcode.com/problems/number-of-islands/
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: ex_algo/lib/ex_algo/set/disjoint_set.ex at main · code-shoily/ex_algo · GitHub
Thank you for motivating me to look into that again, I’ll be back soon
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









