バイナリ ツリー トラバーサル (プレオーダー トラバーサル、インオーダー トラバーサル、ポストオーダー トラバーサル) 再帰的および非再帰的アルゴリズム

1. 事前注文トラバーサル

事前順序トラバーサル: 最初にツリーのルート ノードをトラバースし、次に左側のサブツリーをトラバースし、最後に右側のサブツリーをトラバースします。


ここに画像の説明を挿入

事前注文トラバーサル シーケンス: 1 -> 2 -> 4 -> 5 -> 3 -> 6 -> 7

トピックリンク

1. 再帰

部分問題の分解方法

class Solution {
    
    
public:
    void preOrder(TreeNode* root,vector<int>& str)
    {
    
    
        if(root==NULL)return;
        str.push_back(root->val);
        preOrder(root->left,str);
        preOrder(root->right,str);
    }
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        vector<int> str;
        preOrder(root,str);
        return str;
    }
};

ここに画像の説明を挿入

2. 非再帰的

アイデア: バイナリ ツリーを 2 つの部分として考えます。1 つの部分は左側のノードで、もう 1 つの部分は左側のノードの右側のサブツリーです。最初にバイナリ ツリーのすべての左側のノードをスタックに入れてから、それらをポップアウトします。このプロセスでは、このノードの右のサブツリーが同じ方法でスタックにプッシュされ、次にスタックから 1 つずつポップアウトされます
ここに画像の説明を挿入
ここに画像の説明を挿入

class Solution {
    
    
public:
    vector<int> preorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        TreeNode* cur=root;
        vector<int> v;
        while(cur||!st.empty())
        {
    
    
            //访问一棵树的开始
            //  1、访问左路节点,左路节点入栈,后续一次访问左路节点的右子树
            while(cur)
            {
    
    
                v.push_back(cur->val);
                st.push(cur);
                cur=cur->left;
            }

            //一次访问左路节点的右子树
            TreeNode* top=st.top();
            st.pop();

            //子问题的方式访问右子树
            cur=top->right;
        }
        return v;
    }
};

2 番目、順序どおりの走査

トピックリンク

順序トラバーサル: 最初にツリーの左側のサブツリーをトラバースし、次にルート ノードをトラバースし、最後に右側のサブツリーをトラバースします。 順序
ここに画像の説明を挿入

トラバーサル シーケンス: 4 -> 2 -> 5 -> 1 -> 6 -> 3 -> 7

1. 再帰

再帰呼び出しは部分問題を分解します。これは、事前順序トラバーサル再帰と同じ考え方です。

class Solution {
    
    
public:
    void inorder(TreeNode* root,vector<int>& res)
    {
    
    
        if(root==NULL)return;
        inorder(root->left,res);
        res.push_back(root->val);
        inorder(root->right,res);

    }
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        vector<int> res;
        inorder(root,res);
        return res;
    }
};

2. 非再帰的

プリオーダートラバーサルと同様に、プリオーダートラバーサルはスタックにプッシュする順序でのプリオーダートラバーサルシーケンスであり、インオーダートラバーサルはスタックをポップする順序でのインオーダートラバーサルシーケンスです
ここに画像の説明を挿入
ここに画像の説明を挿入

class Solution {
    
    
public:
    vector<int> inorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        TreeNode* cur=root;
        vector<int> v;
        while(cur||!st.empty())
        {
    
    
            //访问一棵树的开始
            //  1、访问左路节点,左路节点入栈,后续一次访问左路节点的右子树
            while(cur)
            {
    
    
                st.push(cur);
                cur=cur->left;
            }
            //一次访问左路节点的右子树
            TreeNode* top=st.top();
            st.pop();
            v.push_back(top->val);
            //子问题的方式访问右子树
            cur=top->right;
        }
        return v;

    }
};

3. ポストオーダートラバーサル

後続トラバーサル: 最初にツリーの左側のサブツリーをトラバースし、次に右側のサブツリーをトラバースし、最後にルート ノードをトラバースします。 後続の
ここに画像の説明を挿入

トラバーサル シーケンス: 4 -> 5 -> 2 -> 6 -> 7 -> 3 -> 1

トピックリンク

1. 再帰

部分問題の分解

class Solution {
    
    
public:
    void postOrder(TreeNode* root,vector<int>& ret)
    {
    
    
        if(root==NULL)return;
        postOrder(root->left,ret);
        postOrder(root->right,ret);
        ret.push_back(root->val);
    }
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        vector<int> ret;
        postOrder(root,ret);
        return ret;
    }
};

2. 非再帰的

後続のトラバーサルの非再帰は、事前順序および順序内トラバーサルの非再帰よりも少し複雑です。以前の事前順序トラバーサルおよび順序内トラバーサルの非再帰アルゴリズムをいくつか改善する必要があります。 , 現在のノードへのアクセスを記録する変数 prev を作成します。現在の左ノードの右サブツリーが空であるか、右サブツリーが訪問されている場合は、現在接続されているノードをスタックからポップアウトする必要があり、スタッキングこのときのシーケンスがその後のトラバーサルシーケンスとなる。
ここに画像の説明を挿入
ここに画像の説明を挿入

class Solution {
    
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
    
    
        stack<TreeNode*> st;
        TreeNode* cur=root;
        TreeNode* prev=NULL;
        vector<int> v;
        while(cur||!st.empty())
        {
    
    
            //访问一棵树的开始
            //  1、访问左路节点,左路节点入栈,后续一次访问左路节点的右子树
            while(cur)
            {
    
    
                st.push(cur);
                cur=cur->left;
            }
            TreeNode* top=st.top();
            //一个节点右子树为空或者上一个访问节点是右子树的根,说明右子树访问过滤,可以访问根节点了
            if(top->right==nullptr||top->right==prev)
            {
    
    
                prev=top;
                v.push_back(top->val);
                st.pop();
            }else cur=top->right;
        }
        return v;
    }
};

おすすめ

転載: blog.csdn.net/Tianzhenchuan/article/details/132097998