mafulechka
3 июня 2019 г. 14:31

Обход дерева – центрированный (inorder), прямой (preorder) и обратный (postorder) (три основных способа обхода)

Обход дерева означает посещение каждого узла дерева. Например, вы можете добавить все значения в дерево или найти самое большое. Для всех этих операций вам необходимо будет посетить каждый узел дерева.


Линейные структуры данных, такие как массивы, стеки, очереди и связанный список, имеют только один способ чтения данных. Но иерархическая структура данных, такая как дерево, может проходить разными способами.

Давайте подумаем о том, как мы можем прочитать элементы дерева на изображении, показанном выше.

Начиная сверху, слева направо

  1. 1 -> 12 -> 9 -> 5 -> 6

Начиная снизу, слева направо

  1. 5 -> 6 -> 12 -> 9 -> 1

Хотя этот процесс в некотором роде прост, он не учитывает иерархию дерева, а только глубину узлов.

Вместо этого мы используем методы обхода, которые учитывают базовую структуру дерева, то есть

  1. struct node {
  2. int data;
  3. struct node* left;
  4. struct node* right;
  5. }

Узел структуры, на который указывают left (левый) и right (правый) , может иметь другие левые и правые дочерние элементы, поэтому мы должны рассматривать их как поддеревья, а не подузлы.

Согласно этой структуре каждое дерево представляет собой комбинацию

  • Узел, несущий данные
  • Два поддерева

Помните, что нашей задачей является посещение каждого узла, поэтому нам нужно посетить все узлы в поддереве, посетить корневой узел и также посетить все узлы в правом поддереве.

В зависимости от порядка, в котором мы это делаем, может быть три типа обхода .

Центрированный тип обхода (Inorder traversal)

  1. Сначала посетите все узлы в левом поддереве
  2. Затем корневой узел
  3. Посетите все узлы в правом поддереве
  1. inorder(root->left)
  2. display(root->data)
  3. inorder(root->right)

Прямой тип обхода (Preorder traversal)

  1. Посетите корневой узел
  2. Посетите все узлы в левом поддереве
  3. Посетите все узлы в правом поддереве
  1. display(root->data)
  2. preorder(root->left)
  3. preorder(root->right)

Обратный тип обхода (Postorder traversal)

  1. посетить все узлы в левом поддереве
  2. посетить корневой узел
  3. посетить все узлы в правом поддереве
  1. postorder(root->left)
  2. postorder(root->right)
  3. display(root->data)

Давайте визуализируем центрированный тип обхода (inorder traversal). Начнем с корневого узла.

Сначала мы проходим левое поддерево. Мы также должны помнить, что нужно посетить корневой узел и правое поддерево, когда дерево будет готово.

Давайте поместим все это в стек, чтобы мы помнили.

Теперь перейдем к поддереву, указанному на вершине стека.

Опять же, мы следуем тому же правилу центрированного типа (inorder)

  1. Left subtree -> root -> right subtree

Пройдя левое поддерево, мы остаемся с

Поскольку у узла "5" нет поддеревьев, мы печатаем его напрямую. После этого мы печатаем его родительский узел «12», а затем правый дочерний «6».

Поместить все в стек было полезно, потому как теперь, когда левое поддерево корневого узла было пройдено, мы можем распечатать его и перейти к правому поддереву.

После прохождения всех элементов, центрированный тип обхода (inorder traversal) выглядит так:

  1. 5 -> 12 -> 6 -> 1 -> 9

Нам не нужно создавать стек самостоятельно, потому что рекурсия поддерживает для нас правильный порядок.

Полный код для центрированного (inorder), прямого (preorder) и обратного (postorder) типа обхода на языке программирования C размещен ниже:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. struct node {
  5. int data;
  6. struct node* left;
  7. struct node* right;
  8. };
  9.  
  10. void inorder(struct node* root){
  11. if(root == NULL) return;
  12. inorder(root->left);
  13. printf("%d ->", root->data);
  14. inorder(root->right);
  15. }
  16.  
  17. void preorder(struct node* root){
  18. if(root == NULL) return;
  19. printf("%d ->", root->data);
  20. preorder(root->left);
  21. preorder(root->right);
  22. }
  23.  
  24. void postorder(struct node* root) {
  25. if(root == NULL) return;
  26. postorder(root->left);
  27. postorder(root->right);
  28. printf("%d ->", root->data);
  29. }
  30.  
  31.  
  32. struct node* createNode(value){
  33. struct node* newNode = malloc(sizeof(struct node));
  34. newNode->data = value;
  35. newNode->left = NULL;
  36. newNode->right = NULL;
  37.  
  38. return newNode;
  39. }
  40.  
  41. struct node* insertLeft(struct node *root, int value) {
  42. root->left = createNode(value);
  43. return root->left;
  44. }
  45.  
  46.  
  47. struct node* insertRight(struct node *root, int value){
  48. root->right = createNode(value);
  49. return root->right;
  50. }
  51.  
  52.  
  53. int main(){
  54. struct node* root = createNode(1);
  55. insertLeft(root, 12);
  56. insertRight(root, 9);
  57.  
  58. insertLeft(root->left, 5);
  59. insertRight(root->left, 6);
  60.  
  61. printf("Inorder traversal \n");
  62. inorder(root);
  63.  
  64. printf("\nPreorder traversal \n");
  65. preorder(root);
  66.  
  67. printf("\nPostorder traversal \n");
  68. postorder(root);
  69. }

Вывод кода будет выглядеть так:

  1. Inorder traversal
  2. 5 ->12 ->6 ->1 ->9 ->
  3. Preorder traversal
  4. 1 ->12 ->5 ->6 ->9 ->
  5. Postorder traversal
  6. 5 ->6 ->12 ->9 ->1 ->

Вам это нравится? Поделитесь в социальных сетях!

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь