Коли використовувати стратегії попереднього замовлення, післяпорядкування та впорядкування бінарного дерева пошуку


97

Нещодавно я зрозумів, що, використовуючи достатньо BST у своєму житті, я навіть ніколи не думав використовувати щось, окрім обходу Inorder (хоча я усвідомлюю і знаю, наскільки легко адаптувати програму для використання обходу до / після замовлення).

Зрозумівши це, я витягнув деякі зі своїх старих підручників по структурі даних і шукав міркування щодо корисності обходів перед замовленнями та після замовлення - проте вони сказали не так багато.

Наведіть приклади того, коли практично використовувати попереднє замовлення / попереднє замовлення? Коли це має більше сенсу, ніж не в порядку?

Відповіді:


135

Коли використовувати стратегію попереднього замовлення, замовлення та замовлення після замовлення

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

Корінь дерева - 7 , самий лівий вузол - 0 , самий правий вузол - 10 .

введіть тут опис зображення

Обхід попереднього замовлення :

Короткий зміст: Починається в корені ( 7 ), закінчується в самому правому вузлі ( 10 )

Послідовність обходу: 7, 1, 0, 3, 2, 5, 4, 6, 9, 8, 10

Обхід в порядку :

Короткий зміст: Починається в самому лівому вузлі ( 0 ), закінчується в самому правому вузлі ( 10 )

Послідовність обходу: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Обхід після замовлення :

Короткий зміст: Починається з самого лівого вузла ( 0 ), закінчується коренем ( 7 )

Послідовність обходу: 0, 2, 4, 6, 5, 3, 1, 8, 10, 9, 7

Коли використовувати попереднє замовлення, замовлення чи замовлення?

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

  1. Якщо ви знаєте, що вам потрібно дослідити коріння перед тим, як перевіряти будь-яке листя, вибирайте попереднє замовлення, оскільки ви зустрінете всі коріння перед усіма листям.

  2. Якщо ви знаєте, що вам потрібно вивчити всі листки перед будь-якими вузлами, ви вибираєте пост-замовлення, оскільки не витрачаєте часу, перевіряючи коріння на пошук листя.

  3. Якщо ви знаєте, що дерево має невід'ємну послідовність у вузлах, і ви хочете згладити дерево назад у початкову послідовність, слід використовувати обхід в порядку . Дерево було б сплющено так само, як воно було створено. Обхід попереднього замовлення або після замовлення може не розмотати дерево назад у послідовності, яка була використана для його створення.

Рекурсивні алгоритми для попереднього замовлення, замовлення та після замовлення (C ++):

struct Node{
    int data;
    Node *left, *right;
};
void preOrderPrint(Node *root)
{
  print(root->name);                                  //record root
  if (root->left != NULL) preOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) preOrderPrint(root->right);//traverse right if exists
}

void inOrderPrint(Node *root)
{
  if (root.left != NULL) inOrderPrint(root->left);   //traverse left if exists
  print(root->name);                                 //record root
  if (root.right != NULL) inOrderPrint(root->right); //traverse right if exists
}

void postOrderPrint(Node *root)
{
  if (root->left != NULL) postOrderPrint(root->left);  //traverse left if exists
  if (root->right != NULL) postOrderPrint(root->right);//traverse right if exists
  print(root->name);                                   //record root
}

3
А як щодо нерекурсивних обходів? Мені здається, що набагато простіше пройти дерево без рекурсивного попереднього замовлення в порівнянні з попереднім замовленням, оскільки воно не вимагає повернення до попередніх вузлів.
bluenote10,

@ bluenote10 Чи можете ви пояснити, що ви маєте на увазі? У попередньому замовленні ви все одно "повертаєтесь" до вузла для обробки його правої дочірньої організації після обробки її лівої дочірньої організації. Звичайно, ви могли б використовувати чергу "вузлів, які ще не відвідані", але це насправді просто торгівля неявним (стековим) сховищем для явної черги. У всіх методах обходу потрібно обробляти як лівих, так і правих дітей, а це означає, що після виконання одного з них ви повинні "повернутися" до батьків.
Джошуа Тейлор

@JoshuaTaylor: Так, усі вони мають однаковий клас складності, але якщо ви подивитесь на типові реалізації, пост-замовлення , мабуть, трохи складніше.
bluenote10

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

Я думаю, що це правда, навіть якщо дерево не впорядковане правильно, я маю на увазі, щоб не давати відсортовану послідовність, якщо послідовність спочатку не була відсортована.
CodeYogi

29

Попереднє замовлення: використовується для створення копії дерева. Наприклад, якщо ви хочете створити копію дерева, помістіть вузли в масив із попереднім замовленням. Потім виконайте операцію вставки на новому дереві для кожного значення в масиві. Ви отримаєте копію оригінального дерева.

У порядку:: Використовується для отримання значень вузлів у порядку зменшення в BST.

Після замовлення:: Використовується для видалення дерева від листа до кореня


2
це чудова стисла відповідь і допомогла мені зрозуміти випадки використання попереднього замовлення та після замовлення. хоча, це може бути очевидним, враховуючи, що питання зазначає це безпосередньо, але зауважте, що це стосується бінарних дерев ПОШУКУ і не обов'язково працює для загальних бінарних дерев. наприклад, ви не можете необов’язково використовувати обхід попереднього замовлення для копіювання загального бінарного дерева, оскільки логіка вставки під час процесу копіювання не буде працювати.
markckim

7
У порядку:: Отримати значення вузла в порядку "не зменшення" - не "збільшення"
rahil008

26

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

- ROOT
    - A
         - B
         - C
    - D
         - E
         - F
             - G

4
Або в TreeViewкомпоненті в програмі графічного інтерфейсу.
svick

4

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

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


1
Я думаю, ви намагаєтесь сказати щось важливе, чи можете ви пояснити першу половину?
CodeYogi

@CodeYogi Що конкретно вам потрібно пояснити?
Рафаель

1
"Попереднє та післязамовлення стосуються рекурсивних алгоритмів зверху вниз і знизу вгору" Я думаю, ви хочете сказати, що в першому випадку вузол обробляється перед викликом будь-якого з рекурсивних методів і навпаки, в останньому, праворуч ?
CodeYogi

@CodeYogi Так, в основному.
Рафаель

2

У багатьох місцях ви бачите, що ця різниця відіграє справжню роль.

Одне чудове, на яке я зверну увагу, - це створення коду для компілятора. Розглянемо твердження:

x := y + 32

Спосіб створення коду для цього полягає (звичайно, наївно), щоб спочатку згенерувати код для завантаження y в регістр, завантажити 32 в регістр, а потім створити інструкцію для додавання двох. Оскільки щось має бути в реєстрі перед тим, як ви ним маніпулюєте (припустимо, ви завжди можете робити постійні операнди, але що завгодно), ви повинні робити це таким чином.

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

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