二叉树の作成と遍歴(再帰と非再帰)

1. 二叉树から文字列の生成

アイデア:通常の前順再帰遍歴に基づいて、右部分木が空の場合を考慮する。

class Solution {
public:
    string tree2str(TreeNode* root) {
        if (root == nullptr) return "";
        if (root->left == nullptr && root->right == nullptr) return to_string(root->val);
        if (root->right == nullptr) 
            return to_string(root->val) + "(" + tree2str(root->left) + ")";
        return to_string(root->val) + "(" + tree2str(root->left) + ")(" + tree2str(root->right) + ")";
    }
};

2. 二叉树のレベル順遍歴

アイデア:

方法一 (BFS)

キューを使用して各レベルのノードを処理する。

方法二 (DFS)

深さごとのノードを格納する配列を定義する。

方法一: BFS

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vv;
        if (root == nullptr) return vv;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            vector<int> v;
            int size = q.size();
            while (size--) {
                TreeNode* node = q.front();
                q.pop();
                v.emplace_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            vv.push_back(v);
        }
        return vv;
    }
};

方法二: DFS

class Solution {
public:
    void dfs(TreeNode* root, int k, vector<vector<int>>& ans) {
        if (root == nullptr) return;
        if (k == ans.size()) ans.push_back(vector<int>());
        ans[k].push_back(root->val);
        dfs(root->left, k + 1, ans);
        dfs(root->right, k + 1, ans);
    }

    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> ans;
        dfs(root, 0, ans);
        return ans;
    }
};

3. 二叉树のレベル順遍歴II

アイデア:

下から上に各レベルのノード値を出力するため、結果リストの先頭に追加する。

方法一: BFS

class Solution {
public:
    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> vv;
        if (root == nullptr) return vv;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            vector<int> v;
            int n = q.size();
            while (n--) {
                TreeNode* Node = q.front();
                q.pop();
                v.push_back(Node->val);
                if (Node->left) q.push(Node->left);
                if (Node->right) q.push(Node->right);
            }
            vv.push_back(v);
        }
        reverse(vv.begin(), vv.end());
        return vv;
    }
};

方法二: DFS

class Solution {
public:
    void dfs(TreeNode* root, int k, vector<vector<int>>& ans) {
        if (root == nullptr) return;
        if (k == ans.size()) ans.push_back(vector<int>());
        ans[k].push_back(root->val);
        dfs(root->left, k + 1, ans);
        dfs(root->right, k + 1, ans);
    }

    vector<vector<int>> levelOrderBottom(TreeNode* root) {
        vector<vector<int>> ans;
        dfs(root, 0, ans);
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

4. 二叉树の最近共通祖先

アイデア:

2つのノード p と q は次の2つの場合に分かれる:

  1. p と q が同じ部分木にある
  2. p と q が異なる部分木にある

根ノードから始めて、左右の部分木を再帰的に調べる。

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if (root == nullptr || root == p || root == q) return root;
        TreeNode* l = lowestCommonAncestor(root->left, p, q);
        TreeNode* r = lowestCommonAncestor(root->right, p, q);
        if (l && r) return root;
        return l ? l : r;
    }
};

5. 二叉探索木と双方向リスト

アイデア:

二叉探索木の中間順遍歴は昇順になるため、中間順でノードを連結する。

class Solution {
public:
    TreeNode* preNode;

    TreeNode* Convert(TreeNode* pRootOfTree) {
        if (pRootOfTree == nullptr) return pRootOfTree;
        TreeNode* p = pRootOfTree;
        while (p->left) p = p->left;
        Inorder(pRootOfTree);
        return p;
    }

    void Inorder(TreeNode* root) {
        if (root == nullptr) return;
        Inorder(root->left);
        root->left = preNode;
        if (preNode) preNode->right = root;
        preNode = root;
        Inorder(root->right);
    }
};

6. 前順遍歴(非再帰)

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> ans;
        if (root == nullptr) return ans;
        stack<TreeNode*> st;
        TreeNode* node = root;
        while (!st.empty() || node != nullptr) {
            while (node != nullptr) {
                ans.push_back(node->val);
                st.push(node);
                node = node->left;
            }
            node = st.top();
            st.pop();
            node = node->right;
        }
        return ans;
    }
};

7. 中間順遍歴(非再帰)

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> s;
        while (root != nullptr || !s.empty()) {
            while (root != nullptr) {
                s.push(root);
                root = root->left;
            }
            root = s.top();
            s.pop();
            ans.push_back(root->val);
            root = root->right;
        }
        return ans;
    }
};

