Data structure: Binary tree traversal

Overview

Binary tree traversal refers to visiting each node in the binary tree according to a certain search path, so that each node is visited once and only once. The main traversal methods of binary trees include: pre-order traversal, in-order traversal, post-order traversal, and hierarchical traversal. First order, middle order, and last order are actually the order in which the parent nodes are visited. If during the traversal process, the parent node is visited before its child nodes, it is pre-order traversal; if the order in which the parent node is accessed is between the order in which its child nodes are accessed, it is in-order traversal; if the left and right traversals are visited The child then visits the parent node, which is post-order traversal. Regardless of whether it is pre-order traversal, in-order traversal or post-order traversal, the relative access order of the left and right child nodes will not change. The left child node is always accessed first, and then the right child node is accessed. Level traversal is to visit each node of the binary tree in order from top to bottom and from left to right.

Data structure: Binary tree traversal 1

Before introducing the traversal algorithm, first define a binary tree structure. The code is as follows:

struct node{
    
    
    int elem;//数据域
    node *left;//左孩子节点指针
    node *right;//有孩子指针节点
};

preorder traversal

recursion

Using recursion, it is easy to write a traversal algorithm. code show as below:

void pre_order_r(const node* root, int(*visit)(const node*)){
    
    
    //先序遍历,递归,root根结点,visit访问数据元素的函数指针
    if(root == nullptr) return;

    visit(root);//可以把这个理解成cout << root->elem
    pre_order_r(root->left, visit);
    pre_order_r(root->right, visit);
}

Iterate

The first iterative version

code show as below:

void pre_order1(const node* root, int(*visit)(const node*)){
    
    
    //先序遍历,非递归
    const node *p;
    stack<const node*>st;//辅助栈
    p = root;
    if(p != nullptr) st.push(p);//若根结点不为空,则令根结点进栈
    while(!st.empty()){
    
    
        p = st.top();
        st.pop();
        visit(p);
        if(p->right != nullptr) st.push(p->right);
        if(p->left != nullptr) st.push(p->left);
    }
}

Let's use an example to understand how this iteration works.

Data structure: Binary tree traversal 2

Note: The elements above the grid indicate that they have been popped up and visited.

The traversal process of this binary tree is as follows:

  1. Initialize an empty stack and pointer p pointing to the root node.
  2. The root node is not empty and is pushed onto the stack. At this time, a is pushed onto the stack.
  3. Determine whether the stack is empty or not. The loop starts, pops up and accesses the top element of the stack. At this time, the top element of the stack is a.
  4. If a has a right child, push its right child node onto the stack; if there is a left child node, push the left child node onto the stack. At this time, there are two elements b and c in the stack.
  5. Repeat step three until the stack is empty. The traversal ends.

The second iteration method

Let’s look at a larger, more general binary tree:

Data Structure: Binary Tree Traversal 3

The pre-order traversal sequence of this binary tree is: abdheicfjkgl, which means it follows the order shown in the figure below:

Data Structure: Binary Tree Traversal 4

Further, we abstract the binary tree into the following figure:,

Data structure: Binary tree traversal 5

a to e are the nodes on the left chain of the binary tree, and i to f are the children of a to d respectively. It is not difficult to find that pre-order traversal of a binary tree is to first visit the node on the left subtree from top to bottom, and then visit the right subtree of the node on the left subtree from bottom to top. Our second iteration was designed based on this idea. code show as below:

void pre_order2(const node* root, int(*visit)(const node*)){
    
    
    const node *p;
    stack<const node*> st;
    p = root;
    if(p != nullptr) st.push(p);
    while( ! st.empty() ){
    
    
        p = st.top();
        st.pop();
        while(p != nullptr){
    
    //从当前节点出发,逐批访问
            visit(p);//访问当前节点
            if(p->right != nullptr) st.push(p->right);//不为空的右孩子入栈
            p = p->left;//沿左子树深入一层
        }
    }
}

inorder traversal

recursion

Similar to pre-order traversal, no further explanation is required. code show as below:

void in_order_r(const node* root, int(*visit)(const node*)){
    
    
    //中序遍历,递归
    if(root == nullptr) return;

    in_order_r(root->left, visit);
    visit(root);
    in_order_r(root->right, visit);
}

Iterate

Compared with the second iteration version of pre-order traversal, at a macro level, the order of in-order traversal is abstracted as: first visit the bottom node of the left subtree of the binary tree, then visit the right subtree node of the node, and then visit the The parent node of the node, and then access the right subtree of the parent node of the node until all nodes have been visited. As shown below:

Data Structure: Binary Tree Traversal 6

According to this idea, the in-order traversal algorithm can be implemented as follows:

void in_order(const node* root, int(*visit)(const node*)){
    
    
    //中序遍历,非递归
    const node *p;
    stack<const node *> st;
    p= root;
    while(!st.empty() ) {
    
    
        if (p != nullptr){
    
    
            st.push(p);
            p = p->left;
        }
        else {
    
    
            p = st.top();
            st.pop();
            visit(p);
            p = p->right;
        }
    }
}

Postorder traversal

recursion

Just like the previous two, go directly to the code:

void post_order_r(const node* root, int(*visit)(const node*)){
    
    
    //后序遍历,递归
    if(root == nullptr) return;

    post_order_r(root->left,  visit);
    post_order_r(root->right, visit);
    visit(root);
}

Iterate

It is difficult to use the iterative method to implement post-order traversal, because the recursive traversal of the left and right subtrees is strictly not tail recursive. But we can apply the previous ideas and methods and think about it, which node is visited first in post-order traversal? The answer is the highest and leftmost leaf node of the binary tree. This highest and leftmost leaf node may be a left child node or a right child node.

