Алгоритм для перевірки того, чи є двійкове дерево деревом пошуку та підрахунок повних гілок


10

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

Поки що маю

void BST(tree T) {
   if (T == null) return
   if ( T.left and T.right) {
      if (T.left.data < T.data or T.right.data > T.data) {
        count = count + 1
        BST(T.left)
        BST(T.right)
      }
   }
}

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

Хтось може мені допомогти на цьому?


Як <визначається оператор порівняння на вузлах?
Джо,

Ви хочете обчислити кількість, навіть якщо це не двійкове дерево пошуку?
Джо,

1
Чи повинен ваш алгоритм повертати щось, наприклад, trueабо false?
Джо,

2
Можливо, вам слід спробувати спочатку визначити дві окремі функції: одну для перевірки, чи це BST, та одну для підрахунку повних гілок. Це має бути більш керованим.
sepp2k

1
@OghmaOsiris Я думаю, що він сказав це, оскільки питання в основному "Ось мій код, як я змушу його працювати?". Якби код не був різновидом псевдо (ish), це, безумовно, було б питанням SO.
sepp2k

Відповіді:


10

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

Подивимось, як підрахунок спочатку підрахує цілі гілки. Це означає підрахунок вузлів, у яких є і ліва дитина, і права дитина. Тоді вам потрібно збільшити лічильник ( count = count + 1), коли і те, T.leftі інше T.rightє недійсним (немає T.left.dataі T.right.data: дані не мають значення для цього завдання).

if (T.left and T.right) {
    count = count + 1

Крім того, вам потрібно дослідити ліве піддерево, навіть якщо праве піддерево порожнє, і вам потрібно дослідити праве піддерево, навіть якщо ліве піддерево порожнє. Тому дивіться, куди ви ставите рекурсивні дзвінки.

Щоб перевірити, чи є дерево пошуковим деревом, потрібно перевірити значення даних. Ви вже отримали щось близьке до правильного порівняння; не зовсім правильно. Напишіть кілька прикладних дерев з різними формами (не дуже великими, 2 - 5 вузлів) і запустіть на них свій алгоритм, щоб побачити, що відбувається.

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

Нарешті, як тільки вам вдалося записати обидві функції окремо, і ви протестували їх на кількох прикладах, складіть їх обережно (якщо цього вимагає завдання).


дякую, я перечитав питання, і це, мабуть, були окремими методами.
OghmaOsiris

7

У таких речах часто простіше думати назад, тому спочатку подумайте, що вам потрібно. З вашого опису перерахуємо їх:

  • Рекурсія
  • Дійсність
  • Кількість повних вузлів

Гаразд, це досить короткий список, це має бути керованим. Почнемо з порожнього методу, і я додам опис того, що має відбуватися.

valid_bst () {
}

Тепер термін дії. Як ви перевіряєте чинність? У чаті ви сказали, що дерево дійсне "якщо ... всі ліві діти менше, ніж батьки, а праві діти - більше батьків". Я впевнений, що ви мали намір також дозволити рівність. Це було б t.left.value <= t.value <= t.right.value.

valid_bst () {
    This node is valid if t.left.value <= t.value <= t.right.value
}

Але що робити, якщо одного з дітей немає? З того, що ви сказали, я вважаю, що ви знаєте, що вузол все ще дійсний, якщо один (або обидва) відсутні. Додамо це, трохи реструктуризуючи:

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
}

Гаразд, тепер ми знаємо, чи дійсний цей вузол. Як ми перевіряємо, чи дійсне все дерево? Це не в масиві, тому ми, мабуть, не можемо / не хочемо циклічно перебирати його. Ваше завдання дає відповідь: рекурсія. Але як ми накопичуємо відповідь за допомогою рекурсії? Ми маємо доступ до трьох відомостей про те, чи дійсний цей вузол, і результат дзвінків із запитанням, чи правильний лівий і правий вузли. Очевидно, дерево дійсне лише в тому випадку, якщо всі три з них справжні.

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
    Is the left child valid?
    Is the right child valid?
    This tree is only valid if this node and both its children are.
}

Якщо ви звертаєте увагу, це навіть говорить нам, яку функцію потрібно повернути.

Тепер, як ми інтегруємо підрахунок? Ви кажете, що рахується ("батьківський вузол з лівими та правими дочірніми вузлами"), і це не повинно бути важко перевести в фактичний код. Перевірте, чи виконується ця умова і належним чином збільшуйте лічильник. Просто пам’ятайте, що це повинно бути десь там, де це буде досягнуто кожен раз, коли це правда.

І звичайно, я залишив деякі деталі, такі як стан зупинки рекурсії та перевірка на нуль.


6

Мої три коментарі вище - це три натяки на проблеми з вашим кодом.

  1. Якщо ви вже конкретно не визначили, як оператор порівняння повинен обробляти тип даних вузла, швидше за все, порівняння двох вузлів безпосередньо не буде робити те, що ви хочете. Ви, напевно, мали на увазі порівняння полів, що зберігаються у вузлах, наприкладnode1.value < node2.value
  2. зараз ви лише додаєте до рахунку, якщо третя ifправда, ви впевнені, що це ви мали намір зробити? До речі, ви можете переконатися, що якщо оператор робить те, що ви хочете.
  3. Я припускаю, що ви хочете повернути true, якщо дерево є дійсним BST, а інакше помилковим. Це означає, що ви повинні завжди повертати справжнє або помилкове в базовому випадку, і ви також повинні повертати результати своїх рекурсивних дзвінків.

Щодо першого пункту: Це псевдо-код, правда? Поки наміри передаються читачеві, немає жодних причин визначати такі речі.
sepp2k

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

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