8. 後順遍歴(非再帰)

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> s;
        TreeNode* pre = nullptr;
        while (root != nullptr || !s.empty()) {
            while (root != nullptr) {
                s.push(root);
                root = root->left;
            }
            root = s.top();
            s.pop();
            if (root->right == nullptr || root->right == pre) {
                ans.push_back(root->val);
                pre = root;
                root = nullptr;
            } else {
                s.push(root);
                root = root->right;
            }
        }
        return ans;
    }
};

9. 前順と中間順から二叉树を構築

class Solution {
private:
    unordered_map<int, int> pos;

public:
    TreeNode* dfs(const vector<int>& preorder, const vector<int>& inorder, int pl, int pr, int il, int ir) {
        if (pl > pr) return nullptr;
        int k = pos[preorder[pl]];
        TreeNode* root = new TreeNode(preorder[pl]);
        int len = k - il;
        root->left = dfs(preorder, inorder, pl + 1, pl + len, il, k - 1);
        root->right = dfs(preorder, inorder, pl + len + 1, pr, k + 1, ir);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        for (int i = 0; i < n; ++i) {
            pos[inorder[i]] = i;
        }
        return dfs(preorder, inorder, 0, n - 1, 0, n - 1);
    }
};

10. 中間順と後順から二叉树を構築

class Solution {
public:
    unordered_map<int, int> pos;
    TreeNode* dfs(vector<int>& inorder, vector<int>& postorder, int il, int ir, int pl, int pr) {
        if (il > ir) return nullptr;
        int k = pos[postorder[pr]];
        TreeNode* root = new TreeNode(postorder[pr]);
        root->left = dfs(inorder, postorder, il, k - 1, pl, pl + k - 1 - il);
        root->right = dfs(inorder, postorder, k + 1, ir, pl + k - il, pr - 1);
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        for (int i = 0; i < n; i++) {
            pos[inorder[i]] = i;
        }
        return dfs(inorder, postorder, 0, n - 1, 0, n - 1);
    }
};

11. 前順と後順から二叉树を構築

class Solution {
public:
    int preIndex = 0, posIndex = 0;
    TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
        TreeNode* root = new TreeNode(preorder[preIndex++]);
        if (root->val != postorder[posIndex]) 
            root->left = constructFromPrePost(preorder, postorder);
        if (root->val != postorder[posIndex]) 
            root->right = constructFromPrePost(preorder, postorder);
        posIndex++;
        return root;
    }
};

12. 完全二叉树の後順遍歴からレベル順遍歴への変換

#include <iostream>
#include <vector>
using namespace std;

void buildLevelOrder(vector<int>& level, int index, int start, int end, const vector<int>& post) {
    if (start > end) return;
    level[index] = post[end];
    if (start == end) return;
    
    int m = end - start + 1;
    int leftSize = m / 2;
    
    buildLevelOrder(level, 2 * index + 1, start, start + leftSize - 1, post);
    buildLevelOrder(level, 2 * index + 2, start + leftSize, end - 1, post);
}

int main() {
    int n;
    cin >> n;
    vector<int> post(n);
    for (int& num : post) {
        cin >> num;
    }
    
    vector<int> level(n);
    buildLevelOrder(level, 0, 0, n - 1, post);
    
    for (int num : level) {
        cout << num << " ";
    }
    return 0;
}

1. 先順排列の求める

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

string in, post;

void dfs1(int il, int ir, int pl, int pr) {
    if (il > ir) return;
    char root = post[pr];
    cout << root;
    int pos;
    for (pos = il; pos <= ir; pos++) {
        if (in[pos] == root) break;
    }
    dfs1(il, pos - 1, pl, pl + pos - 1 - il);
    dfs1(pos + 1, ir, pl + pos - il, pr - 1);
}

void dfs2(string s1, string s2) {
    int len1 = s1.length();
    int len2 = s2.length();
    if (len1 <= 0) return;
    char root = s2[len2 - 1];
    cout << root;
    int pos = s1.find(root);
    dfs2(s1.substr(0, pos), s2.substr(0, pos));
    dfs2(s1.substr(pos + 1), s2.substr(pos, len1 - pos - 1));
}

int main() {
    cin >> in >> post;
    int n = in.size();
    //dfs1(0, n - 1, 0, n - 1);
    dfs2(in, post);
    return 0;
}

