经典二叉树遍历问题的总结(LeetCode 105 和 106)

1.第1类(给遍历,构造树)

(1)从中序与前序遍历构造二叉树

LeetCode 105. 从前序与中序遍历序列构造二叉树

①代码少,但性能一般

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size() == 0) return NULL;
        TreeNode *root = new TreeNode(preorder[0]);
        //左右子树的前序遍历和中序遍历;
        auto mid = find(inorder.begin(), inorder.end(), preorder[0]);
        vector<int> leftPreorder, leftInorder, rightPreorder, rightInorder; 
        //左子树的中序遍历
        leftInorder.assign(inorder.begin(), mid);
        //左子树的前序遍历
        leftPreorder.assign(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());
        //右子树的前序遍历
        rightPreorder.assign(preorder.begin() + 1 + leftInorder.size(), preorder.end());
        //右子树的中序遍历
        rightInorder.assign(mid + 1, inorder.end());
        root->left = buildTree(leftPreorder, leftInorder);
        root->right = buildTree(rightPreorder, rightInorder);
        return root;
    }
};

在这里插入图片描述
②性能稍好,更常规的写法

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* myBuildTree(const vector<int>& preorder, int headP, int tailP, const vector<int> &inorder, int headI, int tailI) {
        if(headP > tailP) return NULL;
        TreeNode *root = new TreeNode(preorder[headP]);
        //中序遍历中,根节点的下标
        int mid;
        for(int i = headI; i <= tailI; i++) {
            if(inorder[i] == preorder[headP]) {
                mid = i;
                break;
            }
        }
        //左子树
        root->left = myBuildTree(preorder, headP + 1, headP + mid - headI, inorder, headI, mid - 1);
        //右子树
        root->right = myBuildTree(preorder, headP + mid - headI + 1, tailP, inorder, mid + 1, tailI);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        if(preorder.size() == 0) return NULL;
        TreeNode *root = myBuildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
        return root;
    }
};

在这里插入图片描述

(2)从中序与后序遍历构造二叉树

LeetCode 106. 从中序与后序遍历序列构造二叉树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* myBuildTree(const vector<int>& inorder, int headI, int tailI, const vector<int>& postorder, int headP, int tailP) {
        //递归边界
        if(headI > tailI) return NULL;
        //递归式
        TreeNode *root = new TreeNode(postorder[tailP]);
        int mid; //中序遍历中,根节点的下标
        for(int i = headI; i<= tailI; i++) {
            if(inorder[i] == postorder[tailP]) {
                mid = i;
                break;
            }
        }
        int leftTreeNodeNumber = mid - headI;
        //左子树 中序遍历 后序遍历
        root->left = myBuildTree(inorder, headI, mid - 1, postorder, headP, headP + leftTreeNodeNumber - 1);
        root->right = myBuildTree(inorder, mid + 1, tailI, postorder, headP + leftTreeNodeNumber, tailP - 1);
        return root;
    }

    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(inorder.size() == 0) return NULL;
        TreeNode *root = myBuildTree(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1);
        return root;
    }
};

(3)从中序与层序遍历构造二叉树

//从中序与层序遍历构造二叉树

#include<cstdio>
#include <vector>
using namespace std;
typedef unsigned int UL;
//Definition for a binary tree node.
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

TreeNode* myBuildTree(const vector<int>& inorder, int headI, int tailI, const vector<int> &levelorder, int headL, int tailL) {
    if(headI> tailI) return NULL;
    TreeNode *root = new TreeNode(levelorder[headL]);
    //中序遍历中,根节点的下标
    int mid = -1;
    for(int i = headI; i <= tailI; i++) {
        if(inorder[i] == levelorder[headL]) {
            mid = i;
            break;
        }
    }
    UL leftTreeNodeNUmber = mid - headI;
    UL rightTreeNodeNumber = inorder.size() - leftTreeNodeNUmber - 1;
    //构建左子树的层序遍历
    vector<int> leftLevelOrder;
    bool flag = false; //还未找全;
    for (int i = headL; i <= tailL; i++) {
        for (int j = headI; j <= mid - 1; j++) {
            if(levelorder[i] == inorder[j]) {      //既然左子树的层序遍历是分散的,根据左子树的中序遍历,可以把分散的收集到vector中;
                leftLevelOrder.push_back(levelorder[i]);
                if(leftLevelOrder.size() == (leftTreeNodeNUmber))
                    flag = true; //找全了
                break;
            }
        }
        if(flag)
            break;
    }
    //构建右子树的层序遍历
    vector<int> rightLevelOrder;
    flag = false; //还未找全;
    for (int i = headL; i <= tailL; i++) {
        for (int j = mid + 1; j <= tailI; j++) {
            if(levelorder[i] == inorder[j]) {
                rightLevelOrder.push_back(levelorder[i]);
                if(rightLevelOrder.size() == rightTreeNodeNumber) {
                    flag = true; //找全了
                    break;
                }
            }
        }
        if(flag)
            break;
    }

    //左子树
    root->left = myBuildTree(inorder, headI, mid - 1, leftLevelOrder, 0, leftTreeNodeNUmber - 1);
    //右子树
    root->right = myBuildTree(inorder, mid + 1, tailI, rightLevelOrder, 0, rightTreeNodeNumber - 1);
    return root;
}

