003-1【树结构】前序、中序、后序、建树

1、遍历

  • 前序 : 先访问根节点,再访问左子树,最后访问右子树
  • 中序 : 先访问左子树,先访问根节点,最后访问右子树
  • 后序 : 先访问左子树,再访问右子树,最后访问根节点

1.1 前序遍历

//void(*visit)(BinNode<E>*node)为一个函数指针
template <typename E>
void BinTree<E>::preOrder(BinNode<E> *tmp, void (*visit)(BinNode<E> *node))
{
    //根、左、右
    if (tmp == NULL)
        return;
    visit(tmp);
    preOrder(tmp->left(), visit);
    preOrder(tmp->right(), visit);
}

1.2 中序遍历

template <typename E>
void BinTree<E>::inOrder(BinNode<E> *tmp, void (*visit)(BinNode<E> *node))
{
    //左、根、右
    if (tmp == NULL)
        return;
    inOrder(tmp->left(), visit);
    visit(tmp);
    inOrder(tmp->right(), visit);
}

1.3 后序遍历

template <typename E>
void BinTree<E>::postOrder(BinNode<E> *tmp, void (*visit)(BinNode<E> *node)) 
{
    //左、右、根
    if (tmp == NULL)
        return;
    postOrder(tmp->left(), visit);
    postOrder(tmp->right(), visit);
    visit(tmp);
}

1.4 层次遍历(BFS)

template <typename E>
void BinTree<E>::LevelOrderTranverse(BinNode<E> *tmp, void (*visit)(BinNode<E> *node))
{
    if (tmp == NULL)
        return;
    queue<BinNode<E> *> q;
    q.push(tmp);
    while (q.size())
    {
        BinNode<E> *t = q.front();
        q.pop();
        visit(t);
        if (t->left() != NULL)
            q.push(t->left());
        if (t->right() != NULL)
            q.push(t->right());
    }
    return;
}

2、建树

2.1 根据前序和中序建树

前序遍历的结果是
[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
即根节点总是前序遍历中的第一个节点。

中序遍历的结果是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

只要在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目,也就是可以确定前序遍历中序遍历的每一部分。这样,原问题就缩小为:构造根节点,在左子树遍历结果中构造左子树,在右子树遍历结果中构造右子树

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

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    //从preorder[pre_begin,pre_end]&inorder[in_begin,in_end]中构造二叉树
    TreeNode* buildTreeHelper(vector<int>& preorder, int pre_begin,int pre_end,vector<int>& inorder,int in_begin,int in_end){
        //preoder为空,返回NULL
        if(pre_begin>pre_end)return NULL;

        //构造根节点
        TreeNode* root=new TreeNode(preorder[pre_begin]);
        //在inoder中找到根节点的位置
        int rootIdx=in_begin;
        for(int i=in_begin;i<=in_end;i++){
            if(inorder[i]==preorder[pre_begin])
                rootIdx=i;
        }
        int leftNum=rootIdx-in_begin;//左子树的节点数

        //构造左子树:递归搜索preorder[pre_begin+1,pre_begin+leftNum]&inorder[in_begin,rootIdx-1]
        root->left=buildTreeHelper(preorder,pre_begin+1,pre_begin+leftNum,inorder,in_begin,rootIdx-1);
        //构造右子树:递归搜索preorder[pre_begin+1+leftNum,pre_end]&inorder[rootIdx+1,in_end]
        root->right=buildTreeHelper(preorder,pre_begin+1+leftNum,pre_end,inorder,rootIdx+1,in_end);
        return root;
    }
    
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return buildTreeHelper(preorder,0,preorder.size()-1,inorder,0,inorder.size()-1);
    }
};

利用map记录inorder中的<item,index>,减少在inorder中搜索root的时间:


class Solution {
    unordered_map<int,int> m;//<item,index>

    TreeNode* buildTreeHelper(vector<int>& preorder, int pre_begin,int pre_end,vector<int>& inorder,int in_begin,int in_end){
        ......
        //在inoder中找到根节点的位置
        int rootIdx=m[preorder[pre_begin]];
        .......
    }
    
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        // 建立(元素,下标)键值对的哈希表
        for(int i=0;i<inorder.size();i++){
            m[inorder[i]]=i;
        }
		......
    }
};

2.1 根据中序和后序建树

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

中序遍历的结果是
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]

后序遍历的结果是
[ [左子树的前序遍历结果], [右子树的前序遍历结果], 根节点 ]
即根节点总是后序遍历中的最后一个节点。

