web-dev-qa-db-ja.com

木がHaskellの二分探索木かどうかを調べる

  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をインポートし、リストをソートしてそれらが等しいですが、少し複雑です。他のモジュールをインポートせずにこれを行うことはできますか?

10
Jayyyyyy

これは、ツリーを平坦化せずにそれを行う方法です。

定義から、ここでは、

data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
     deriving Show

Nodeと括弧を無視して、ツリーを左から右にトラバースすると、Nullsとasの交互のシーケンスが得られることがわかります。つまり、2つの値の間にNullがあります。

私の計画は、各サブツリーが適切なrequirementsを満たしていることを確認することですrefineNodeの要件を確認し、間にある値を覚えてから- testNullでそれら。値のすべての順序ペアの間にNullがあるため、すべての順序(左から右)のペアが減少しないことをテストします。

要件とは何ですか? looseツリーの値の下限と上限です。左端と右端の要件を含む要件を表すために、次のようにBottom要素と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

二分探索木は、BotTopの間にある木です。

isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordBetween Bot Top

各サブツリーでactual極値を計算し、それらを外側にバブリングすると、必要以上の情報が得られ、左または右のサブツリーが空であるEdgeの場合は面倒です。 requirementsを維持してチェックすることは、それらを内部にプッシュすることで、かなり均一になります。

13
pigworker

ここにヒントがあります:補助関数を作る

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 14isBSTree' right = NonEmptyBST 21 45がある場合、isBSTree' treeNonEmptyBST 1 45になります。

tree = Node left 24 rightを除いて同じケースでは、代わりにisBSTree' tree = NotBSTが必要です。

結果をBoolに変換するのは簡単です。

6
chi

はい、リストをソートする必要はありません。すべての要素が次の要素以下かどうかを確認できます。これは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は空のツリーなので、それ自体がバイナリ検索ツリーであると主張できると思います。したがって、これは、すべてのノード(ノードがない)で、左側のサブツリーの要素がノードの値以下であり、右側のサブツリーの要素がすべてノードの値以上であることを意味します。 。

3

次のようにツリーを左から右に進めることができます。

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つ維持するだけで十分です。

1
Will Ness