Tìm xem cây có phải là cây tìm kiếm nhị phân trong Haskell không


10
  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)

Điều tôi muốn làm là viết một hàm để xác định xem cây đã cho có phải là cây tìm kiếm nhị phân hay không, phương pháp của tôi là nhóm tất cả các giá trị trong danh sách và nhập Data.Listrồi sắp xếp danh sách để tìm xem chúng có bằng nhau không, nhưng nó là một chút phức tạp. Chúng ta có thể làm điều này mà không cần nhập mô-đun khác?


Tôi sẽ không xác định flattenTreeđầu tiên. Bạn có thể quay lại Falsesớm nếu một nút vi phạm thuộc tính tìm kiếm mà không phải duyệt toàn bộ cây con gốc ở nút đó.
chepner

@chepner vấn đề là với sort, không phải với flattenTree, mà là đủ lười biếng.
Will Ness

Vâng, điều đó xảy ra với tôi sau khi xem một số câu trả lời khác.
chepner

Câu trả lời:


13

Đây là một cách để làm điều đó mà không làm phẳng cây.

Từ định nghĩa, ở đây,

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

người ta có thể thấy rằng đi qua cây từ trái sang phải, bỏ qua Nodevà dấu ngoặc đơn, cung cấp cho bạn một chuỗi xen kẽ của Nulls và as. Đó là, giữa mỗi hai giá trị, có một Null.

Kế hoạch của tôi là kiểm tra xem mỗi cây con có thỏa mãn các yêu cầu phù hợp hay không : chúng ta có thể tinh chỉnh các yêu cầu ở mỗi loại Node, ghi nhớ những giá trị nào chúng ta nằm giữa, sau đó kiểm tra từng giá trị Null. Vì có một Nullcặp giá trị theo thứ tự, chúng tôi sẽ kiểm tra xem tất cả các cặp theo thứ tự (từ trái sang phải) không giảm.

Yêu cầu là gì? Đó là một giới hạn dưới và trên lỏng lẻo trên các giá trị trong cây. Để thể hiện các yêu cầu, bao gồm cả các yêu cầu ở cuối cùng bên trái và bên phải, chúng tôi có thể mở rộng bất kỳ thứ tự nào với Bottom và Topcác phần tử, như sau:

data TopBot a = Bot | Val a | Top deriving (Show, Eq, Ord)

Bây giờ chúng ta hãy kiểm tra xem một cây nhất định có thỏa mãn các yêu cầu về cả thứ tự và giữa các giới hạn nhất định không.

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

Cây tìm kiếm nhị phân là một cây theo thứ tự và giữa BotTop.

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

Tính toán các giá trị cực trị thực tế trong mỗi cây con, đưa chúng ra bên ngoài, cung cấp cho bạn nhiều thông tin hơn bạn cần và rất khó sử dụng trong các trường hợp cạnh trong đó cây con trái hoặc phải trống. Duy trì và kiểm tra các yêu cầu , đẩy chúng vào trong, khá đồng đều.


6

Đây là một gợi ý: tạo một chức năng phụ trợ

isBSTree' :: (Ord a) => BinaryTree a -> BSTResult a

nơi BSTResult ađược định nghĩa là

data BSTResult a
   = NotBST             -- not a BST
   | EmptyBST           -- empty tree (hence a BST)
   | NonEmptyBST a a    -- nonempty BST with provided minimum and maximum

Bạn sẽ có thể tiến hành đệ quy, khai thác kết quả trên các cây con để thúc đẩy tính toán, đặc biệt là tối thiểu và tối đa.

Ví dụ, nếu bạn có tree = Node left 20 right, với isBSTree' left = NonEmptyBST 1 14isBSTree' right = NonEmptyBST 21 45, thì isBSTree' treenên NonEmptyBST 1 45.

Trong trường hợp tương tự ngoại trừ tree = Node left 24 right, thay vào đó chúng ta nên có isBSTree' tree = NotBST.

Chuyển đổi kết quả thành Booltầm thường.


1
hoặc xác định Monoid rõ ràng cho BSTResult avà gấp vào đó. :) (hoặc thậm chí nếu đó không phải là Monoid hợp pháp ....)
Will Ness

(nhưng dù sao thì nó cũng hợp pháp, tôi nghĩ vậy)
Will Ness

3

, bạn không cần phải sắp xếp danh sách. Bạn có thể kiểm tra xem mọi phần tử nhỏ hơn hoặc bằng phần tử tiếp theo. Điều này hiệu quả hơn vì chúng ta có thể làm điều này trong O (n) , trong khi việc đánh giá danh sách được sắp xếp hoàn toàn mất O (n log n) .

Do đó, chúng tôi có thể kiểm tra điều này với:

ordered :: Ord a => [a] -> Bool
ordered [] = True
ordered xa@(_:xs) = and (zipWith (<=) xa xs)

Vì vậy, chúng ta có thể kiểm tra xem cây nhị phân có phải là cây tìm kiếm nhị phân không:

isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordered . flattenTree

Tôi nghĩ người ta có thể khẳng định rằng Nullchính nó là một cây tìm kiếm nhị phân, vì nó là một cây trống. Điều này có nghĩa là với mỗi nút (không có nút), các phần tử trong cây con bên trái nhỏ hơn hoặc bằng giá trị trong nút và các phần tử trong cây con bên phải đều lớn hơn hoặc bằng giá trị trong nút .


1

Chúng ta có thể tiến hành từ trái sang phải qua cây như thế này:

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

Lấy cảm hứng từ John McCarthygopher .

Danh sách đẩy xuống rõ ràng có thể được loại bỏ bằng cách tiếp tục truyền,

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)

Duy trì chỉ một yếu tố lớn nhất từ ​​trước đến nay là đủ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.