TreeNode* buildTree(vector<int>& inorder, vector<int>& levelorder) {
    if(inorder.size() == 0) return NULL;
    TreeNode *root = myBuildTree(inorder, 0, inorder.size() - 1, levelorder, 0, levelorder.size() - 1);
    return root;
}

void preOrder(TreeNode *root) {
    if(root == NULL)
        return;
    printf("%d ", root->val);
    preOrder(root->left);
    preOrder(root->right);
}

int main() {
    //测试
    //input
    vector<int> inorder = {7, 5, 4, 2, 8, 6, 9}, levelorder = {4, 5, 6, 7, 8, 9, 2};
    TreeNode *root = buildTree(inorder, levelorder);
    //前序遍历验证
    preOrder(root);
    return 0;
}

在这里插入图片描述

总结:(1)~(2)思路一致(注释已说明),(3)多了一个步骤:根据子树的中序遍历和树的层序遍历求子树的层序遍历(求法见代码)。
(1)~(2)难度相当,(3)较难点

2.第2类(给遍历,求遍历)

(1)中序遍历 + 前序遍历 -> 后序遍历 || 层序遍历

①求后序遍历

void postOrder(vector<int>& postorder, const vector<int>& inorder, int headI, int tailI, const vector<int>& preorder, int headP, int tailP) {
    //递归边界;
    if(headI > tailI)
        return;
    //找中序遍历中根节点的下标
    int mid;
    for (int i = headI; i <= tailI; i++) {
        if(inorder[i] == preorder[headP]) {
            mid = i;
            break;
        }
    }

    int leftTreeNodeNumber = mid - headI;
    //后序遍历
    //左子树
    postOrder(postorder, inorder, headI, mid - 1, preorder, headP + 1, headP + leftTreeNodeNumber);
    //右子树
    postOrder(postorder, inorder, mid + 1, tailI, preorder, headP + leftTreeNodeNumber + 1, tailP);
    postorder.push_back(preorder[headP]);
}

②求层序遍历(本文:先建树(见上文[1.(1)])后层序遍历)

(2)中序遍历 + 后序遍历 -> 前序遍历 || 层序遍历

①前序遍历

void preOrder(vector<int>& preorder, const vector<int>& inorder, int headI, int tailI, const vector<int>& postorder, int headP, int tailP) {
    //递归边界;
    if(headI > tailI)
        return;
    //找中序遍历中根节点的下标
    int mid = -1;
    for (int i = headI; i <= tailI; i++) {
        if(inorder[i] == postorder[tailP]) {
            mid = i;
            break;
        }
    }
    int leftTreeNodeNumber = mid - headI;
    //前序遍历
    preorder.push_back(postorder[tailP]);
    //左子树
    preOrder(preorder, inorder, headI, mid - 1, postorder, headP, headP + leftTreeNodeNumber - 1);
    //右子树
    preOrder(preorder, inorder, mid + 1, tailI, postorder, headP + leftTreeNodeNumber, tailP - 1);
}

②求层序遍历(本文:先建树(见上文[1.(2)])后层序遍历)

(3)中序遍历 + 层序遍历 -> 前序遍历 || 后序遍历

①求前序遍历

