Обход дерева означает посещение каждого узла дерева. Например, вы можете добавить все значения в дерево или найти самое большое. Для всех этих операций вам необходимо будет посетить каждый узел дерева.
Линейные структуры данных, такие как массивы, стеки, очереди и связанный список, имеют только один способ чтения данных. Но иерархическая структура данных, такая как дерево, может проходить разными способами.
Давайте подумаем о том, как мы можем прочитать элементы дерева на изображении, показанном выше.
Начиная сверху, слева направо
1 -> 12 -> 9 -> 5 -> 6
Начиная снизу, слева направо
5 -> 6 -> 12 -> 9 -> 1
Хотя этот процесс в некотором роде прост, он не учитывает иерархию дерева, а только глубину узлов.
Вместо этого мы используем методы обхода, которые учитывают базовую структуру дерева, то есть
struct node { int data; struct node* left; struct node* right; }
Узел структуры, на который указывают left (левый) и right (правый) , может иметь другие левые и правые дочерние элементы, поэтому мы должны рассматривать их как поддеревья, а не подузлы.
Согласно этой структуре каждое дерево представляет собой комбинацию
- Узел, несущий данные
- Два поддерева
Помните, что нашей задачей является посещение каждого узла, поэтому нам нужно посетить все узлы в поддереве, посетить корневой узел и также посетить все узлы в правом поддереве.
В зависимости от порядка, в котором мы это делаем, может быть три типа обхода .
Центрированный тип обхода (Inorder traversal)
- Сначала посетите все узлы в левом поддереве
- Затем корневой узел
- Посетите все узлы в правом поддереве
inorder(root->left) display(root->data) inorder(root->right)
Прямой тип обхода (Preorder traversal)
- Посетите корневой узел
- Посетите все узлы в левом поддереве
- Посетите все узлы в правом поддереве
display(root->data) preorder(root->left) preorder(root->right)
Обратный тип обхода (Postorder traversal)
- посетить все узлы в левом поддереве
- посетить корневой узел
- посетить все узлы в правом поддереве
postorder(root->left) postorder(root->right) display(root->data)
Давайте визуализируем центрированный тип обхода (inorder traversal). Начнем с корневого узла.
Сначала мы проходим левое поддерево. Мы также должны помнить, что нужно посетить корневой узел и правое поддерево, когда дерево будет готово.
Давайте поместим все это в стек, чтобы мы помнили.
Теперь перейдем к поддереву, указанному на вершине стека.
Опять же, мы следуем тому же правилу центрированного типа (inorder)
Left subtree -> root -> right subtree
Пройдя левое поддерево, мы остаемся с
Поскольку у узла "5" нет поддеревьев, мы печатаем его напрямую. После этого мы печатаем его родительский узел «12», а затем правый дочерний «6».
Поместить все в стек было полезно, потому как теперь, когда левое поддерево корневого узла было пройдено, мы можем распечатать его и перейти к правому поддереву.
После прохождения всех элементов, центрированный тип обхода (inorder traversal) выглядит так:
5 -> 12 -> 6 -> 1 -> 9
Нам не нужно создавать стек самостоятельно, потому что рекурсия поддерживает для нас правильный порядок.
Полный код для центрированного (inorder), прямого (preorder) и обратного (postorder) типа обхода на языке программирования C размещен ниже:
#include <stdio.h> #include <stdlib.h> struct node { int data; struct node* left; struct node* right; }; void inorder(struct node* root){ if(root == NULL) return; inorder(root->left); printf("%d ->", root->data); inorder(root->right); } void preorder(struct node* root){ if(root == NULL) return; printf("%d ->", root->data); preorder(root->left); preorder(root->right); } void postorder(struct node* root) { if(root == NULL) return; postorder(root->left); postorder(root->right); printf("%d ->", root->data); } struct node* createNode(value){ struct node* newNode = malloc(sizeof(struct node)); newNode->data = value; newNode->left = NULL; newNode->right = NULL; return newNode; } struct node* insertLeft(struct node *root, int value) { root->left = createNode(value); return root->left; } struct node* insertRight(struct node *root, int value){ root->right = createNode(value); return root->right; } int main(){ struct node* root = createNode(1); insertLeft(root, 12); insertRight(root, 9); insertLeft(root->left, 5); insertRight(root->left, 6); printf("Inorder traversal \n"); inorder(root); printf("\nPreorder traversal \n"); preorder(root); printf("\nPostorder traversal \n"); postorder(root); }
Вывод кода будет выглядеть так:
Inorder traversal 5 ->12 ->6 ->1 ->9 -> Preorder traversal 1 ->12 ->5 ->6 ->9 -> Postorder traversal 5 ->6 ->12 ->9 ->1 ->