只要在中序遍历中定位到根节点,那么我们就可以分别知道左子树和右子树中的节点数目,也就是可以确定后序遍历中序遍历的每一部分。这样,原问题就缩小为:构造根节点,在左子树遍历结果中构造左子树,在右子树遍历结果中构造右子树

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    TreeNode* buildTreeHelper(vector<int>& inorder, int in_begin,int in_end,vector<int>& postorder,int post_begin,int post_end) {
        if(post_begin>post_end)return NULL;

        //构造根节点
        TreeNode* root=new TreeNode(postorder[post_end]);

        //在inoder中找到根节点的位置
        int rootIdx=in_begin;
        for(int i=in_begin;i<=in_end;i++){
            if(inorder[i]==postorder[post_end])
                rootIdx=i;
        }
        int leftNum=rootIdx-in_begin;//左子树的节点数

        //构造左子树:递归搜索inorder[in_begin,rootIdx-1]&postorder[0,leftNum-1]
        root->left=buildTreeHelper(inorder,in_begin,rootIdx-1,postorder,post_begin,post_begin+leftNum-1);
        //构造右子树:递归搜索inorder[rootIdx+1,in_end]&postorder[leftNum,post_end-1]
        root->right=buildTreeHelper(inorder,rootIdx+1,in_end,postorder,post_begin+leftNum,post_end-1);
        return root;
    }

public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        return buildTreeHelper(inorder,0,inorder.size()-1,postorder,0,postorder.size()-1);
    }
};

利用map记录inorder中的<item,index>,减少在inorder中搜索root的时间:

class Solution {
    unordered_map<int,int> m;//<item,index>

    TreeNode* buildTreeHelper(vector<int>& inorder, int in_begin,int in_end,vector<int>& postorder,int post_begin,int post_end) {
        ......
        //在inoder中找到根节点的位置
        int rootIdx=m[postorder[post_end]];
		......
    }

public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        // 建立(元素,下标)键值对的哈希表
        for(int i=0;i<inorder.size();i++){
            m[inorder[i]]=i;
        }

        return buildTreeHelper(inorder,0,inorder.size()-1,postorder,0,postorder.size()-1);
    }
};

2.3根据前序和后序建树(不唯一)

889. 根据前序和后序遍历构造二叉树

给定两个整数数组,preorder 和 postorder ,其中 preorder 是一个具有 无重复 值的二叉树的前序遍历,postorder 是同一棵树的后序遍历,重构并返回二叉树。

如果存在多个答案,您可以返回其中 任何 一个。

前序遍历为:

  • 【根结点】【前序遍历左分支】【前序遍历右分支】
  • 【根结点】【左子树根节点,root->left>left, root->left>right】【右子树根节点,root->right>left, root->right>right】

而后序遍历为:

  • 【root->left>left, root->left>right,左子树根节点】【root->right>left, root->right>right,右子树根节点】【根结点】

可以发现,划分出左右子树的关键是找到左子树根节点在preorder和postorder中的位置,然后就可以算出左子树的节点数,之后递归地去构造左右子树。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    unordered_map<int,int> m;//<item,index>
    
    TreeNode* helper(vector<int>& preorder, int pre_begin,int pre_end,vector<int>& postorder,int post_begin,int post_end) {
        if(pre_begin>pre_end || post_begin>post_end)return NULL;
        if(pre_begin==pre_end) return new TreeNode(preorder[pre_begin]);

        TreeNode* root=new TreeNode(preorder[pre_begin]);
        int leftSubRootIdx=m[preorder[pre_begin+1]];
        // int leftSubRootIdx=post_begin;
        // while(preorder[pre_begin+1]!=postorder[leftSubRootIdx]){
        //     leftSubRootIdx++;
        // }
        int leftNum=leftSubRootIdx-post_begin;

        //构造左子树:递归搜索
        root->left=helper(preorder,pre_begin+1,pre_begin+1+leftNum,postorder,post_begin,leftSubRootIdx);
        //构造右子树:递归搜索
        root->right=helper(preorder,pre_begin+1+leftNum+1,pre_end,postorder,leftSubRootIdx+1,post_end-1);
        return root;
    }

public:
    TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
        for(int i=0;i<postorder.size();i++){
            m[postorder[i]]=i;
        }

        return helper(preorder,0,preorder.size()-1,postorder,0,postorder.size()-1);
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值