2. 中間順と後順からレベル順を出力

#include <iostream>
#include <queue>
#include <string>
using namespace std;

const int N = 35;
int b[N], c[N];
int l[N], r[N];
int n;
queue<int> q;

int dfs(int x1, int y1, int x2, int y2) {
    if (x1 > y1) return 0;
    int pos = x1, root = c[y2];

    while (b[pos] != root) pos++;

    l[root] = dfs(x1, pos - 1, x2, x2 + pos - x1 - 1);
    r[root] = dfs(pos + 1, y1, x2 + pos - x1, y2 - 1);

    return root;
}

void bfs(int root) {
    int flag = 0;
    q.push(root);

    while (!q.empty()) {
        int i = q.front();
        q.pop();

        if (flag) printf(" %d", i);
        else {
            flag = 1;
            printf("%d", i);
        }

        if (l[i] != 0) q.push(l[i]);
        if (r[i] != 0) q.push(r[i]);
    }
}

int main() {
    cin >> n;

    for (int i = 0; i < n; i++) cin >> c[i];
    for (int i = 0; i < n; i++) cin >> b[i];

    int root = dfs(0, n - 1, 0, n - 1);
    bfs(root);

    return 0;
}

3. 中間順列

class Solution {
public:
    int len = 0;
    void dfs(vector<int>& pre, int pl, int pr, vector<int>& suf, int sl, int sr, vector<int>& ans) {
        if (pl > pr || sl > sr) return;
        if (pl == pr) {
            ans[len++] = pre[pl];
            return;
        }
        int pos = -1;
        for (int i = sl; i <= sr; i++) {
            if (suf[i] == pre[pl + 1]) 
                pos = i;
        }
        
        dfs(pre, pl + 1, pos - sl + pl + 1, suf, sl, pos, ans);
        ans[len++] = pre[pl];
        dfs(pre, pos - sl + pl + 2, pr, suf, pos + 1, sr - 1, ans);
    }

    vector<int> solve(int n, vector<int>& pre, vector<int>& suf) {
        vector<int> ans(n);
        dfs(pre, 0, n - 1, suf, 0, n - 1, ans);
        return ans;
    }
};

1. N 叉树のレベル順遍歴

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> ans;
        if (root == nullptr) return ans;
        queue<Node*> q;
        q.push(root);
        while (!q.empty()) {
            vector<int> arr;
            int size = q.size();
            while (size--) {
                Node* node = q.front();
                q.pop();
                arr.emplace_back(node->val);
                for (Node* child : node->children) {
                    if (child != nullptr) q.push(child);
                }
            }
            ans.emplace_back(arr);
        }
        return ans;
    }
};

2. 二叉树のジグザグ形レベル順遍歴

class Solution {
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
        vector<vector<int>> ans;
        if (root == nullptr) return ans;
        queue<TreeNode*> q;
        q.push(root);
        int cnt = 0;
        while (!q.empty()) {
            vector<int> arr;
            int size = q.size();
            cnt++;
            while (size--) {
                TreeNode* node = q.front();
                q.pop();
                arr.emplace_back(node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            if (cnt % 2 == 0) reverse(arr.begin(), arr.end());
            ans.emplace_back(arr);
        }
        return ans;
    }
};

3. 二叉树の最大幅

class Solution {
public:
    int widthOfBinaryTree(TreeNode* root) {
        vector<pair<TreeNode*, unsigned int>> q;
        q.push_back({root, 1});
        unsigned int ret = 0;
        while (!q.empty()) {
            auto& [x1, y1] = q[0];
            auto& [x2, y2] = q.back();
            ret = max(ret, y2 - y1 + 1);
            vector<pair<TreeNode*, unsigned int>> tmp;
            for (auto& [x, y] : q) {
                if (x->left) tmp.push_back({x->left, y * 2});
                if (x->right) tmp.push_back({x->right, y * 2 + 1});
            }
            q = tmp;
        }
        return ret;
    }
};

4. 各行の最大値を見つける

class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> ret;
        if (root == nullptr) return ret;
        queue<TreeNode*> q;
        q.push(root);
        while (!q.empty()) {
            int ans = INT_MIN, size = q.size();
            while (size--) {
                TreeNode* node = q.front();
                q.pop();
                ans = max(ans, node->val);
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            ret.emplace_back(ans);
        }
        return ret;
    }
};

タグ: C++ 二叉树 遍历 递归 非递归

7月26日 19:56 投稿