//前序遍历
void preOrder(vector<int>& preorder, const vector<int>& inorder, int headI, int tailI, const vector<int> &levelorder, int headL, int tailL) {
    if(headI> tailI) return;
    //中序遍历中,根节点的下标
    int mid = -1;
    for(int i = headI; i <= tailI; i++) {
        if(inorder[i] == levelorder[headL]) {
            mid = i;
            break;
        }
    }
    UL leftTreeNodeNumber = mid - headI;
    UL rightTreeNodeNumber = inorder.size() - leftTreeNodeNumber - 1;
    //构建左子树的层序遍历
    vector<int> leftLevelOrder;
    bool flag = false; //还未找全;
    for (int i = headL; i <= tailL; i++) {
        for (int j = headI; j <= mid - 1; j++) {
            if(levelorder[i] == inorder[j]) {      //既然左子树的层序遍历是分散的,根据左子树的中序遍历,可以把分散的收集到vector中;
                leftLevelOrder.push_back(levelorder[i]);
                if(leftLevelOrder.size() == (leftTreeNodeNumber))
                    flag = true; //找全了
                break;
            }
        }
        if(flag)
            break;
    }
    //构建右子树的层序遍历
    vector<int> rightLevelOrder;
    flag = false; //还未找全;
    for (int i = headL; i <= tailL; i++) {
        for (int j = mid + 1; j <= tailI; j++) {
            if(levelorder[i] == inorder[j]) {
                rightLevelOrder.push_back(levelorder[i]);
                if(rightLevelOrder.size() == rightTreeNodeNumber) {
                    flag = true; //找全了
                    break;
                }
            }
        }
        if(flag)
            break;
    }

    //前序遍历
    preorder.push_back(levelorder[headL]);
    preOrder(preorder, inorder, headI, mid - 1, leftLevelOrder, 0, leftTreeNodeNumber - 1);
    preOrder(preorder, inorder, mid + 1, tailI, rightLevelOrder, 0, rightTreeNodeNumber - 1);
}

②后序遍历

和上面①前序遍历代码几乎一致,最大的差别仅在最后三行,即:①是前序遍历,②是后序遍历)

//后序遍历
void postOrder(vector<int>& postorder, const vector<int>& inorder, int headI, int tailI, const vector<int> &levelorder, int headL, int tailL) {
    if(headI> tailI) return;
    //中序遍历中,根节点的下标
    int mid = -1;
    for(int i = headI; i <= tailI; i++) {
        if(inorder[i] == levelorder[headL]) {
            mid = i;
            break;
        }
    }
    UL leftTreeNodeNumber = mid - headI;
    UL rightTreeNodeNumber = inorder.size() - leftTreeNodeNumber - 1;
    //构建左子树的层序遍历
    vector<int> leftLevelOrder;
    bool flag = false; //还未找全;
    for (int i = headL; i <= tailL; i++) {
        for (int j = headI; j <= mid - 1; j++) {
            if(levelorder[i] == inorder[j]) {      //既然左子树的层序遍历是分散的,根据左子树的中序遍历,可以把分散的收集到vector中;
                leftLevelOrder.push_back(levelorder[i]);
                if(leftLevelOrder.size() == (leftTreeNodeNumber))
                    flag = true; //找全了
                break;
            }
        }
        if(flag)
            break;
    }
    //构建右子树的层序遍历
    vector<int> rightLevelOrder;
    flag = false; //还未找全;
    for (int i = headL; i <= tailL; i++) {
        for (int j = mid + 1; j <= tailI; j++) {
            if(levelorder[i] == inorder[j]) {
                rightLevelOrder.push_back(levelorder[i]);
                if(rightLevelOrder.size() == rightTreeNodeNumber) {
                    flag = true; //找全了
                    break;
                }
            }
        }
        if(flag)
            break;
    }

    //后序遍历
    postOrder(postorder, inorder, headI, mid - 1, leftLevelOrder, 0, leftTreeNodeNumber - 1);
    postOrder(postorder, inorder, mid + 1, tailI, rightLevelOrder, 0, rightTreeNodeNumber - 1);
    postorder.push_back(levelorder[headL]);

}

总结:思路和1.(3)几乎一致,只不过需求不同,一个是建树,一个仅需把遍历结果存储在vector中。

3.总结

在这里插入图片描述
①第1类,会(1)便会(2),反而(3)需要留心一下;
②第2类,同样是会(1)便会(2),而且思路和第1类是一致的,只是需求不同,第1类是建树,第2类是仅需把遍历结果存储在vector中即可。反而求层序遍历需要留心一下,本文采用先构建树再层序遍历的方式,不建树的方式见给定二叉树的中序遍历和后序遍历,不建树求其层序遍历(PAT A1020)
③对于(3),思路和第1类的(3)相同,也只是需求不同。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值