Temukan apakah sebuah pohon adalah pohon pencarian biner di 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)

Yang ingin saya lakukan adalah menulis fungsi untuk menentukan apakah pohon yang diberikan adalah pohon pencarian biner, metode saya adalah mengelompokkan semua nilai dalam daftar dan mengimpor Data.Listlalu mengurutkan daftar untuk menemukan apakah mereka sama, tetapi sedikit rumit. Bisakah kita melakukan ini tanpa mengimpor modul lain?

Jayyyyyy
sumber
Saya tidak akan mendefinisikan flattenTreedulu. Anda dapat kembali Falselebih awal jika sebuah simpul melanggar properti pencarian tanpa harus melintasi seluruh subtree yang di-root pada simpul itu.
chepner
@ lebih baik masalahnya dengan sort, bukan dengan flattenTree, yang cukup malas.
Will Ness
Ya, itu terpikir oleh saya setelah melihat beberapa jawaban lainnya.
chepner

Jawaban:

13

Inilah cara untuk melakukannya tanpa meratakan pohon.

Dari definisi tersebut, di sini,

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

orang dapat melihat bahwa melintasi pohon dari kiri ke kanan, mengabaikan Nodedan tanda kurung, memberi Anda urutan Nulls dan as yang bergantian . Yaitu, di antara setiap dua nilai, ada a Null.

Rencana saya adalah untuk memeriksa bahwa setiap subtree memenuhi persyaratan yang sesuai : kita dapat memperbaiki persyaratan di masing-masing Node, mengingat nilai-nilai mana kita berada, kemudian mengujinya di masing-masing Null. Karena ada di Nullantara setiap pasangan nilai yang berurutan, kami akan menguji bahwa semua pasangan berurutan (kiri-ke-kanan) tidak menurun.

Apa itu persyaratan? Ini adalah batas bawah dan atas longgar pada nilai-nilai di pohon. Untuk menyatakan persyaratan, termasuk yang di ujung paling kiri dan paling kanan, kami dapat memperpanjang pemesanan dengan Bottom dan Topelemen, sebagai berikut:

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

Sekarang mari kita periksa apakah pohon yang diberikan memenuhi persyaratan untuk urutan dan antara batas yang diberikan.

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

Pohon pencarian biner adalah pohon yang berurutan dan antara Botdan Top.

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

Menghitung nilai - nilai ekstrem aktual dalam setiap subtree, menggelembungkannya keluar, memberi Anda lebih banyak informasi daripada yang Anda butuhkan, dan secara fiddly dalam kasus tepi di mana subtree kiri atau kanan kosong. Mempertahankan dan memeriksa persyaratan , mendorongnya ke dalam, agak lebih seragam.

pekerja pigmen
sumber
6

Inilah sebuah petunjuk: buat fungsi bantu

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

dimana BSTResult adidefinisikan sebagai

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

Anda harus dapat melanjutkan secara rekursif, mengeksploitasi hasil pada subtree untuk mendorong perhitungan, khususnya minimum dan maksimum.

Misalnya, jika Anda memiliki tree = Node left 20 right, dengan isBSTree' left = NonEmptyBST 1 14dan isBSTree' right = NonEmptyBST 21 45, maka isBSTree' treeseharusnya NonEmptyBST 1 45.

Dalam kasus yang sama kecuali untuk tree = Node left 24 right, kita seharusnya memiliki isBSTree' tree = NotBST.

Mengkonversi hasilnya menjadi Boolsepele.

chi
sumber
1
atau tentukan Monoid yang jelas untuk BSTResult adan lipat ke dalamnya. :) (atau bahkan jika itu bukan Monoid halal ....)
Will Ness
(Tapi menurut saya itu sah menurut hukum)
Will Ness
3

Ya , Anda tidak perlu menyortir daftar. Anda dapat memeriksa apakah setiap elemen kurang dari atau sama dengan elemen berikutnya. Ini lebih efisien karena kita dapat melakukan ini di O (n) , sedangkan mengevaluasi daftar yang diurutkan sepenuhnya membutuhkan O (n log n) .

Dengan demikian kami dapat memeriksa ini dengan:

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

Jadi kita dapat memeriksa apakah pohon biner adalah pohon pencarian biner dengan:

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

Saya pikir orang dapat mengklaim bahwa Nullitu sendiri adalah pohon pencarian biner, karena itu adalah pohon kosong. Ini berarti bahwa untuk setiap node (tidak ada node) elemen-elemen dalam subtree kiri kurang dari atau sama dengan nilai dalam node, dan elemen-elemen dalam subtree kanan semuanya lebih besar dari atau sama dengan nilai pada node .

Willem Van Onsem
sumber
1

Kita dapat melanjutkan dari kiri ke kanan di atas pohon seperti ini:

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

Terinspirasi oleh John McCarthy'sgopher .

Daftar push-down eksplisit dapat dihilangkan dengan kelanjutan-lewat,

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)

Mempertahankan hanya satu, elemen terbesar sejauh ini , sudah cukup.

Will Ness
sumber