Skip to content

Instantly share code, notes, and snippets.

@ChrisPenner
Last active August 23, 2019 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChrisPenner/9980175db8c7624d56290788dff29f5a to your computer and use it in GitHub Desktop.
Save ChrisPenner/9980175db8c7624d56290788dff29f5a to your computer and use it in GitHub Desktop.
A quick experiment using union-find to determine which pieces of 'land' in a grid are connected. Could ostensibly be used with any equivalence/grouping predicate.
{-# LANGUAGE ScopedTypeVariables #-}
module Lib where
import Data.UnionFind.IO
import Control.Monad
import Control.Applicative
import Data.Foldable
import Data.Maybe
import qualified Data.Map as M
import qualified Data.Set as S
import Data.Traversable
import Data.Tuple
import Control.Arrow
-- .#.#.
-- ...#.
-- #....
-- .#..#
-- .#...
land :: M.Map (Int, Int) (Int, Int)
land = M.fromList . fmap (id &&& id) $ [(0, 1), (0, 3), (1, 3), (2, 0), (3, 1), (3, 4), (4, 1)]
findIslands :: IO [[ (Int, Int) ]]
findIslands = do
pointMap <- traverse fresh land -- O(n)
void . flip M.traverseWithKey pointMap $ \(x, y) point -> do -- O(n)
for_ (catMaybes (flip M.lookup pointMap <$> [(x + 1, y), (x, y + 1)])) -- O(log(n))
$ \neighbourPoint -> do
union point neighbourPoint -- O(1)
withUnionKey :: (M.Map (Int, Int) (Int, Int)) <- for pointMap (repr >=> descriptor) -- O(n)
let unionKeyToCoord :: [((Int, Int), (Int, Int))] = (swap <$> M.toList withUnionKey) -- O(n)
let results = M.fromListWith (<>) $ (fmap (:[]) <$> unionKeyToCoord) -- O(nlogn)
return (M.elems results)
-- >>> findIslands
-- [ [(0, 1)]
-- , [(1, 3), (0, 3)]
-- , [(2, 0)]
-- , [(3, 4)]
-- , [(4, 1), (3, 1)]
-- ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment