Обход дерева означает посещение каждого узла дерева. Например, вы можете добавить все значения в дерево или найти самое большое. Для всех этих операций вам необходимо будет посетить каждый узел дерева.
Линейные структуры данных, такие как массивы, стеки, очереди и связанный список, имеют только один способ чтения данных. Но иерархическая структура данных, такая как дерево, может проходить разными способами.
Давайте подумаем о том, как мы можем прочитать элементы дерева на изображении, показанном выше.
Начиная сверху, слева направо
- 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 ->