我来自命令式背景,正在尝试实现一个简单的不相交集(“union-find”)数据结构,以练习在 Haskell 中创建和修改(持久)数据结构。目标是有一个简单的实现,但我也关心效率,我的问题与此有关。
首先,我创建了一个不相交集的森林实现,并使用按等级联合,并从定义“点”的数据类型开始:
data Point = Point
{ _value :: Int
, _parent :: Maybe Point
, _rank :: Int
} deriving Show
脱节集森林是一个IntMap
带有Int → Point
映射的:
type DSForest = IntMap Point
empty :: DSForest
empty = I.empty
单例集只是从其值x到值为x的 Point的映射,没有父级且等级为 1:
makeSet :: DSForest -> Int -> DSForest
makeSet dsf x = I.insert x (Point x Nothing 0) dsf
现在,有趣的部分—— union
。此操作将通过将另一个点设置为其父点来修改一个点(并在某些情况下更改其等级)。在Point
s 等级不同的情况下,Point
简单地“更新”(创建一个新点)以使其父点指向另一个。在它们相等的情况下,Point
创建一个新的,其等级增加一:
union :: DSForest -> Int -> Int -> DSForest
union dsf x y | x == y = dsf
union dsf x y =
if _value x' == _value y'
then dsf
else case compare (_rank x') (_rank y') of
GT -> I.insert (_value y') y'{ _parent = Just x' } dsf
LT -> I.insert (_value x') x'{ _parent = Just y' } dsf
-- 1) increase x's rank by one:
EQ -> let x'' = x'{ _rank = _rank x' + 1 }
-- 2) update the value for x's rank to point to the new x:
dsf' = I.insert (_value x'') x'' dsf
-- 3) then update y to have the new x as its parent:
in I.insert (_value y') y'{ _parent = Just x'' } dsf'
where x' = dsf ! findSet dsf x
y' = dsf ! findSet dsf y
现在,对于我真正的问题,如果在这种EQ
情况下我做了以下事情:
EQ -> let dsf' = I.insert (_value x') x'{ _rank = _rank x' + 1} dsf
in I.insert (_value y') y'{ _parent = Just x'{ _rank = _rank x' + 1 }} dsf'
即首先插入一个新的Point
x并增加其等级,然后让y'
' 的父级成为一个新的Point
x并增加其等级,这是否意味着它们不再指向Point
内存中的相同?(这有关系吗?在使用/创建持久数据结构时我应该担心这些事情吗?)
为了完整起见,这里是findSet
:
findSet :: DSForest -> Int -> Int
findSet dsf' x' = case _parent (dsf' ! x') of
Just (Point v _ _) -> findSet dsf' v
Nothing -> x'
(也欢迎对这段代码的效率和设计发表一般评论。)