[Структура данных и алгоритм] Простой для понимания обход двоичного дерева

В простом для понимания объяснении бинарных деревьев поиска (пожалуйста, ткните меня) статья в основном знакомит с природой бинарных деревьев поиска.Эта статья продолжит знакомство с обходом бинарных деревьев.

Обход двоичного дерева поиска

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

Обход предзаказа

Операция предварительного просмотра двоичного дерева выглядит следующим образом:

  • Посетите корневой узел
  • Предварительный заказ прохождения левого поддерева
  • Предварительный заказ пройти по правому поддереву

Например, на следующем рисунке порядок доступа к узлу предварительного обхода двоичного дерева равен 3 1 2 5 4 6: В
[Структура данных и алгоритм] Простой для понимания обход двоичного дерева
соответствии с этапами работы предварительного обхода, проанализированными выше, нетрудно увидеть, что можно легко реализовать доступ предварительного порядка двоичного дерева рекурсивным методом:

//前序遍历递归版本
void preOrder(struct node *root)
{
    if(root != NULL)
    {
        cout << root->data << " ";
        preOrder(root->left);
        preOrder(root->right);
    }
}

Фактически, предварительный обход двоичного дерева также может быть реализован нерекурсивным образом: поскольку корневой узел должен быть извлечен после обхода корневого узла, чтобы пройти правое поддерево, соответствующее корневому узлу, вы можете рассмотреть возможность использования стека для хранения посещенных Корневой узел реализации кода выглядит следующим образом:

//前序遍历的非递归版本
void preOrder(struct node *root)  
{  
    stack<struct node *> s;  
    while (root != NULL || !s.empty()) 
    {  
        if (root != NULL) 
        {  
        //访问结点并入栈
            cout << root->data << " ";   
            s.push(root);                
            root = root->left; //访问左子树  
       } 
       else 
       {  
            root = s.top();  //回溯至父亲结点  
            s.pop();  
            root = root->right; //访问右子树  
       }  
    }  
    cout << endl;  
}  

Обход по порядку

Операция обхода среднего порядка двоичного дерева выглядит следующим образом:

  • Обход левого поддерева среднего порядка
  • Посетите корневой узел
  • Обход правого поддерева по порядку

Например, на следующем рисунке порядок доступа узлов в обходе двоичного дерева - 1 2 3 4 5 6:
[Структура данных и алгоритм] Простой для понимания обход двоичного дерева

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

//中序遍历递归版本
void inOrder(struct node *root)
{
    if(root != NULL)
    {
        inOrder(root->left);
    //和前序遍历相比,只是输出语句换了个位置唯一
    cout << root->data << " ";
        inOrder(root->right);
    }
}
//中序遍历的非递归版本
void inOrder(struct node *root)  
{  
    stack<struct node *> s;  
    while (root != NULL || !s.empty()) 
    {  
        if (root != NULL) 
        {  
            s.push(root);  
            root = root->left;  
        }  
        else 
        {  
        //访问完左子树后才访问根结点  
            root = s.top();  
            cout << root->data << " ";  
            s.pop();  
            root = root->right; //访问右子树  
        }  
    }  
    cout << endl;  
}  

Последующий обход

Операция обхода двоичного дерева после заказа выглядит следующим образом:

  • Пост-заказный обход левого поддерева
  • Пост-заказный обход правого поддерева
  • Посетите корневой узел

Например, на следующем рисунке порядок доступа к узлам обхода пост-порядка двоичного дерева равен 2 1 4 6 5 3:
[Структура данных и алгоритм] Простой для понимания обход двоичного дерева
Рекурсивная версия обхода пост-порядка двоичного дерева аналогична среднему порядку предварительного порядка, а именно:

//后序遍历递归版本
void postOrder(struct node *root)
{
    if(root != NULL)
    {
        postOrder(root->left);
        postOrder(root->right);

        //最后访问根节点
        cout << root->data << " ";
    }
}

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

(1) Переместите корневой узел в первый стек s.
(2) Извлечь узел из стека и отправить его в выход стека.
(3) Затем переместите левый дочерний и правый дочерние элементы узла в первый стек s.
(4) Повторяйте процедуры 2 и 3, пока стопка s не станет пустой.
(5) Теперь все узлы помещены в выходной стек и сохранены в порядке обхода пост-порядка, а непосредственное извлечение является результатом обхода пост-порядка бинарного дерева.

Код реализован следующим образом:

//后序遍历的非递归版本
void postOrder(struct node *root)  
{  
    if (!root) return;  
    stack<struct node*> s, output;  
    s.push(root);  
    while (!s.empty()) 
    {  
        struct node *curr = s.top();  
        output.push(curr);  
        s.pop();  
        if (curr->left)  
            s.push(curr->left);  
        if (curr->right)  
            s.push(curr->right);  
    }  

    while (!output.empty()) 
    {  
        cout << output.top()->data << " ";  
        output.pop();  
    }  
    cout << endl;  
}  

Рекомендуемая литература:

[Благополучие] Обмен видео с онлайн-курсами, собранными мной (
Часть 1 ) [Проектирование системы] Кэш LRU
[Структура данных и алгоритм] Простой для понимания связанный список
[Структура данных и алгоритм] Простая для понимания сортировка битов
[C ++ Notes] C ++ 11 Параллельное программирование (1) Начать путешествие по потоку
[Примечания C ++] Распространенные ошибки при использовании указателей C / C ++
[Примечания C ++] Подробное описание статических и динамических библиотек (включено)

Сосредоточьтесь на обмене сводкой знаний о стеке фоновых технологий сервера

Добро пожаловать, чтобы обратить внимание на общение и общий прогресс

[Структура данных и алгоритм] Простой для понимания обход двоичного дерева

Кодирование

Разработчик кода может предоставить вам простые для понимания технические статьи, которые упростят технологию!

рекомендация

отblog.51cto.com/15006953/2552022