Дізнайтеся, чи є дерево бінарним деревом пошуку в Haskell


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)

Що я хочу зробити, це написати функцію, щоб визначити, чи вказане дерево є бінарним деревом пошуку. Мій метод полягає в тому, щоб згрупувати всі значення в списку та імпортувати, Data.Listа потім сортувати список, щоб знайти, чи вони рівні, але це трохи складніше. Чи можемо ми це зробити, не імпортуючи інший модуль?


Я б не визначився flattenTreeспочатку. Ви можете повернутися Falseдостроково, якщо вузол порушує пошукову властивість, не маючи необхідності переходити все піддерево, вкорінене на цьому вузлі.
чепнер

@chepner проблема з проблемою sort, а не з цим flattenTree, що досить ледачий.
Буде Несс

Так, це мені прийшло в голову після перегляду деяких інших відповідей.
чепнер

Відповіді:


13

Ось спосіб це зробити, не згладжуючи дерево.

З визначення, тут,

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

видно, що проходження дерева ліворуч праворуч, ігнорування Nodeта круглі дужки дає чергування послідовностей Nulls і as. Тобто між кожними двома значеннями є а Null.

Мій план полягає в тому, щоб перевірити, чи відповідає кожному піддереву відповідні вимоги : ми можемо уточнити вимоги у кожному Node, запам'ятавши, між якими значеннями ми знаходимось, а потім перевірити їх у кожному Null. Оскільки існує Nullпара значень пари в порядку, ми перевірили, що всі порядкові (зліва направо) пари не зменшуються.

Що таке вимога? Це вільна нижня і верхня межа значень на дереві. Щоб висловити вимоги, включаючи ті, що знаходяться в лівому і правому кінці, ми можемо розширити будь-яке замовлення з Botтома та 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

Обчислення фактичних крайніх значень у кожному піддереві, виведення їх назовні, дає вам більше інформації, ніж вам потрібно, і непомітно у крайніх випадках, коли ліве або праве піддерево порожнє. Дотримання та перевірка вимог , штовхаючи їх всередину, є більш рівномірним.


6

Ось підказка: зробіть допоміжну функцію

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то тривіальне.


1
або визначити очевидний Monoid для BSTResult aі скласти в нього. :) (або навіть якщо це не законний Monoid ....)
Буде Несс

(але це все-таки законно, я думаю)
Буде

3

Так , вам не потрібно сортувати список. Ви можете перевірити, чи кожен елемент менший або рівний наступному. Це більш ефективно, оскільки ми можемо це зробити в O (n) , тоді як для оцінки відсортованого списку повністю потрібно O (n log n) .

Таким чином, ми можемо перевірити це за допомогою:

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

Тож ми можемо перевірити, чи є двійкове дерево двійковим деревом пошуку з:

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

Я думаю, можна стверджувати, що Nullсаме по собі є двійковим деревом пошуку, оскільки це порожнє дерево. Таким чином, це означає, що для кожного вузла (немає вузлів) елементи в лівому піддереві менше або дорівнюють значенню у вузлі, а елементи в правому піддереві всі більші або рівні значенню у вузлі .


1

Ми можемо переходити зліва направо над деревом так:

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

Натхненний Джоном Маккарті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)

Достатньо зберегти лише один, найбільший досі елемент.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.