type BSTree a = BinaryTree a
data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
deriving Show
flattenTree :: BinaryTree a -> [a]
flattenTree tree = case tree of
Null -> []
Node left val right -> (flattenTree left) ++ [val] ++ (flattenTree right)
isBSTree :: (Ord a) => BinaryTree a -> Bool
isBSTree btree = case btree of
Null -> False
tree -> (flattenTree tree) == sort (flattenTree tree)
私がやりたいことは、指定されたツリーがバイナリ検索ツリーであるかどうかを判別する関数を書くことです。私の方法は、リスト内のすべての値をグループ化してData.List
をインポートし、リストをソートしてそれらが等しいですが、少し複雑です。他のモジュールをインポートせずにこれを行うことはできますか?
これは、ツリーを平坦化せずにそれを行う方法です。
定義から、ここでは、
data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
deriving Show
Node
と括弧を無視して、ツリーを左から右にトラバースすると、Null
sとa
sの交互のシーケンスが得られることがわかります。つまり、2つの値の間にNull
があります。
私の計画は、各サブツリーが適切なrequirementsを満たしていることを確認することですrefine各Node
の要件を確認し、間にある値を覚えてから- test各Null
でそれら。値のすべての順序ペアの間にNull
があるため、すべての順序(左から右)のペアが減少しないことをテストします。
要件とは何ですか? looseツリーの値の下限と上限です。左端と右端の要件を含む要件を表すために、次のようにBot
tom要素とTop
要素を使用して順序を拡張できます。
data TopBot a = Bot | Val a | Top deriving (Show, Eq, Ord)
ここで、与えられたツリーが、与えられた境界の間と順序の間にあるという要件を満たしていることを確認します。
ordBetween :: Ord a => TopBot a -> TopBot a -> BinaryTree a -> Bool
-- tighten the demanded bounds, left and right of any Node
ordBetween lo hi (Node l x r) = ordBetween lo (Val x) l && ordBetween (Val x) hi r
-- check that the demanded bounds are in order when we reach Null
ordBetween lo hi Null = lo <= hi
二分探索木は、Bot
とTop
の間にある木です。
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordBetween Bot Top
各サブツリーでactual極値を計算し、それらを外側にバブリングすると、必要以上の情報が得られ、左または右のサブツリーが空であるEdgeの場合は面倒です。 requirementsを維持してチェックすることは、それらを内部にプッシュすることで、かなり均一になります。
ここにヒントがあります:補助関数を作る
isBSTree' :: (Ord a) => BinaryTree a -> BSTResult a
ここで、BSTResult a
は次のように定義されています
data BSTResult a
= NotBST -- not a BST
| EmptyBST -- empty tree (hence a BST)
| NonEmptyBST a a -- nonempty BST with provided minimum and maximum
サブツリーの結果を利用して、特に最小値と最大値を計算して再帰的に処理できるはずです。
たとえば、tree = Node left 20 right
があり、isBSTree' left = NonEmptyBST 1 14
とisBSTree' right = NonEmptyBST 21 45
がある場合、isBSTree' tree
はNonEmptyBST 1 45
になります。
tree = Node left 24 right
を除いて同じケースでは、代わりにisBSTree' tree = NotBST
が必要です。
結果をBool
に変換するのは簡単です。
はい、リストをソートする必要はありません。すべての要素が次の要素以下かどうかを確認できます。これはO(n)で実行できるため、より効率的ですが、ソートされたリストを完全に評価しますはO(nlogn)をとります。
したがって、これを次のように確認できます。
ordered :: Ord a => [a] -> Bool
ordered [] = True
ordered xa@(_:xs) = and (zipWith (<=) xa xs)
ですから、二分木が二分探索木であるかどうかをチェックすることができます:
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordered . flattenTree
Null
は空のツリーなので、それ自体がバイナリ検索ツリーであると主張できると思います。したがって、これは、すべてのノード(ノードがない)で、左側のサブツリーの要素がノードの値以下であり、右側のサブツリーの要素がすべてノードの値以上であることを意味します。 。
次のようにツリーを左から右に進めることができます。
isBSTtreeG :: Ord a => BinaryTree a -> Bool
isBSTtreeG t = Gopher Nothing [Right t]
where
Gopher _ [] = True
Gopher x (Right Null:ts) = Gopher x ts
Gopher x (Right (Node lt v rt):ts) = Gopher x (Right lt:Left v:Right rt:ts)
Gopher Nothing (Left v:ts) = Gopher (Just v) ts
Gopher (Just y) (Left v:ts) = y <= v && Gopher (Just v) ts
John McCarthyのGopher
に触発されました。
明示的なプッシュダウンリストは、継続渡しで削除できます。
isBSTtreeC :: Ord a => BinaryTree a -> Bool
isBSTtreeC t = Gopher Nothing t (const True)
where
Gopher x Null g = g x
Gopher x (Node lt v rt) g = Gopher x lt (\case
Nothing -> Gopher (Just v) rt g
Just y -> y <= v && Gopher (Just v) rt g)
largest-so-far要素を1つ維持するだけで十分です。