Data Structure: Binary Tree Traversal 7

The entire post-order traversal can be decomposed into several fragments. Each fragment starts from a node on the path and includes three steps: visiting the current node, traversing the subtree rooted at its right brother, and tracing back up to its parent node and go to the next fragment. Based on this understanding, our code is as follows:

void post_order(const node* root, int(*visit)(const node*)){
    
    
    //后序遍历,非递归
    const node *p, *q;//p正在访问的节点,q刚刚访问过的节点
    stack<const node *> st;
    p = root;
    do {
    
    
        while(p != nullptr){
    
    //往左走下去
            st.push(p);
            p = p->left;
        }
        q = nullptr;
        while( !st.empty() ){
    
    
            p = st.top();
            st.pop();
            if(p -> right == q) {
    
    //右孩子不存在或者已被访问过
                visit(p);
                q = p;//保存刚访问的节点
            }
            else{
    
    
                st.push(p);//当前节点不能访问
                p = p->right;//先处理右子树
                break;
            }
        }
    }while(! st.empty() );
}

We perform postorder traversal on the first instance of preorder traversal to see how this iteration works.

Data structure: Binary tree traversal 8

The traversal process of this binary tree is as follows:

  1. Initialize an empty stack and pointer p (representing the node being visited) q (the node just visited)
  2. Go left from the current node until you reach the end.
  3. Determine whether the current node can be accessed (that is, determine whether the current node has a right subtree or has been visited). If the current node does not have a right subtree or has been visited, access the current node; otherwise, point p to the right subtree of the current node. tree and return to step 2.
  4. until the stack is empty. The traversal ends.

Level traversal

In the introduction to hierarchical traversal at the beginning, we said that hierarchical traversal visits each node of the binary tree strictly in top-down, left-to-right order. So here we need the queue as an auxiliary, the code is as follows:

void level_order(const node *root, int(*visit)(const node *)){
    
    
    //层次遍历(BFS)
    const node *p;
    queue<const node *> qu;
    p = root;
    if(p != nullptr) qu.push(p);
    while(! qu.empty() ) {
    
    
        p = qu.front();
        qu.pop();
        visit(p);
        if(p->left != nullptr) qu.push(p->left);
        if(p->right != nullptr) qu.push(p->right);
    }
}

Complete code

#include "iostream"
#include "queue"
#include "stack"

using namespace std;

struct node{
    
    
    int elem;
    node *left;
    node *right;
};

void pre_order_r(const node* root, int(*visit)(const node*)){
    
    
    //先序遍历,递归,root根结点,visit访问数据元素的函数指针
    if(root == nullptr) return;

    visit(root);
    pre_order_r(root->left, visit);
    pre_order_r(root->right, visit);
}

void in_order_r(const node* root, int(*visit)(const node*)){
    
    
    //中序遍历,递归
    if(root == nullptr) return;

    in_order_r(root->left, visit);
    visit(root);
    in_order_r(root->right, visit);
}

void post_order_r(const node* root, int(*visit)(const node*)){
    
    
    //后序遍历,递归
    if(root == nullptr) return;

    post_order_r(root->left,  visit);
    post_order_r(root->right, visit);
    visit(root);
}

void pre_order1(const node* root, int(*visit)(const node*)){
    
    
    //先序遍历,非递归
    const node *p;
    stack<const node*>st;
    p = root;
    if(p != nullptr) st.push(p);
    while(!st.empty()){
    
    
        p = st.top();
        st.pop();
        visit(p);
        if(p->right != nullptr) st.push(p->right);
        if(p->left != nullptr) st.push(p->left);
    }
}

void pre_order2(const node* root, int(*visit)(const node*)){
    
    
    const node *p;
    stack<const node*> st;
    p = root;
    if(p != nullptr) st.push(p);
    while( ! st.empty() ){
    
    
        p = st.top();
        st.pop();
        while(p != nullptr){
    
    
            visit(p);
            if(p->right != nullptr) st.push(p->right);
            p = p->left;
        }
    }
}
void in_order(const node* root, int(*visit)(const node*)){
    
    
    //中序遍历,非递归
    const node *p;
    stack<const node *> st;
    p= root;
    while(!st.empty() ) {
    
    
        if (p != nullptr){
    
    
            st.push(p);
            p = p->left;
        }
        else {
    
    
            p = st.top();
            st.pop();
            visit(p);
            p = p->right;
        }
    }
}

void post_order(const node* root, int(*visit)(const node*)){
    
    
    //后序遍历,非递归
    const node *p, *q;//p正在访问的节点,q刚刚访问过的节点
    stack<const node *> st;
    p = root;
    do {
    
    
        while(p != nullptr){
    
    //往左走下去
            st.push(p);
            p = p->left;
        }
        q = nullptr;
        while( !st.empty() ){
    
    
            p = st.top();
            st.pop();
            if(p -> right == q) {
    
    //右孩子不存在或者已被访问过
                visit(p);
                q = p;//保存刚访问的节点
            }
            else{
    
    
                st.push(p);//当前节点不能访问
                p = p->right;//先处理右子树
                break;
            }
        }
    }while(! st.empty() );
}

void level_order(const node *root, int(*visit)(const node *)){
    
    
    //层次遍历(BFS)
    const node *p;
    queue<const node *> qu;
    p = root;
    if(p != nullptr) qu.push(p);
    while(! qu.empty() ) {
    
    
        p = qu.front();
        qu.pop();
        visit(p);
        if(p->left != nullptr) qu.push(p->left);
        if(p->right != nullptr) qu.push(p->right);
    }
}

Guess you like

Origin blog.csdn.net/weixin_45652283/article/details/131862323